1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, 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  ***************************************************************************/
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 SessionHandle {
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  * readline()
71  *
72  * Reads a complete line from a file into a dynamically allocated buffer.
73  *
74  * Calling function may call this multiple times with same 'buffer'
75  * and 'bufsize' pointers to avoid multiple buffer allocations. Buffer
76  * will be reallocated and 'bufsize' increased until whole line fits in
77  * buffer before returning it.
78  *
79  * Calling function is responsible to free allocated buffer.
80  *
81  * This function may return:
82  *   GPE_OUT_OF_MEMORY
83  *   GPE_END_OF_FILE
84  *   GPE_OK
85  */
86 
readline(char ** buffer,size_t * bufsize,FILE * stream)87 static int readline(char **buffer, size_t *bufsize, FILE *stream)
88 {
89   size_t offset = 0;
90   size_t length;
91   char *newptr;
92 
93   if(!*buffer) {
94     *buffer = malloc(128);
95     if(!*buffer)
96       return GPE_OUT_OF_MEMORY;
97     *bufsize = 128;
98   }
99 
100   for(;;) {
101     int bytestoread = curlx_uztosi(*bufsize - offset);
102 
103     if(!fgets(*buffer + offset, bytestoread, stream))
104       return (offset != 0) ? GPE_OK : GPE_END_OF_FILE ;
105 
106     length = offset + strlen(*buffer + offset);
107     if(*(*buffer + length - 1) == '\n')
108       break;
109     offset = length;
110     if(length < *bufsize - 1)
111       continue;
112 
113     newptr = realloc(*buffer, *bufsize * 2);
114     if(!newptr)
115       return GPE_OUT_OF_MEMORY;
116     *buffer = newptr;
117     *bufsize *= 2;
118   }
119 
120   return GPE_OK;
121 }
122 
123 /*
124  * appenddata()
125  *
126  * This appends data from a given source buffer to the end of the used part of
127  * a destination buffer. Arguments relative to the destination buffer are, the
128  * address of a pointer to the destination buffer 'dst_buf', the length of data
129  * in destination buffer excluding potential null string termination 'dst_len',
130  * the allocated size of destination buffer 'dst_alloc'. All three destination
131  * buffer arguments may be modified by this function. Arguments relative to the
132  * source buffer are, a pointer to the source buffer 'src_buf' and indication
133  * whether the source buffer is base64 encoded or not 'src_b64'.
134  *
135  * If the source buffer is indicated to be base64 encoded, this appends the
136  * decoded data, binary or whatever, to the destination. The source buffer
137  * may not hold binary data, only a null terminated string is valid content.
138  *
139  * Destination buffer will be enlarged and relocated as needed.
140  *
141  * Calling function is responsible to provide preallocated destination
142  * buffer and also to deallocate it when no longer needed.
143  *
144  * This function may return:
145  *   GPE_OUT_OF_MEMORY
146  *   GPE_OK
147  */
148 
appenddata(char ** dst_buf,size_t * dst_len,size_t * dst_alloc,char * src_buf,int src_b64)149 static int appenddata(char  **dst_buf,   /* dest buffer */
150                       size_t *dst_len,   /* dest buffer data length */
151                       size_t *dst_alloc, /* dest buffer allocated size */
152                       char   *src_buf,   /* source buffer */
153                       int     src_b64)   /* != 0 if source is base64 encoded */
154 {
155   size_t need_alloc = 0;
156   size_t src_len = strlen(src_buf);
157 
158   if(!src_len)
159     return GPE_OK;
160 
161   need_alloc = src_len + *dst_len + 1;
162 
163   if(src_b64) {
164     if(src_buf[src_len - 1] == '\r')
165       src_len--;
166 
167     if(src_buf[src_len - 1] == '\n')
168       src_len--;
169   }
170 
171   /* enlarge destination buffer if required */
172   if(need_alloc > *dst_alloc) {
173     size_t newsize = need_alloc * 2;
174     char *newptr = realloc(*dst_buf, newsize);
175     if(!newptr) {
176       return GPE_OUT_OF_MEMORY;
177     }
178     *dst_alloc = newsize;
179     *dst_buf = newptr;
180   }
181 
182   /* memcpy to support binary blobs */
183   memcpy(*dst_buf + *dst_len, src_buf, src_len);
184   *dst_len += src_len;
185   *(*dst_buf + *dst_len) = '\0';
186 
187   return GPE_OK;
188 }
189 
decodedata(char ** buf,size_t * len)190 static int decodedata(char  **buf,   /* dest buffer */
191                       size_t *len)   /* dest buffer data length */
192 {
193   int error = 0;
194   unsigned char *buf64 = NULL;
195   size_t src_len = 0;
196 
197   if(!*len)
198     return GPE_OK;
199 
200   /* base64 decode the given buffer */
201   error = (int) Curl_base64_decode(*buf, &buf64, &src_len);
202   if(error)
203     return GPE_OUT_OF_MEMORY;
204 
205   if(!src_len) {
206     /*
207     ** currently there is no way to tell apart an OOM condition in
208     ** Curl_base64_decode() from zero length decoded data. For now,
209     ** let's just assume it is an OOM condition, currently we have
210     ** no input for this function that decodes to zero length data.
211     */
212     free(buf64);
213 
214     return GPE_OUT_OF_MEMORY;
215   }
216 
217   /* memcpy to support binary blobs */
218   memcpy(*buf, buf64, src_len);
219   *len = src_len;
220   *(*buf + src_len) = '\0';
221 
222   free(buf64);
223 
224   return GPE_OK;
225 }
226 
227 /*
228  * getpart()
229  *
230  * This returns whole contents of specified XML-like section and subsection
231  * from the given file. This is mostly used to retrieve a specific part from
232  * a test definition file for consumption by test suite servers.
233  *
234  * Data is returned in a dynamically allocated buffer, a pointer to this data
235  * and the size of the data is stored at the addresses that caller specifies.
236  *
237  * If the returned data is a string the returned size will be the length of
238  * the string excluding null termination. Otherwise it will just be the size
239  * of the returned binary data.
240  *
241  * Calling function is responsible to free returned buffer.
242  *
243  * This function may return:
244  *   GPE_NO_BUFFER_SPACE
245  *   GPE_OUT_OF_MEMORY
246  *   GPE_OK
247  */
248 
getpart(char ** outbuf,size_t * outlen,const char * main,const char * sub,FILE * stream)249 int getpart(char **outbuf, size_t *outlen,
250             const char *main, const char *sub, FILE *stream)
251 {
252 # define MAX_TAG_LEN 79
253   char couter[MAX_TAG_LEN+1]; /* current outermost section */
254   char cmain[MAX_TAG_LEN+1];  /* current main section */
255   char csub[MAX_TAG_LEN+1];   /* current sub section */
256   char ptag[MAX_TAG_LEN+1];   /* potential tag */
257   char patt[MAX_TAG_LEN+1];   /* potential attributes */
258   char *buffer = NULL;
259   char *ptr;
260   char *end;
261   union {
262     ssize_t sig;
263      size_t uns;
264   } len;
265   size_t bufsize = 0;
266   size_t outalloc = 256;
267   int in_wanted_part = 0;
268   int base64 = 0;
269   int error;
270 
271   enum {
272     STATE_OUTSIDE = 0,
273     STATE_OUTER   = 1,
274     STATE_INMAIN  = 2,
275     STATE_INSUB   = 3,
276     STATE_ILLEGAL = 4
277   } state = STATE_OUTSIDE;
278 
279   *outlen = 0;
280   *outbuf = malloc(outalloc);
281   if(!*outbuf)
282     return GPE_OUT_OF_MEMORY;
283   *(*outbuf) = '\0';
284 
285   couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0';
286 
287   while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) {
288 
289     ptr = buffer;
290     EAT_SPACE(ptr);
291 
292     if('<' != *ptr) {
293       if(in_wanted_part) {
294         show(("=> %s", buffer));
295         error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
296         if(error)
297           break;
298       }
299       continue;
300     }
301 
302     ptr++;
303 
304     if('/' == *ptr) {
305       /*
306       ** closing section tag
307       */
308 
309       ptr++;
310       end = ptr;
311       EAT_WORD(end);
312       if((len.sig = end - ptr) > MAX_TAG_LEN) {
313         error = GPE_NO_BUFFER_SPACE;
314         break;
315       }
316       memcpy(ptag, ptr, len.uns);
317       ptag[len.uns] = '\0';
318 
319       if((STATE_INSUB == state) && !strcmp(csub, ptag)) {
320         /* end of current sub section */
321         state = STATE_INMAIN;
322         csub[0] = '\0';
323         if(in_wanted_part) {
324           /* end of wanted part */
325           in_wanted_part = 0;
326 
327           /* Do we need to base64 decode the data? */
328           if(base64) {
329             error = decodedata(outbuf, outlen);
330             if(error)
331               return error;
332           }
333           break;
334         }
335       }
336       else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) {
337         /* end of current main section */
338         state = STATE_OUTER;
339         cmain[0] = '\0';
340         if(in_wanted_part) {
341           /* end of wanted part */
342           in_wanted_part = 0;
343 
344           /* Do we need to base64 decode the data? */
345           if(base64) {
346             error = decodedata(outbuf, outlen);
347             if(error)
348               return error;
349           }
350           break;
351         }
352       }
353       else if((STATE_OUTER == state) && !strcmp(couter, ptag)) {
354         /* end of outermost file section */
355         state = STATE_OUTSIDE;
356         couter[0] = '\0';
357         if(in_wanted_part) {
358           /* end of wanted part */
359           in_wanted_part = 0;
360           break;
361         }
362       }
363 
364     }
365     else if(!in_wanted_part) {
366       /*
367       ** opening section tag
368       */
369 
370       /* get potential tag */
371       end = ptr;
372       EAT_WORD(end);
373       if((len.sig = end - ptr) > MAX_TAG_LEN) {
374         error = GPE_NO_BUFFER_SPACE;
375         break;
376       }
377       memcpy(ptag, ptr, len.uns);
378       ptag[len.uns] = '\0';
379 
380       /* ignore comments, doctypes and xml declarations */
381       if(('!' == ptag[0]) || ('?' == ptag[0])) {
382         show(("* ignoring (%s)", buffer));
383         continue;
384       }
385 
386       /* get all potential attributes */
387       ptr = end;
388       EAT_SPACE(ptr);
389       end = ptr;
390       while(*end && ('>' != *end))
391         end++;
392       if((len.sig = end - ptr) > MAX_TAG_LEN) {
393         error = GPE_NO_BUFFER_SPACE;
394         break;
395       }
396       memcpy(patt, ptr, len.uns);
397       patt[len.uns] = '\0';
398 
399       if(STATE_OUTSIDE == state) {
400         /* outermost element (<testcase>) */
401         strcpy(couter, ptag);
402         state = STATE_OUTER;
403         continue;
404       }
405       else if(STATE_OUTER == state) {
406         /* start of a main section */
407         strcpy(cmain, ptag);
408         state = STATE_INMAIN;
409         continue;
410       }
411       else if(STATE_INMAIN == state) {
412         /* start of a sub section */
413         strcpy(csub, ptag);
414         state = STATE_INSUB;
415         if(!strcmp(cmain, main) && !strcmp(csub, sub)) {
416           /* start of wanted part */
417           in_wanted_part = 1;
418           if(strstr(patt, "base64="))
419               /* bit rough test, but "mostly" functional, */
420               /* treat wanted part data as base64 encoded */
421               base64 = 1;
422         }
423         continue;
424       }
425 
426     }
427 
428     if(in_wanted_part) {
429       show(("=> %s", buffer));
430       error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
431       if(error)
432         break;
433     }
434 
435   } /* while */
436 
437   free(buffer);
438 
439   if(error != GPE_OK) {
440     if(error == GPE_END_OF_FILE)
441       error = GPE_OK;
442     else {
443       free(*outbuf);
444       *outbuf = NULL;
445       *outlen = 0;
446     }
447   }
448 
449   return error;
450 }
451 
452