_ Make it compilable again on OS400.
[platform/upstream/curl.git] / packages / OS400 / ccsidcurl.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id$
22  *
23  ***************************************************************************/
24
25 /* CCSID API wrappers for OS/400. */
26
27 #include <iconv.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <stdarg.h>
32
33 #pragma enum(int)
34
35 #include "curl.h"
36 #include "mprintf.h"
37 #include "urldata.h"
38 #include "url.h"
39 #include "getinfo.h"
40 #include "ccsidcurl.h"
41
42 #include "os400sys.h"
43
44 #ifndef SIZE_MAX
45 #define SIZE_MAX        ((size_t) ~0)   /* Is unsigned on OS/400. */
46 #endif
47
48
49 #define ASCII_CCSID     819     /* Use ISO-8859-1 as ASCII. */
50 #define NOCONV_CCSID    65535   /* No conversion. */
51 #define ICONV_ID_SIZE   32      /* Size of iconv_open() code identifier. */
52 #define ICONV_OPEN_ERROR(t)     ((t).return_value == -1)
53
54 #define ALLOC_GRANULE   8       /* Alloc. granule for curl_formadd_ccsid(). */
55
56
57 static void
58 makeOS400IconvCode(char buf[ICONV_ID_SIZE], unsigned int ccsid)
59
60 {
61   /**
62   *** Convert a CCSID to the corresponding IBM iconv_open() character
63   ***  code identifier.
64   ***  This code is specific to the OS400 implementation of the iconv library.
65   ***  CCSID 65535 (no conversion) is replaced by the ASCII CCSID.
66   ***  CCSID 0 is interpreted by the OS400 as the job's CCSID.
67   **/
68
69   ccsid &= 0xFFFF;
70
71   if (ccsid == NOCONV_CCSID)
72     ccsid = ASCII_CCSID;
73
74   memset(buf, 0, ICONV_ID_SIZE);
75   curl_msprintf(buf, "IBMCCSID%05u0000000", ccsid);
76 }
77
78
79 static iconv_t
80 iconv_open_CCSID(unsigned int ccsidout, unsigned int ccsidin, unsigned int cstr)
81
82 {
83   char fromcode[ICONV_ID_SIZE];
84   char tocode[ICONV_ID_SIZE];
85
86   /**
87   ***  Like iconv_open(), but character codes are given as CCSIDs.
88   ***  If `cstr' is non-zero, conversion is set up to stop whenever a
89   ***   null character is encountered.
90   ***  See iconv_open() IBM description in "National Language Support API".
91   **/
92
93   makeOS400IconvCode(fromcode, ccsidin);
94   makeOS400IconvCode(tocode, ccsidout);
95   memset(tocode + 13, 0, sizeof tocode - 13);   /* Dest. code id format. */
96
97   if (cstr)
98     fromcode[18] = '1';                         /* Set null-terminator flag. */
99
100   return iconv_open(tocode, fromcode);
101 }
102
103
104 static int
105 convert(char * d, size_t dlen, int dccsid,
106         const char * s, int slen, int sccsid)
107
108 {
109   int i;
110   iconv_t cd;
111   size_t lslen;
112
113   /**
114   ***  Convert `sccsid'-coded `slen'-data bytes at `s' into `dccsid'-coded
115   ***   data stored in the `dlen'-byte buffer at `d'.
116   ***  If `slen' < 0, source string is null-terminated.
117   ***  CCSID 65535 (no conversion) is replaced by the ASCII CCSID.
118   ***  Return the converted destination byte count, or -1 if error.
119   **/
120
121   if (sccsid == 65535)
122     sccsid = ASCII_CCSID;
123
124   if (dccsid == 65535)
125     dccsid = ASCII_CCSID;
126
127   if (sccsid == dccsid) {
128     lslen = slen >= 0? slen: strlen(s) + 1;
129     i = lslen < dlen? lslen: dlen;
130
131     if (s != d && i > 0)
132       memcpy(d, s, i);
133
134     return i;
135     }
136
137   if (slen < 0) {
138     lslen = 0;
139     cd = iconv_open_CCSID(dccsid, sccsid, 1);
140     }
141   else {
142     lslen = (size_t) slen;
143     cd = iconv_open_CCSID(dccsid, sccsid, 0);
144     }
145
146   if (ICONV_OPEN_ERROR(cd))
147     return -1;
148
149   i = dlen;
150
151   if ((int) iconv(cd, (char * *) &s, &lslen, &d, &dlen) < 0)
152     i = -1;
153   else
154     i -= dlen;
155
156   iconv_close(cd);
157   return i;
158 }
159
160
161 static char *
162 dynconvert(int dccsid, const char * s, int slen, int sccsid)
163
164 {
165   char * d;
166   char * cp;
167   size_t dlen;
168   int l;
169   int l2;
170   static const char nullbyte = 0;
171
172   /* Like convert, but the destination is allocated and returned. */
173
174   dlen = (size_t) (slen < 0? strlen(s): slen) + 1;
175   dlen *= MAX_CONV_EXPANSION;           /* Allow some expansion. */
176   d = malloc(dlen);
177
178   if (!d)
179     return (char *) NULL;
180
181   l = convert(d, dlen, dccsid, s, slen, sccsid);
182
183   if (l < 0) {
184     free(d);
185     return (char *) NULL;
186     }
187
188   if (slen < 0) {
189     /* Need to null-terminate even when source length is given.
190        Since destination code size is unknown, use a conversion to generate
191        terminator. */
192
193     l2 = convert(d + l, dlen - l, dccsid, &nullbyte, -1, ASCII_CCSID);
194
195     if (l2 < 0) {
196       free(d);
197       return (char *) NULL;
198       }
199
200     l += l2;
201     }
202
203   if ((size_t) l < dlen) {
204     cp = realloc(d, l);         /* Shorten to minimum needed. */
205
206     if (cp)
207       d = cp;
208     }
209
210   return d;
211 }
212
213
214 char *
215 curl_version_ccsid(unsigned int ccsid)
216
217 {
218   int i;
219   char * aversion;
220   char * eversion;
221
222   aversion = curl_version();
223
224   if (!aversion)
225     return aversion;
226
227   i = strlen(aversion) + 1;
228   i *= MAX_CONV_EXPANSION;
229
230   if (!(eversion = Curl_thread_buffer(LK_CURL_VERSION, i)))
231     return (char *) NULL;
232
233   if (convert(eversion, i, ccsid, aversion, -1, ASCII_CCSID) < 0)
234     return (char *) NULL;
235
236   return eversion;
237 }
238
239
240 char *
241 curl_easy_escape_ccsid(CURL * handle, const char * string, int length,
242                        unsigned int sccsid, unsigned int dccsid)
243
244 {
245   char * s;
246   char * d;
247
248   if (!string) {
249     errno = EINVAL;
250     return (char *) NULL;
251     }
252
253   s = dynconvert(ASCII_CCSID, s, length? length: -1, sccsid);
254
255   if (!s)
256     return (char *) NULL;
257
258   d = curl_easy_escape(handle, s, 0);
259   free(s);
260
261   if (!d)
262     return (char *) NULL;
263
264   s = dynconvert(dccsid, d, -1, ASCII_CCSID);
265   free(d);
266   return s;
267 }
268
269
270 char *
271 curl_easy_unescape_ccsid(CURL * handle, const char * string, int length,
272                          int * outlength,
273                          unsigned int sccsid, unsigned int dccsid)
274
275 {
276   char * s;
277   char * d;
278
279   if (!string) {
280     errno = EINVAL;
281     return (char *) NULL;
282     }
283
284   s = dynconvert(ASCII_CCSID, s, length? length: -1, sccsid);
285
286   if (!s)
287     return (char *) NULL;
288
289   d = curl_easy_unescape(handle, s, 0, outlength);
290   free(s);
291
292   if (!d)
293     return (char *) NULL;
294
295   s = dynconvert(dccsid, d, -1, ASCII_CCSID);
296   free(d);
297
298   if (s && outlength)
299     *outlength = strlen(s);
300
301   return s;
302 }
303
304
305 struct curl_slist *
306 curl_slist_append_ccsid(struct curl_slist * list,
307                         const char * data, unsigned int ccsid)
308
309 {
310   char * s;
311
312   s = (char *) NULL;
313
314   if (!data)
315     return curl_slist_append(list, data);
316
317   s = dynconvert(ASCII_CCSID, data, -1, ccsid);
318
319   if (!s)
320     return (struct curl_slist *) NULL;
321
322   list = curl_slist_append(list, s);
323   free(s);
324   return list;
325 }
326
327
328 time_t
329 curl_getdate_ccsid(const char * p, const time_t * unused, unsigned int ccsid)
330
331 {
332   char * s;
333   time_t t;
334
335   if (!p)
336     return curl_getdate(p, unused);
337
338   s = dynconvert(ASCII_CCSID, p, -1, ccsid);
339
340   if (!s)
341     return (time_t) -1;
342
343   t = curl_getdate(s, unused);
344   free(s);
345   return t;
346 }
347
348
349 static int
350 convert_version_info_string(const char * * stringp,
351                             char * * bufp, int * left, unsigned int ccsid)
352
353 {
354   int l;
355
356   /* Helper for curl_version_info_ccsid(): convert a string if defined.
357      Result is stored in the `*left'-byte buffer at `*bufp'.
358      `*bufp' and `*left' are updated accordingly.
359      Return 0 if ok, else -1. */
360
361   if (*stringp) {
362     l = convert(*bufp, *left, ccsid, *stringp, -1, ASCII_CCSID);
363
364     if (l <= 0)
365       return -1;
366
367     *stringp = *bufp;
368     *bufp += l;
369     *left -= l;
370     }
371
372   return 0;
373 }
374
375
376 curl_version_info_data *
377 curl_version_info_ccsid(CURLversion stamp, unsigned int ccsid)
378
379 {
380   curl_version_info_data * p;
381   char * cp;
382   int n;
383   int nproto;
384   int i;
385   curl_version_info_data * id;
386
387   /* The assertion below is possible, because although the second operand
388      is an enum member, the first is a #define. In that case, the OS/400 C
389      compiler seems to compare string values after substitution. */
390
391 #if CURLVERSION_NOW != CURLVERSION_FOURTH
392 #error curl_version_info_data structure has changed: upgrade this procedure too.
393 #endif
394
395   /* If caller has been compiled with a new version, error. */
396
397   if (stamp > CURLVERSION_NOW)
398     return (curl_version_info_data *) NULL;
399
400   p = curl_version_info(stamp);
401
402   if (!p)
403     return p;
404
405   /* Measure thread space needed. */
406
407   n = 0;
408   nproto = 0;
409
410   if (p->protocols) {
411     while (p->protocols[nproto])
412       n += strlen(p->protocols[nproto++]);
413
414     n += nproto++;
415     }
416
417   if (p->version)
418     n += strlen(p->version) + 1;
419
420   if (p->host)
421     n += strlen(p->host) + 1;
422
423   if (p->ssl_version)
424     n += strlen(p->ssl_version) + 1;
425
426   if (p->libz_version)
427     n += strlen(p->libz_version) + 1;
428
429   if (p->ares)
430     n += strlen(p->ares) + 1;
431
432   if (p->libidn)
433     n += strlen(p->libidn) + 1;
434
435   if (p->libssh_version)
436     n += strlen(p->libssh_version) + 1;
437
438   /* Allocate thread space. */
439
440   n *= MAX_CONV_EXPANSION;
441
442   if (nproto)
443     n += nproto * sizeof(const char *);
444
445   cp = Curl_thread_buffer(LK_VERSION_INFO_DATA, n);
446   id = (curl_version_info_data *) Curl_thread_buffer(LK_VERSION_INFO,
447                                                      sizeof *id);
448
449   if (!id || !cp)
450     return (curl_version_info_data *) NULL;
451
452   /* Copy data and convert strings. */
453
454   memcpy((char *) id, (char *) p, sizeof *p);
455
456   if (id->protocols) {
457     id->protocols = (const char * const *) cp;
458     i = nproto * sizeof id->protocols[0];
459     memcpy(cp, (char *) p->protocols, i);
460     cp += i;
461     n -= i;
462
463     for (i = 0; id->protocols[i]; i++)
464       if (convert_version_info_string(((const char * *) id->protocols) + i,
465                                       &cp, &n, ccsid))
466         return (curl_version_info_data *) NULL;
467     }
468
469   if (convert_version_info_string(&id->version, &cp, &n, ccsid))
470     return (curl_version_info_data *) NULL;
471
472   if (convert_version_info_string(&id->host, &cp, &n, ccsid))
473     return (curl_version_info_data *) NULL;
474
475   if (convert_version_info_string(&id->ssl_version, &cp, &n, ccsid))
476     return (curl_version_info_data *) NULL;
477
478   if (convert_version_info_string(&id->libz_version, &cp, &n, ccsid))
479     return (curl_version_info_data *) NULL;
480
481   if (convert_version_info_string(&id->ares, &cp, &n, ccsid))
482     return (curl_version_info_data *) NULL;
483
484   if (convert_version_info_string(&id->libidn, &cp, &n, ccsid))
485     return (curl_version_info_data *) NULL;
486
487   if (convert_version_info_string(&id->libssh_version, &cp, &n, ccsid))
488     return (curl_version_info_data *) NULL;
489
490   return id;
491 }
492
493
494 const char *
495 curl_easy_strerror_ccsid(CURLcode error, unsigned int ccsid)
496
497 {
498   int i;
499   const char * s;
500   char * buf;
501
502   s = curl_easy_strerror(error);
503
504   if (!s)
505     return s;
506
507   i = MAX_CONV_EXPANSION * (strlen(s) + 1);
508
509   if (!(buf = Curl_thread_buffer(LK_EASY_STRERROR, i)))
510     return (const char *) NULL;
511
512   if (convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
513     return (const char *) NULL;
514
515   return (const char *) buf;
516 }
517
518
519 const char *
520 curl_share_strerror_ccsid(CURLSHcode error, unsigned int ccsid)
521
522 {
523   int i;
524   const char * s;
525   char * buf;
526
527   s = curl_share_strerror(error);
528
529   if (!s)
530     return s;
531
532   i = MAX_CONV_EXPANSION * (strlen(s) + 1);
533
534   if (!(buf = Curl_thread_buffer(LK_SHARE_STRERROR, i)))
535     return (const char *) NULL;
536
537   if (convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
538     return (const char *) NULL;
539
540   return (const char *) buf;
541 }
542
543
544 const char *
545 curl_multi_strerror_ccsid(CURLMcode error, unsigned int ccsid)
546
547 {
548   int i;
549   const char * s;
550   char * buf;
551
552   s = curl_multi_strerror(error);
553
554   if (!s)
555     return s;
556
557   i = MAX_CONV_EXPANSION * (strlen(s) + 1);
558
559   if (!(buf = Curl_thread_buffer(LK_MULTI_STRERROR, i)))
560     return (const char *) NULL;
561
562   if (convert(buf, i, ccsid, s, -1, ASCII_CCSID) < 0)
563     return (const char *) NULL;
564
565   return (const char *) buf;
566 }
567
568
569 CURLcode
570 curl_easy_getinfo_ccsid(CURL * curl, CURLINFO info, ...)
571
572 {
573   va_list arg;
574   void * paramp;
575   CURLcode ret;
576   unsigned int ccsid;
577   char * * cpp;
578   char * s;
579   char * d;
580   struct SessionHandle * data;
581
582   /* WARNING: unlike curl_easy_get_info(), the strings returned by this
583      procedure have to be free'ed. */
584
585   data = (struct SessionHandle *) curl;
586   va_start(arg, info);
587   paramp = va_arg(arg, void *);
588   ret = Curl_getinfo(data, info, paramp);
589
590   if (ret != CURLE_OK || ((int) info & CURLINFO_TYPEMASK) != CURLINFO_STRING) {
591     va_end(arg);
592     return ret;
593     }
594
595   ccsid = va_arg(arg, unsigned int);
596   va_end(arg);
597   cpp = (char * *) paramp;
598   s = *cpp;
599
600   if (!s)
601     return ret;
602
603   d = dynconvert(ccsid, s, -1, ASCII_CCSID);
604   *cpp = d;
605
606   if (!d)
607     return CURLE_OUT_OF_MEMORY;
608
609   return ret;
610 }
611
612
613 static int
614 Curl_is_formadd_string(CURLformoption option)
615
616 {
617   switch (option) {
618
619     case CURLFORM_FILENAME:
620     case CURLFORM_CONTENTTYPE:
621     case CURLFORM_BUFFER:
622     case CURLFORM_FILE:
623     case CURLFORM_FILECONTENT:
624     case CURLFORM_COPYCONTENTS:
625     case CURLFORM_COPYNAME:
626       return 1;
627     }
628
629   return 0;
630 }
631
632
633 static void
634 Curl_formadd_release_local(struct curl_forms * forms, int nargs, int skip)
635
636 {
637   while (nargs--)
638     if (nargs != skip)
639       if (Curl_is_formadd_string(forms[nargs].option))
640         if (forms[nargs].value)
641           free((char *) forms[nargs].value);
642
643   free((char *) forms);
644 }
645
646
647 static int
648 Curl_formadd_convert(struct curl_forms * forms,
649                      int formx, int lengthx, unsigned int ccsid)
650
651 {
652   int l;
653   char * cp;
654   char * cp2;
655
656   if (formx < 0 || !forms[formx].value)
657     return 0;
658
659   if (lengthx >= 0)
660     l = (int) forms[lengthx].value;
661   else
662     l = strlen(forms[formx].value) + 1;
663
664   cp = malloc(MAX_CONV_EXPANSION * l);
665
666   if (!cp)
667     return -1;
668
669   l = convert(cp, MAX_CONV_EXPANSION * l, ASCII_CCSID,
670               forms[formx].value, l, ccsid);
671
672   if (l < 0) {
673     free(cp);
674     return -1;
675     }
676
677   cp2 = realloc(cp, l);                 /* Shorten buffer to the string size. */
678
679   if (cp2)
680     cp = cp2;
681
682   forms[formx].value = cp;
683
684   if (lengthx >= 0)
685     forms[lengthx].value = (char *) l;  /* Update to length after conversion. */
686
687   return l;
688 }
689
690
691 CURLFORMcode
692 curl_formadd_ccsid(struct curl_httppost * * httppost,
693                    struct curl_httppost * * last_post, ...)
694
695 {
696   va_list arg;
697   CURLformoption option;
698   CURLFORMcode result;
699   struct curl_forms * forms;
700   struct curl_forms * lforms;
701   struct curl_forms * tforms;
702   unsigned int lformlen;
703   const char * value;
704   unsigned int ccsid;
705   int nargs;
706   int namex;
707   int namelengthx;
708   int contentx;
709   int lengthx;
710   unsigned int contentccsid;
711   unsigned int nameccsid;
712
713   /* A single curl_formadd() call cannot be splitted in several calls to deal
714      with all parameters: the original parameters are thus copied to a local
715      curl_forms array and converted to ASCII when needed.
716      CURLFORM_PTRNAME is processed as if it were CURLFORM_COPYNAME.
717      CURLFORM_COPYNAME and CURLFORM_NAMELENGTH occurrence order in
718      parameters is not defined; for this reason, the actual conversion is
719      delayed to the end of parameter processing. The same applies to
720      CURLFORM_COPYCONTENTS/CURLFORM_CONTENTSLENGTH, but these may appear
721      several times in the parameter list; the problem resides here in knowing
722      which CURLFORM_CONTENTSLENGTH applies to which CURLFORM_COPYCONTENTS and
723      when we can be sure to have both info for conversion: end of parameter
724      list is such a point, but CURLFORM_CONTENTTYPE is also used here as a
725      natural separator between content data definitions; this seems to be
726      in accordance with FormAdd() behavior. */
727
728   /* Allocate the local curl_forms array. */
729
730   lformlen = ALLOC_GRANULE;
731   lforms = malloc(lformlen * sizeof *lforms);
732
733   if (!lforms)
734     return CURL_FORMADD_MEMORY;
735
736   /* Process the arguments, copying them into local array, latching conversion
737      indexes and converting when needed. */
738
739   result = CURL_FORMADD_OK;
740   nargs = 0;
741   contentx = -1;
742   lengthx = -1;
743   namex = -1;
744   namelengthx = -1;
745   forms = (struct curl_forms *) NULL;
746   va_start(arg, last_post);
747
748   for (;;) {
749     /* Make sure there is still room for an item in local array. */
750
751     if (nargs >= lformlen) {
752       lformlen += ALLOC_GRANULE;
753       tforms = realloc(lforms, lformlen * sizeof *lforms);
754
755       if (!tforms) {
756         result = CURL_FORMADD_MEMORY;
757         break;
758         }
759
760       lforms = tforms;
761       }
762
763     /* Get next option. */
764
765     if (forms) {
766       /* Get option from array. */
767
768       option = forms->option;
769       value = forms->value;
770       forms++;
771       }
772     else {
773       /* Get option from arguments. */
774
775       option = va_arg(arg, CURLformoption);
776
777       if (option == CURLFORM_END)
778         break;
779       }
780
781     /* Dispatch by option. */
782
783     switch (option) {
784
785     case CURLFORM_END:
786       forms = (struct curl_forms *) NULL;       /* Leave array mode. */
787       continue;
788
789     case CURLFORM_ARRAY:
790       if (!forms) {
791         forms = va_arg(arg, struct curl_forms *);
792         continue;
793         }
794
795       result = CURL_FORMADD_ILLEGAL_ARRAY;
796       break;
797
798     case CURLFORM_COPYNAME:
799       option = CURLFORM_PTRNAME;                /* Static for now. */
800
801     case CURLFORM_PTRNAME:
802       if (namex >= 0)
803         result = CURL_FORMADD_OPTION_TWICE;
804
805       namex = nargs;
806
807       if (!forms) {
808         value = va_arg(arg, char *);
809         nameccsid = (unsigned int) va_arg(arg, long);
810         }
811       else {
812         nameccsid = (unsigned int) forms->value;
813         forms++;
814         }
815
816       break;
817
818     case CURLFORM_COPYCONTENTS:
819       if (contentx >= 0)
820         result = CURL_FORMADD_OPTION_TWICE;
821
822       contentx = nargs;
823
824       if (!forms) {
825         value = va_arg(arg, char *);
826         contentccsid = (unsigned int) va_arg(arg, long);
827         }
828       else {
829         contentccsid = (unsigned int) forms->value;
830         forms++;
831         }
832
833       break;
834
835     case CURLFORM_PTRCONTENTS:
836     case CURLFORM_BUFFERPTR:
837       if (!forms)
838         value = va_arg(arg, char *);            /* No conversion. */
839
840       break;
841
842     case CURLFORM_CONTENTSLENGTH:
843       lengthx = nargs;
844
845       if (!forms)
846         value = (char *) va_arg(arg, long);
847
848       break;
849
850     case CURLFORM_NAMELENGTH:
851       namelengthx = nargs;
852
853       if (!forms)
854         value = (char *) va_arg(arg, long);
855
856       break;
857
858     case CURLFORM_BUFFERLENGTH:
859       if (!forms)
860         value = (char *) va_arg(arg, long);
861
862       break;
863
864     case CURLFORM_CONTENTHEADER:
865       if (!forms)
866         value = (char *) va_arg(arg, struct curl_slist *);
867
868       break;
869
870     case CURLFORM_STREAM:
871       if (!forms)
872         value = (char *) va_arg(arg, void *);
873
874       break;
875
876     case CURLFORM_CONTENTTYPE:
877       /* If a previous content has been encountered, convert it now. */
878
879       if (Curl_formadd_convert(lforms, contentx, lengthx, contentccsid) < 0) {
880         result = CURL_FORMADD_MEMORY;
881         break;
882         }
883
884       contentx = -1;
885       lengthx = -1;
886       /* Fall into default. */
887
888     default:
889       /* Must be a convertible string. */
890
891       if (!Curl_is_formadd_string(option)) {
892         result = CURL_FORMADD_UNKNOWN_OPTION;
893         break;
894         }
895
896       if (!forms) {
897         value = va_arg(arg, char *);
898         ccsid = (unsigned int) va_arg(arg, long);
899         }
900       else {
901         ccsid = (unsigned int) forms->value;
902         forms++;
903         }
904
905       /* Do the conversion. */
906
907       lforms[nargs].value = value;
908
909       if (Curl_formadd_convert(lforms, nargs, -1, ccsid) < 0) {
910         result = CURL_FORMADD_MEMORY;
911         break;
912         }
913
914       value = lforms[nargs].value;
915       }
916
917     if (result != CURL_FORMADD_OK)
918       break;
919
920     lforms[nargs].value = value;
921     lforms[nargs++].option = option;
922     }
923
924   va_end(arg);
925
926   /* Convert the name and the last content, now that we know their lengths. */
927
928   if (result == CURL_FORMADD_OK && namex >= 0) {
929     if (Curl_formadd_convert(lforms, namex, namelengthx, nameccsid) < 0)
930       result = CURL_FORMADD_MEMORY;
931     else
932       lforms[namex].option = CURLFORM_COPYNAME;         /* Force copy. */
933     }
934
935   if (result == CURL_FORMADD_OK) {
936     if (Curl_formadd_convert(lforms, contentx, lengthx, contentccsid) < 0)
937       result = CURL_FORMADD_MEMORY;
938     else
939       contentx = -1;
940     }
941
942   /* Do the formadd with our converted parameters. */
943
944   if (result == CURL_FORMADD_OK) {
945     lforms[nargs].option = CURLFORM_END;
946     result = curl_formadd(httppost, last_post,
947                           CURLFORM_ARRAY, lforms, CURLFORM_END);
948     }
949
950   /* Terminate. */
951
952   Curl_formadd_release_local(lforms, nargs, contentx);
953   return result;
954 }
955
956
957 typedef struct {
958   curl_formget_callback append;
959   void *                arg;
960   unsigned int          ccsid;
961 }   cfcdata;
962
963
964 static size_t
965 Curl_formget_callback_ccsid(void * arg, const char * buf, size_t len)
966
967 {
968   cfcdata * p;
969   char * b;
970   int l;
971   size_t ret;
972
973   p = (cfcdata *) arg;
974
975   if ((long) len <= 0)
976     return (*p->append)(p->arg, buf, len);
977
978   b = malloc(MAX_CONV_EXPANSION * len);
979
980   if (!b)
981     return (size_t) -1;
982
983   l = convert(b, MAX_CONV_EXPANSION * len, p->ccsid, buf, len, ASCII_CCSID);
984
985   if (l < 0) {
986     free(b);
987     return (size_t) -1;
988     }
989
990   ret = (*p->append)(p->arg, b, l);
991   free(b);
992   return ret == l? len: -1;
993 }
994
995
996 int
997 curl_formget_ccsid(struct curl_httppost * form, void * arg,
998                    curl_formget_callback append, unsigned int ccsid)
999
1000 {
1001   cfcdata lcfc;
1002
1003   lcfc.append = append;
1004   lcfc.arg = arg;
1005   lcfc.ccsid = ccsid;
1006   return curl_formget(form, (void *) &lcfc, Curl_formget_callback_ccsid);
1007 }
1008
1009
1010 CURLcode
1011 curl_easy_setopt_ccsid(CURL * curl, CURLoption tag, ...)
1012
1013 {
1014   CURLcode result;
1015   va_list arg;
1016   struct SessionHandle * data;
1017   char * s;
1018   char * cp;
1019   unsigned int ccsid;
1020   size_t len;
1021   curl_off_t pfsize;
1022   static char testwarn = 1;
1023
1024   /* Warns if this procedure has not been updated when the dupstring enum
1025      changes.
1026      We (try to) do it only once: there is no need to issue several times
1027      the same message; but since threadsafeness is not handled here,
1028      this may occur (and we don't care!). */
1029
1030   if (testwarn) {
1031     testwarn = 0;
1032
1033     if ((int) STRING_LAST != (int) STRING_MAIL_FROM + 1)
1034       curl_mfprintf(stderr,
1035        "*** WARNING: curl_easy_setopt_ccsid() should be reworked ***\n");
1036     }
1037
1038   data = (struct SessionHandle *) curl;
1039   va_start(arg, tag);
1040
1041   switch (tag) {
1042
1043   case CURLOPT_CAINFO:
1044   case CURLOPT_CAPATH:
1045   case CURLOPT_COOKIE:
1046   case CURLOPT_COOKIEFILE:
1047   case CURLOPT_COOKIEJAR:
1048   case CURLOPT_COOKIELIST:
1049   case CURLOPT_CUSTOMREQUEST:
1050   case CURLOPT_EGDSOCKET:
1051   case CURLOPT_ENCODING:
1052   case CURLOPT_FTPPORT:
1053   case CURLOPT_FTP_ACCOUNT:
1054   case CURLOPT_FTP_ALTERNATIVE_TO_USER:
1055   case CURLOPT_INTERFACE:
1056   case CURLOPT_KEYPASSWD:
1057   case CURLOPT_KRBLEVEL:
1058   case CURLOPT_NETRC_FILE:
1059   case CURLOPT_PROXY:
1060   case CURLOPT_PROXYUSERPWD:
1061   case CURLOPT_RANDOM_FILE:
1062   case CURLOPT_RANGE:
1063   case CURLOPT_REFERER:
1064   case CURLOPT_SSH_PRIVATE_KEYFILE:
1065   case CURLOPT_SSH_PUBLIC_KEYFILE:
1066   case CURLOPT_SSLCERT:
1067   case CURLOPT_SSLCERTTYPE:
1068   case CURLOPT_SSLENGINE:
1069   case CURLOPT_SSLKEY:
1070   case CURLOPT_SSLKEYTYPE:
1071   case CURLOPT_SSL_CIPHER_LIST:
1072   case CURLOPT_URL:
1073   case CURLOPT_USERAGENT:
1074   case CURLOPT_USERPWD:
1075   case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
1076   case CURLOPT_CRLFILE:
1077   case CURLOPT_ISSUERCERT:
1078   case CURLOPT_USERNAME:
1079   case CURLOPT_PASSWORD:
1080   case CURLOPT_PROXYUSERNAME:
1081   case CURLOPT_PROXYPASSWORD:
1082   case CURLOPT_NOPROXY:
1083   case CURLOPT_RTSP_SESSION_ID:
1084   case CURLOPT_RTSP_STREAM_URI:
1085   case CURLOPT_RTSP_TRANSPORT:
1086   /* SSH2 not (yet) implemented on OS400. */
1087   /* case CURLOPT_SSH_KNOWNHOSTS: */
1088   case CURLOPT_SOCKS5_GSSAPI_SERVICE:
1089   case CURLOPT_MAIL_FROM:
1090     s = va_arg(arg, char *);
1091     ccsid = va_arg(arg, unsigned int);
1092
1093     if (s) {
1094       s = dynconvert(ASCII_CCSID, s, -1, ccsid);
1095
1096       if (!s) {
1097         result = CURLE_OUT_OF_MEMORY;
1098         break;
1099         }
1100       }
1101
1102     result = curl_easy_setopt(curl, tag, s);
1103
1104     if (s)
1105       free(s);
1106
1107     break;
1108
1109   case CURLOPT_COPYPOSTFIELDS:
1110     /* Special case: byte count may have been given by CURLOPT_POSTFIELDSIZE
1111        prior to this call. In this case, convert the given byte count and
1112        replace the length according to the conversion result. */
1113     s = va_arg(arg, char *);
1114     ccsid = va_arg(arg, unsigned int);
1115
1116     pfsize = data->set.postfieldsize;
1117
1118     if (!s || !pfsize || ccsid == NOCONV_CCSID || ccsid == ASCII_CCSID) {
1119       result = curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, s);
1120       break;
1121       }
1122
1123     if (pfsize == -1) {
1124       /* Data is null-terminated. */
1125       s = dynconvert(ASCII_CCSID, s, -1, ccsid);
1126
1127       if (!s) {
1128         result = CURLE_OUT_OF_MEMORY;
1129         break;
1130         }
1131       }
1132     else {
1133       /* Data length specified. */
1134
1135       if (pfsize < 0 || pfsize > SIZE_MAX) {
1136         result = CURLE_OUT_OF_MEMORY;
1137         break;
1138         }
1139
1140       len = pfsize;
1141       pfsize = len * MAX_CONV_EXPANSION;
1142
1143       if (pfsize > SIZE_MAX)
1144         pfsize = SIZE_MAX;
1145
1146       cp = malloc(pfsize);
1147
1148       if (!cp) {
1149         result = CURLE_OUT_OF_MEMORY;
1150         break;
1151         }
1152
1153       pfsize = convert(cp, pfsize, ASCII_CCSID, s, len, ccsid);
1154
1155       if (pfsize < 0) {
1156         free(cp);
1157         result = CURLE_OUT_OF_MEMORY;
1158         break;
1159         }
1160
1161       data->set.postfieldsize = pfsize;         /* Replace data size. */
1162       s = cp;
1163       }
1164
1165     result = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, s);
1166     data->set.str[STRING_COPYPOSTFIELDS] = s;   /* Give to library. */
1167     break;
1168
1169   case CURLOPT_ERRORBUFFER:                     /* This is an output buffer. */
1170   default:
1171     result = Curl_setopt(data, tag, arg);
1172     break;
1173     }
1174
1175   va_end(arg);
1176   return result;
1177 }
1178
1179
1180 char *
1181 curl_form_long_value(long value)
1182
1183 {
1184   /* ILE/RPG cannot cast an integer to a pointer. This procedure does it. */
1185
1186   return (char *) value;
1187 }