Merge pull request #25 from trtom/master
[platform/upstream/curl.git] / tests / server / getpart.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2011, 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
23 #define CURL_NO_OLDIES
24
25 #include "setup.h"
26
27 #include "getpart.h"
28
29 #define ENABLE_CURLX_PRINTF
30 /* make the curlx header define all printf() functions to use the curlx_*
31    versions instead */
32 #include "curlx.h" /* from the private lib dir */
33
34 /* just to please curl_base64.h we create a fake struct */
35 struct SessionHandle {
36   int fake;
37 };
38
39 #include "curl_base64.h"
40 #include "curl_memory.h"
41
42 /* include memdebug.h last */
43 #include "memdebug.h"
44
45 #define EAT_SPACE(p) while(*(p) && ISSPACE(*(p))) (p)++
46
47 #define EAT_WORD(p)  while(*(p) && !ISSPACE(*(p)) && ('>' != *(p))) (p)++
48
49 #ifdef DEBUG_GETPART
50 #define show(x) printf x
51 #else
52 #define show(x) Curl_nop_stmt
53 #endif
54
55 #if defined(_MSC_VER) && defined(_DLL)
56 #  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
57 #endif
58
59 curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc;
60 curl_free_callback Curl_cfree = (curl_free_callback)free;
61 curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
62 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)strdup;
63 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
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
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
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, src_len;
156   union {
157     unsigned char *as_uchar;
158              char *as_char;
159   } buf64;
160
161   src_len = strlen(src_buf);
162   if(!src_len)
163     return GPE_OK;
164
165   buf64.as_char = NULL;
166
167   if(src_b64) {
168     /* base64 decode the given buffer */
169     int error = (int) Curl_base64_decode(src_buf, &buf64.as_uchar, &src_len);
170     if(error)
171       return GPE_OUT_OF_MEMORY;
172     src_buf = buf64.as_char;
173     if(!src_len || !src_buf) {
174       /*
175       ** currently there is no way to tell apart an OOM condition in
176       ** Curl_base64_decode() from zero length decoded data. For now,
177       ** let's just assume it is an OOM condition, currently we have
178       ** no input for this function that decodes to zero length data.
179       */
180       if(buf64.as_char)
181         free(buf64.as_char);
182       return GPE_OUT_OF_MEMORY;
183     }
184   }
185
186   need_alloc = src_len + *dst_len + 1;
187
188   /* enlarge destination buffer if required */
189   if(need_alloc > *dst_alloc) {
190     size_t newsize = need_alloc * 2;
191     char *newptr = realloc(*dst_buf, newsize);
192     if(!newptr) {
193       if(buf64.as_char)
194         free(buf64.as_char);
195       return GPE_OUT_OF_MEMORY;
196     }
197     *dst_alloc = newsize;
198     *dst_buf = newptr;
199   }
200
201   /* memcpy to support binary blobs */
202   memcpy(*dst_buf + *dst_len, src_buf, src_len);
203   *dst_len += src_len;
204   *(*dst_buf + *dst_len) = '\0';
205
206   if(buf64.as_char)
207     free(buf64.as_char);
208
209   return GPE_OK;
210 }
211
212 /*
213  * getpart()
214  *
215  * This returns whole contents of specified XML-like section and subsection
216  * from the given file. This is mostly used to retrieve a specific part from
217  * a test definition file for consumption by test suite servers.
218  *
219  * Data is returned in a dynamically allocated buffer, a pointer to this data
220  * and the size of the data is stored at the addresses that caller specifies.
221  *
222  * If the returned data is a string the returned size will be the length of
223  * the string excluding null termination. Otherwise it will just be the size
224  * of the returned binary data.
225  *
226  * Calling function is responsible to free returned buffer.
227  *
228  * This function may return:
229  *   GPE_NO_BUFFER_SPACE
230  *   GPE_OUT_OF_MEMORY
231  *   GPE_OK
232  */
233
234 int getpart(char **outbuf, size_t *outlen,
235             const char *main, const char *sub, FILE *stream)
236 {
237 # define MAX_TAG_LEN 79
238   char couter[MAX_TAG_LEN+1]; /* current outermost section */
239   char cmain[MAX_TAG_LEN+1];  /* current main section */
240   char csub[MAX_TAG_LEN+1];   /* current sub section */
241   char ptag[MAX_TAG_LEN+1];   /* potential tag */
242   char patt[MAX_TAG_LEN+1];   /* potential attributes */
243   char *buffer = NULL;
244   char *ptr;
245   char *end;
246   union {
247     ssize_t sig;
248      size_t uns;
249   } len;
250   size_t bufsize = 0;
251   size_t outalloc = 256;
252   int in_wanted_part = 0;
253   int base64 = 0;
254   int error;
255
256   enum {
257     STATE_OUTSIDE = 0,
258     STATE_OUTER   = 1,
259     STATE_INMAIN  = 2,
260     STATE_INSUB   = 3,
261     STATE_ILLEGAL = 4
262   } state = STATE_OUTSIDE;
263
264   *outlen = 0;
265   *outbuf = malloc(outalloc);
266   if(!*outbuf)
267     return GPE_OUT_OF_MEMORY;
268   *(*outbuf) = '\0';
269
270   couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0';
271
272   while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) {
273
274     ptr = buffer;
275     EAT_SPACE(ptr);
276
277     if('<' != *ptr) {
278       if(in_wanted_part) {
279         show(("=> %s", buffer));
280         error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
281         if(error)
282           break;
283       }
284       continue;
285     }
286
287     ptr++;
288
289     if('/' == *ptr) {
290       /*
291       ** closing section tag
292       */
293
294       ptr++;
295       end = ptr;
296       EAT_WORD(end);
297       if((len.sig = end - ptr) > MAX_TAG_LEN) {
298         error = GPE_NO_BUFFER_SPACE;
299         break;
300       }
301       memcpy(ptag, ptr, len.uns);
302       ptag[len.uns] = '\0';
303
304       if((STATE_INSUB == state) && !strcmp(csub, ptag)) {
305         /* end of current sub section */
306         state = STATE_INMAIN;
307         csub[0] = '\0';
308         if(in_wanted_part) {
309           /* end of wanted part */
310           in_wanted_part = 0;
311           break;
312         }
313       }
314       else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) {
315         /* end of current main section */
316         state = STATE_OUTER;
317         cmain[0] = '\0';
318         if(in_wanted_part) {
319           /* end of wanted part */
320           in_wanted_part = 0;
321           break;
322         }
323       }
324       else if((STATE_OUTER == state) && !strcmp(couter, ptag)) {
325         /* end of outermost file section */
326         state = STATE_OUTSIDE;
327         couter[0] = '\0';
328         if(in_wanted_part) {
329           /* end of wanted part */
330           in_wanted_part = 0;
331           break;
332         }
333       }
334
335     }
336     else if(!in_wanted_part) {
337       /*
338       ** opening section tag
339       */
340
341       /* get potential tag */
342       end = ptr;
343       EAT_WORD(end);
344       if((len.sig = end - ptr) > MAX_TAG_LEN) {
345         error = GPE_NO_BUFFER_SPACE;
346         break;
347       }
348       memcpy(ptag, ptr, len.uns);
349       ptag[len.uns] = '\0';
350
351       /* ignore comments, doctypes and xml declarations */
352       if(('!' == ptag[0]) || ('?' == ptag[0])) {
353         show(("* ignoring (%s)", buffer));
354         continue;
355       }
356
357       /* get all potential attributes */
358       ptr = end;
359       EAT_SPACE(ptr);
360       end = ptr;
361       while(*end && ('>' != *end))
362         end++;
363       if((len.sig = end - ptr) > MAX_TAG_LEN) {
364         error = GPE_NO_BUFFER_SPACE;
365         break;
366       }
367       memcpy(patt, ptr, len.uns);
368       patt[len.uns] = '\0';
369
370       if(STATE_OUTSIDE == state) {
371         /* outermost element (<testcase>) */
372         strcpy(couter, ptag);
373         state = STATE_OUTER;
374         continue;
375       }
376       else if(STATE_OUTER == state) {
377         /* start of a main section */
378         strcpy(cmain, ptag);
379         state = STATE_INMAIN;
380         continue;
381       }
382       else if(STATE_INMAIN == state) {
383         /* start of a sub section */
384         strcpy(csub, ptag);
385         state = STATE_INSUB;
386         if(!strcmp(cmain, main) && !strcmp(csub, sub)) {
387           /* start of wanted part */
388           in_wanted_part = 1;
389           if(strstr(patt, "base64="))
390               /* bit rough test, but "mostly" functional, */
391               /* treat wanted part data as base64 encoded */
392               base64 = 1;
393         }
394         continue;
395       }
396
397     }
398
399     if(in_wanted_part) {
400       show(("=> %s", buffer));
401       error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
402       if(error)
403         break;
404     }
405
406   } /* while */
407
408   if(buffer)
409     free(buffer);
410
411   if(error != GPE_OK) {
412     if(error == GPE_END_OF_FILE)
413       error = GPE_OK;
414     else {
415       if(*outbuf)
416         free(*outbuf);
417       *outbuf = NULL;
418       *outlen = 0;
419     }
420   }
421
422   return error;
423 }
424