Imported Upstream version 7.59.0
[platform/upstream/curl.git] / tests / server / getpart.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, 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 https://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  ***************************************************************************/
22 #include "server_setup.h"
23
24 #include "getpart.h"
25
26 #define ENABLE_CURLX_PRINTF
27 /* make the curlx header define all printf() functions to use the curlx_*
28    versions instead */
29 #include "curlx.h" /* from the private lib dir */
30
31 /* just to please curl_base64.h we create a fake struct */
32 struct Curl_easy {
33   int fake;
34 };
35
36 #include "curl_base64.h"
37 #include "curl_memory.h"
38
39 /* include memdebug.h last */
40 #include "memdebug.h"
41
42 #define EAT_SPACE(p) while(*(p) && ISSPACE(*(p))) (p)++
43
44 #define EAT_WORD(p)  while(*(p) && !ISSPACE(*(p)) && ('>' != *(p))) (p)++
45
46 #ifdef DEBUG_GETPART
47 #define show(x) printf x
48 #else
49 #define show(x) Curl_nop_stmt
50 #endif
51
52 #if defined(_MSC_VER) && defined(_DLL)
53 #  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
54 #endif
55
56 curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
57 curl_free_callback Curl_cfree = (curl_free_callback)free;
58 curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
59 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup;
60 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
61 #if defined(WIN32) && defined(UNICODE)
62 curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
63 #endif
64
65 #if defined(_MSC_VER) && defined(_DLL)
66 #  pragma warning(default:4232) /* MSVC extension, dllimport identity */
67 #endif
68
69
70 /*
71  * Curl_convert_clone() returns a malloced copy of the source string (if
72  * returning CURLE_OK), with the data converted to network format. This
73  * function is used by base64 code in libcurl built to support data
74  * conversion. This is a DUMMY VERSION that returns data unmodified - for
75  * use by the test server only.
76  */
77 CURLcode Curl_convert_clone(struct Curl_easy *data,
78                             const char *indata,
79                             size_t insize,
80                             char **outbuf);
81 CURLcode Curl_convert_clone(struct Curl_easy *data,
82                             const char *indata,
83                             size_t insize,
84                             char **outbuf)
85 {
86   char *convbuf;
87   (void)data;
88
89   convbuf = malloc(insize);
90   if(!convbuf)
91     return CURLE_OUT_OF_MEMORY;
92
93   memcpy(convbuf, indata, insize);
94   *outbuf = convbuf;
95   return CURLE_OK;
96 }
97
98 /*
99  * readline()
100  *
101  * Reads a complete line from a file into a dynamically allocated buffer.
102  *
103  * Calling function may call this multiple times with same 'buffer'
104  * and 'bufsize' pointers to avoid multiple buffer allocations. Buffer
105  * will be reallocated and 'bufsize' increased until whole line fits in
106  * buffer before returning it.
107  *
108  * Calling function is responsible to free allocated buffer.
109  *
110  * This function may return:
111  *   GPE_OUT_OF_MEMORY
112  *   GPE_END_OF_FILE
113  *   GPE_OK
114  */
115
116 static int readline(char **buffer, size_t *bufsize, FILE *stream)
117 {
118   size_t offset = 0;
119   size_t length;
120   char *newptr;
121
122   if(!*buffer) {
123     *buffer = malloc(128);
124     if(!*buffer)
125       return GPE_OUT_OF_MEMORY;
126     *bufsize = 128;
127   }
128
129   for(;;) {
130     int bytestoread = curlx_uztosi(*bufsize - offset);
131
132     if(!fgets(*buffer + offset, bytestoread, stream))
133       return (offset != 0) ? GPE_OK : GPE_END_OF_FILE;
134
135     length = offset + strlen(*buffer + offset);
136     if(*(*buffer + length - 1) == '\n')
137       break;
138     offset = length;
139     if(length < *bufsize - 1)
140       continue;
141
142     newptr = realloc(*buffer, *bufsize * 2);
143     if(!newptr)
144       return GPE_OUT_OF_MEMORY;
145     *buffer = newptr;
146     *bufsize *= 2;
147   }
148
149   return GPE_OK;
150 }
151
152 /*
153  * appenddata()
154  *
155  * This appends data from a given source buffer to the end of the used part of
156  * a destination buffer. Arguments relative to the destination buffer are, the
157  * address of a pointer to the destination buffer 'dst_buf', the length of data
158  * in destination buffer excluding potential null string termination 'dst_len',
159  * the allocated size of destination buffer 'dst_alloc'. All three destination
160  * buffer arguments may be modified by this function. Arguments relative to the
161  * source buffer are, a pointer to the source buffer 'src_buf' and indication
162  * whether the source buffer is base64 encoded or not 'src_b64'.
163  *
164  * If the source buffer is indicated to be base64 encoded, this appends the
165  * decoded data, binary or whatever, to the destination. The source buffer
166  * may not hold binary data, only a null terminated string is valid content.
167  *
168  * Destination buffer will be enlarged and relocated as needed.
169  *
170  * Calling function is responsible to provide preallocated destination
171  * buffer and also to deallocate it when no longer needed.
172  *
173  * This function may return:
174  *   GPE_OUT_OF_MEMORY
175  *   GPE_OK
176  */
177
178 static int appenddata(char  **dst_buf,   /* dest buffer */
179                       size_t *dst_len,   /* dest buffer data length */
180                       size_t *dst_alloc, /* dest buffer allocated size */
181                       char   *src_buf,   /* source buffer */
182                       int     src_b64)   /* != 0 if source is base64 encoded */
183 {
184   size_t need_alloc = 0;
185   size_t src_len = strlen(src_buf);
186
187   if(!src_len)
188     return GPE_OK;
189
190   need_alloc = src_len + *dst_len + 1;
191
192   if(src_b64) {
193     if(src_buf[src_len - 1] == '\r')
194       src_len--;
195
196     if(src_buf[src_len - 1] == '\n')
197       src_len--;
198   }
199
200   /* enlarge destination buffer if required */
201   if(need_alloc > *dst_alloc) {
202     size_t newsize = need_alloc * 2;
203     char *newptr = realloc(*dst_buf, newsize);
204     if(!newptr) {
205       return GPE_OUT_OF_MEMORY;
206     }
207     *dst_alloc = newsize;
208     *dst_buf = newptr;
209   }
210
211   /* memcpy to support binary blobs */
212   memcpy(*dst_buf + *dst_len, src_buf, src_len);
213   *dst_len += src_len;
214   *(*dst_buf + *dst_len) = '\0';
215
216   return GPE_OK;
217 }
218
219 static int decodedata(char  **buf,   /* dest buffer */
220                       size_t *len)   /* dest buffer data length */
221 {
222   CURLcode error = CURLE_OK;
223   unsigned char *buf64 = NULL;
224   size_t src_len = 0;
225
226   if(!*len)
227     return GPE_OK;
228
229   /* base64 decode the given buffer */
230   error = Curl_base64_decode(*buf, &buf64, &src_len);
231   if(error)
232     return GPE_OUT_OF_MEMORY;
233
234   if(!src_len) {
235     /*
236     ** currently there is no way to tell apart an OOM condition in
237     ** Curl_base64_decode() from zero length decoded data. For now,
238     ** let's just assume it is an OOM condition, currently we have
239     ** no input for this function that decodes to zero length data.
240     */
241     free(buf64);
242
243     return GPE_OUT_OF_MEMORY;
244   }
245
246   /* memcpy to support binary blobs */
247   memcpy(*buf, buf64, src_len);
248   *len = src_len;
249   *(*buf + src_len) = '\0';
250
251   free(buf64);
252
253   return GPE_OK;
254 }
255
256 /*
257  * getpart()
258  *
259  * This returns whole contents of specified XML-like section and subsection
260  * from the given file. This is mostly used to retrieve a specific part from
261  * a test definition file for consumption by test suite servers.
262  *
263  * Data is returned in a dynamically allocated buffer, a pointer to this data
264  * and the size of the data is stored at the addresses that caller specifies.
265  *
266  * If the returned data is a string the returned size will be the length of
267  * the string excluding null termination. Otherwise it will just be the size
268  * of the returned binary data.
269  *
270  * Calling function is responsible to free returned buffer.
271  *
272  * This function may return:
273  *   GPE_NO_BUFFER_SPACE
274  *   GPE_OUT_OF_MEMORY
275  *   GPE_OK
276  */
277
278 int getpart(char **outbuf, size_t *outlen,
279             const char *main, const char *sub, FILE *stream)
280 {
281 # define MAX_TAG_LEN 79
282   char couter[MAX_TAG_LEN + 1]; /* current outermost section */
283   char cmain[MAX_TAG_LEN + 1];  /* current main section */
284   char csub[MAX_TAG_LEN + 1];   /* current sub section */
285   char ptag[MAX_TAG_LEN + 1];   /* potential tag */
286   char patt[MAX_TAG_LEN + 1];   /* potential attributes */
287   char *buffer = NULL;
288   char *ptr;
289   char *end;
290   union {
291     ssize_t sig;
292      size_t uns;
293   } len;
294   size_t bufsize = 0;
295   size_t outalloc = 256;
296   int in_wanted_part = 0;
297   int base64 = 0;
298   int error;
299
300   enum {
301     STATE_OUTSIDE = 0,
302     STATE_OUTER   = 1,
303     STATE_INMAIN  = 2,
304     STATE_INSUB   = 3,
305     STATE_ILLEGAL = 4
306   } state = STATE_OUTSIDE;
307
308   *outlen = 0;
309   *outbuf = malloc(outalloc);
310   if(!*outbuf)
311     return GPE_OUT_OF_MEMORY;
312   *(*outbuf) = '\0';
313
314   couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0';
315
316   while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) {
317
318     ptr = buffer;
319     EAT_SPACE(ptr);
320
321     if('<' != *ptr) {
322       if(in_wanted_part) {
323         show(("=> %s", buffer));
324         error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
325         if(error)
326           break;
327       }
328       continue;
329     }
330
331     ptr++;
332
333     if('/' == *ptr) {
334       /*
335       ** closing section tag
336       */
337
338       ptr++;
339       end = ptr;
340       EAT_WORD(end);
341       len.sig = end - ptr;
342       if(len.sig > MAX_TAG_LEN) {
343         error = GPE_NO_BUFFER_SPACE;
344         break;
345       }
346       memcpy(ptag, ptr, len.uns);
347       ptag[len.uns] = '\0';
348
349       if((STATE_INSUB == state) && !strcmp(csub, ptag)) {
350         /* end of current sub section */
351         state = STATE_INMAIN;
352         csub[0] = '\0';
353         if(in_wanted_part) {
354           /* end of wanted part */
355           in_wanted_part = 0;
356
357           /* Do we need to base64 decode the data? */
358           if(base64) {
359             error = decodedata(outbuf, outlen);
360             if(error)
361               return error;
362           }
363           break;
364         }
365       }
366       else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) {
367         /* end of current main section */
368         state = STATE_OUTER;
369         cmain[0] = '\0';
370         if(in_wanted_part) {
371           /* end of wanted part */
372           in_wanted_part = 0;
373
374           /* Do we need to base64 decode the data? */
375           if(base64) {
376             error = decodedata(outbuf, outlen);
377             if(error)
378               return error;
379           }
380           break;
381         }
382       }
383       else if((STATE_OUTER == state) && !strcmp(couter, ptag)) {
384         /* end of outermost file section */
385         state = STATE_OUTSIDE;
386         couter[0] = '\0';
387         if(in_wanted_part) {
388           /* end of wanted part */
389           in_wanted_part = 0;
390           break;
391         }
392       }
393
394     }
395     else if(!in_wanted_part) {
396       /*
397       ** opening section tag
398       */
399
400       /* get potential tag */
401       end = ptr;
402       EAT_WORD(end);
403       len.sig = end - ptr;
404       if(len.sig > MAX_TAG_LEN) {
405         error = GPE_NO_BUFFER_SPACE;
406         break;
407       }
408       memcpy(ptag, ptr, len.uns);
409       ptag[len.uns] = '\0';
410
411       /* ignore comments, doctypes and xml declarations */
412       if(('!' == ptag[0]) || ('?' == ptag[0])) {
413         show(("* ignoring (%s)", buffer));
414         continue;
415       }
416
417       /* get all potential attributes */
418       ptr = end;
419       EAT_SPACE(ptr);
420       end = ptr;
421       while(*end && ('>' != *end))
422         end++;
423       len.sig = end - ptr;
424       if(len.sig > MAX_TAG_LEN) {
425         error = GPE_NO_BUFFER_SPACE;
426         break;
427       }
428       memcpy(patt, ptr, len.uns);
429       patt[len.uns] = '\0';
430
431       if(STATE_OUTSIDE == state) {
432         /* outermost element (<testcase>) */
433         strcpy(couter, ptag);
434         state = STATE_OUTER;
435         continue;
436       }
437       else if(STATE_OUTER == state) {
438         /* start of a main section */
439         strcpy(cmain, ptag);
440         state = STATE_INMAIN;
441         continue;
442       }
443       else if(STATE_INMAIN == state) {
444         /* start of a sub section */
445         strcpy(csub, ptag);
446         state = STATE_INSUB;
447         if(!strcmp(cmain, main) && !strcmp(csub, sub)) {
448           /* start of wanted part */
449           in_wanted_part = 1;
450           if(strstr(patt, "base64="))
451               /* bit rough test, but "mostly" functional, */
452               /* treat wanted part data as base64 encoded */
453               base64 = 1;
454         }
455         continue;
456       }
457
458     }
459
460     if(in_wanted_part) {
461       show(("=> %s", buffer));
462       error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
463       if(error)
464         break;
465     }
466
467   } /* while */
468
469   free(buffer);
470
471   if(error != GPE_OK) {
472     if(error == GPE_END_OF_FILE)
473       error = GPE_OK;
474     else {
475       free(*outbuf);
476       *outbuf = NULL;
477       *outlen = 0;
478     }
479   }
480
481   return error;
482 }