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 */
32struct 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
56curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
57curl_free_callback Curl_cfree = (curl_free_callback)free;
58curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
59curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup;
60curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
61#if defined(WIN32) && defined(UNICODE)
62curl_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
87static 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
149static 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
190static 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    if(buf64)
213      free(buf64);
214
215    return GPE_OUT_OF_MEMORY;
216  }
217
218  /* memcpy to support binary blobs */
219  memcpy(*buf, buf64, src_len);
220  *len = src_len;
221  *(*buf + src_len) = '\0';
222
223  free(buf64);
224
225  return GPE_OK;
226}
227
228/*
229 * getpart()
230 *
231 * This returns whole contents of specified XML-like section and subsection
232 * from the given file. This is mostly used to retrieve a specific part from
233 * a test definition file for consumption by test suite servers.
234 *
235 * Data is returned in a dynamically allocated buffer, a pointer to this data
236 * and the size of the data is stored at the addresses that caller specifies.
237 *
238 * If the returned data is a string the returned size will be the length of
239 * the string excluding null termination. Otherwise it will just be the size
240 * of the returned binary data.
241 *
242 * Calling function is responsible to free returned buffer.
243 *
244 * This function may return:
245 *   GPE_NO_BUFFER_SPACE
246 *   GPE_OUT_OF_MEMORY
247 *   GPE_OK
248 */
249
250int getpart(char **outbuf, size_t *outlen,
251            const char *main, const char *sub, FILE *stream)
252{
253# define MAX_TAG_LEN 79
254  char couter[MAX_TAG_LEN+1]; /* current outermost section */
255  char cmain[MAX_TAG_LEN+1];  /* current main section */
256  char csub[MAX_TAG_LEN+1];   /* current sub section */
257  char ptag[MAX_TAG_LEN+1];   /* potential tag */
258  char patt[MAX_TAG_LEN+1];   /* potential attributes */
259  char *buffer = NULL;
260  char *ptr;
261  char *end;
262  union {
263    ssize_t sig;
264     size_t uns;
265  } len;
266  size_t bufsize = 0;
267  size_t outalloc = 256;
268  int in_wanted_part = 0;
269  int base64 = 0;
270  int error;
271
272  enum {
273    STATE_OUTSIDE = 0,
274    STATE_OUTER   = 1,
275    STATE_INMAIN  = 2,
276    STATE_INSUB   = 3,
277    STATE_ILLEGAL = 4
278  } state = STATE_OUTSIDE;
279
280  *outlen = 0;
281  *outbuf = malloc(outalloc);
282  if(!*outbuf)
283    return GPE_OUT_OF_MEMORY;
284  *(*outbuf) = '\0';
285
286  couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0';
287
288  while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) {
289
290    ptr = buffer;
291    EAT_SPACE(ptr);
292
293    if('<' != *ptr) {
294      if(in_wanted_part) {
295        show(("=> %s", buffer));
296        error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
297        if(error)
298          break;
299      }
300      continue;
301    }
302
303    ptr++;
304
305    if('/' == *ptr) {
306      /*
307      ** closing section tag
308      */
309
310      ptr++;
311      end = ptr;
312      EAT_WORD(end);
313      if((len.sig = end - ptr) > MAX_TAG_LEN) {
314        error = GPE_NO_BUFFER_SPACE;
315        break;
316      }
317      memcpy(ptag, ptr, len.uns);
318      ptag[len.uns] = '\0';
319
320      if((STATE_INSUB == state) && !strcmp(csub, ptag)) {
321        /* end of current sub section */
322        state = STATE_INMAIN;
323        csub[0] = '\0';
324        if(in_wanted_part) {
325          /* end of wanted part */
326          in_wanted_part = 0;
327
328          /* Do we need to base64 decode the data? */
329          if(base64) {
330            error = decodedata(outbuf, outlen);
331            if(error)
332              return error;
333          }
334          break;
335        }
336      }
337      else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) {
338        /* end of current main section */
339        state = STATE_OUTER;
340        cmain[0] = '\0';
341        if(in_wanted_part) {
342          /* end of wanted part */
343          in_wanted_part = 0;
344
345          /* Do we need to base64 decode the data? */
346          if(base64) {
347            error = decodedata(outbuf, outlen);
348            if(error)
349              return error;
350          }
351          break;
352        }
353      }
354      else if((STATE_OUTER == state) && !strcmp(couter, ptag)) {
355        /* end of outermost file section */
356        state = STATE_OUTSIDE;
357        couter[0] = '\0';
358        if(in_wanted_part) {
359          /* end of wanted part */
360          in_wanted_part = 0;
361          break;
362        }
363      }
364
365    }
366    else if(!in_wanted_part) {
367      /*
368      ** opening section tag
369      */
370
371      /* get potential tag */
372      end = ptr;
373      EAT_WORD(end);
374      if((len.sig = end - ptr) > 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      /* ignore comments, doctypes and xml declarations */
382      if(('!' == ptag[0]) || ('?' == ptag[0])) {
383        show(("* ignoring (%s)", buffer));
384        continue;
385      }
386
387      /* get all potential attributes */
388      ptr = end;
389      EAT_SPACE(ptr);
390      end = ptr;
391      while(*end && ('>' != *end))
392        end++;
393      if((len.sig = end - ptr) > MAX_TAG_LEN) {
394        error = GPE_NO_BUFFER_SPACE;
395        break;
396      }
397      memcpy(patt, ptr, len.uns);
398      patt[len.uns] = '\0';
399
400      if(STATE_OUTSIDE == state) {
401        /* outermost element (<testcase>) */
402        strcpy(couter, ptag);
403        state = STATE_OUTER;
404        continue;
405      }
406      else if(STATE_OUTER == state) {
407        /* start of a main section */
408        strcpy(cmain, ptag);
409        state = STATE_INMAIN;
410        continue;
411      }
412      else if(STATE_INMAIN == state) {
413        /* start of a sub section */
414        strcpy(csub, ptag);
415        state = STATE_INSUB;
416        if(!strcmp(cmain, main) && !strcmp(csub, sub)) {
417          /* start of wanted part */
418          in_wanted_part = 1;
419          if(strstr(patt, "base64="))
420              /* bit rough test, but "mostly" functional, */
421              /* treat wanted part data as base64 encoded */
422              base64 = 1;
423        }
424        continue;
425      }
426
427    }
428
429    if(in_wanted_part) {
430      show(("=> %s", buffer));
431      error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
432      if(error)
433        break;
434    }
435
436  } /* while */
437
438  if(buffer)
439    free(buffer);
440
441  if(error != GPE_OK) {
442    if(error == GPE_END_OF_FILE)
443      error = GPE_OK;
444    else {
445      if(*outbuf)
446        free(*outbuf);
447      *outbuf = NULL;
448      *outlen = 0;
449    }
450  }
451
452  return error;
453}
454
455