Git init
[external/curl.git] / tests / server / getpart.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2010, 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 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)
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     src_len = Curl_base64_decode(src_buf, &buf64.as_uchar);
170     src_buf = buf64.as_char;
171     if(!src_len || !src_buf) {
172       /*
173       ** currently there is no way to tell apart an OOM condition in
174       ** Curl_base64_decode() from zero length decoded data. For now,
175       ** let's just assume it is an OOM condition, currently we have
176       ** no input for this function that decodes to zero length data.
177       */
178       if(buf64.as_char)
179         free(buf64.as_char);
180       return GPE_OUT_OF_MEMORY;
181     }
182   }
183
184   need_alloc = src_len + *dst_len + 1;
185
186   /* enlarge destination buffer if required */
187   if(need_alloc > *dst_alloc) {
188     size_t newsize = need_alloc * 2;
189     char *newptr = realloc(*dst_buf, newsize);
190     if(!newptr) {
191       if(buf64.as_char)
192         free(buf64.as_char);
193       return GPE_OUT_OF_MEMORY;
194     }
195     *dst_alloc = newsize;
196     *dst_buf = newptr;
197   }
198
199   /* memcpy to support binary blobs */
200   memcpy(*dst_buf + *dst_len, src_buf, src_len);
201   *dst_len += src_len;
202   *(*dst_buf + *dst_len) = '\0';
203
204   if(buf64.as_char)
205     free(buf64.as_char);
206
207   return GPE_OK;
208 }
209
210 /*
211  * getpart()
212  *
213  * This returns whole contents of specified XML-like section and subsection
214  * from the given file. This is mostly used to retrieve a specific part from
215  * a test definition file for consumption by test suite servers.
216  *
217  * Data is returned in a dynamically allocated buffer, a pointer to this data
218  * and the size of the data is stored at the addresses that caller specifies.
219  *
220  * If the returned data is a string the returned size will be the length of
221  * the string excluding null termination. Otherwise it will just be the size
222  * of the returned binary data.
223  *
224  * Calling function is responsible to free returned buffer.
225  *
226  * This function may return:
227  *   GPE_NO_BUFFER_SPACE
228  *   GPE_OUT_OF_MEMORY
229  *   GPE_OK
230  */
231
232 int getpart(char **outbuf, size_t *outlen,
233             const char *main, const char *sub, FILE *stream)
234 {
235 # define MAX_TAG_LEN 79
236   char couter[MAX_TAG_LEN+1]; /* current outermost section */
237   char cmain[MAX_TAG_LEN+1];  /* current main section */
238   char csub[MAX_TAG_LEN+1];   /* current sub section */
239   char ptag[MAX_TAG_LEN+1];   /* potential tag */
240   char patt[MAX_TAG_LEN+1];   /* potential attributes */
241   char *buffer = NULL;
242   char *ptr;
243   char *end;
244   union {
245     ssize_t sig;
246      size_t uns;
247   } len;
248   size_t bufsize = 0;
249   size_t outalloc = 256;
250   int in_wanted_part = 0;
251   int base64 = 0;
252   int error;
253
254   enum {
255     STATE_OUTSIDE = 0,
256     STATE_OUTER   = 1,
257     STATE_INMAIN  = 2,
258     STATE_INSUB   = 3,
259     STATE_ILLEGAL = 4
260   } state = STATE_OUTSIDE;
261
262   *outlen = 0;
263   *outbuf = malloc(outalloc);
264   if(!*outbuf)
265     return GPE_OUT_OF_MEMORY;
266   *(*outbuf) = '\0';
267
268   couter[0] = cmain[0] = csub[0] = ptag[0] = patt[0] = '\0';
269
270   while((error = readline(&buffer, &bufsize, stream)) == GPE_OK) {
271
272     ptr = buffer;
273     EAT_SPACE(ptr);
274
275     if('<' != *ptr) {
276       if(in_wanted_part) {
277         show(("=> %s", buffer));
278         error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
279         if(error)
280           break;
281       }
282       continue;
283     }
284
285     ptr++;
286
287     if('/' == *ptr) {
288       /*
289       ** closing section tag
290       */
291
292       ptr++;
293       end = ptr;
294       EAT_WORD(end);
295       if((len.sig = end - ptr) > MAX_TAG_LEN) {
296         error = GPE_NO_BUFFER_SPACE;
297         break;
298       }
299       memcpy(ptag, ptr, len.uns);
300       ptag[len.uns] = '\0';
301
302       if((STATE_INSUB == state) && !strcmp(csub, ptag)) {
303         /* end of current sub section */
304         state = STATE_INMAIN;
305         csub[0] = '\0';
306         if(in_wanted_part) {
307           /* end of wanted part */
308           in_wanted_part = 0;
309           break;
310         }
311       }
312       else if((STATE_INMAIN == state) && !strcmp(cmain, ptag)) {
313         /* end of current main section */
314         state = STATE_OUTER;
315         cmain[0] = '\0';
316         if(in_wanted_part) {
317           /* end of wanted part */
318           in_wanted_part = 0;
319           break;
320         }
321       }
322       else if((STATE_OUTER == state) && !strcmp(couter, ptag)) {
323         /* end of outermost file section */
324         state = STATE_OUTSIDE;
325         couter[0] = '\0';
326         if(in_wanted_part) {
327           /* end of wanted part */
328           in_wanted_part = 0;
329           break;
330         }
331       }
332
333     }
334     else if(!in_wanted_part) {
335       /*
336       ** opening section tag
337       */
338
339       /* get potential tag */
340       end = ptr;
341       EAT_WORD(end);
342       if((len.sig = end - ptr) > 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       /* ignore comments, doctypes and xml declarations */
350       if(('!' == ptag[0]) || ('?' == ptag[0])) {
351         show(("* ignoring (%s)", buffer));
352         continue;
353       }
354
355       /* get all potential attributes */
356       ptr = end;
357       EAT_SPACE(ptr);
358       end = ptr;
359       while(*end && ('>' != *end))
360         end++;
361       if((len.sig = end - ptr) > MAX_TAG_LEN) {
362         error = GPE_NO_BUFFER_SPACE;
363         break;
364       }
365       memcpy(patt, ptr, len.uns);
366       patt[len.uns] = '\0';
367
368       if(STATE_OUTSIDE == state) {
369         /* outermost element (<testcase>) */
370         strcpy(couter, ptag);
371         state = STATE_OUTER;
372         continue;
373       }
374       else if(STATE_OUTER == state) {
375         /* start of a main section */
376         strcpy(cmain, ptag);
377         state = STATE_INMAIN;
378         continue;
379       }
380       else if(STATE_INMAIN == state) {
381         /* start of a sub section */
382         strcpy(csub, ptag);
383         state = STATE_INSUB;
384         if(!strcmp(cmain, main) && !strcmp(csub, sub)) {
385           /* start of wanted part */
386           in_wanted_part = 1;
387           if(strstr(patt, "base64="))
388               /* bit rough test, but "mostly" functional, */
389               /* treat wanted part data as base64 encoded */
390               base64 = 1;
391         }
392         continue;
393       }
394
395     }
396
397     if(in_wanted_part) {
398       show(("=> %s", buffer));
399       error = appenddata(outbuf, outlen, &outalloc, buffer, base64);
400       if(error)
401         break;
402     }
403
404   } /* while */
405
406   if(buffer)
407     free(buffer);
408
409   if(error != GPE_OK) {
410     if(error == GPE_END_OF_FILE)
411       error = GPE_OK;
412     else {
413       if(*outbuf)
414         free(*outbuf);
415       *outbuf = NULL;
416       *outlen = 0;
417     }
418   }
419
420   return error;
421 }
422