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