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