/src/MapServer/src/mapio.c
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * $Id$ |
3 | | * |
4 | | * Project: MapServer |
5 | | * Purpose: Implementations for MapServer IO redirection capability. |
6 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com> |
10 | | * |
11 | | * Permission is hereby granted, free of charge, to any person obtaining a |
12 | | * copy of this software and associated documentation files (the "Software"), |
13 | | * to deal in the Software without restriction, including without limitation |
14 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
15 | | * and/or sell copies of the Software, and to permit persons to whom the |
16 | | * Software is furnished to do so, subject to the following conditions: |
17 | | * |
18 | | * The above copyright notice and this permission notice shall be included in |
19 | | * all copies of this Software or works derived from this Software. |
20 | | * |
21 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
22 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
24 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
26 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
27 | | * DEALINGS IN THE SOFTWARE. |
28 | | ****************************************************************************/ |
29 | | |
30 | | #include <stdarg.h> |
31 | | |
32 | | #include "mapserver.h" |
33 | | #include "mapthread.h" |
34 | | |
35 | | #ifdef _WIN32 |
36 | | #include <fcntl.h> |
37 | | #include <io.h> |
38 | | #endif |
39 | | |
40 | | #ifdef MOD_WMS_ENABLED |
41 | | #include "httpd.h" |
42 | | #include "apr_strings.h" |
43 | | #endif |
44 | | |
45 | | static int is_msIO_initialized = MS_FALSE; |
46 | | static int is_msIO_header_enabled = MS_TRUE; |
47 | | |
48 | | typedef struct msIOContextGroup_t { |
49 | | msIOContext stdin_context; |
50 | | msIOContext stdout_context; |
51 | | msIOContext stderr_context; |
52 | | |
53 | | void *thread_id; |
54 | | struct msIOContextGroup_t *next; |
55 | | } msIOContextGroup; |
56 | | |
57 | | static msIOContextGroup default_contexts; |
58 | | static msIOContextGroup *io_context_list = NULL; |
59 | | static void msIO_Initialize(void); |
60 | | |
61 | | #ifdef msIO_printf |
62 | | #undef msIO_printf |
63 | | #undef msIO_fprintf |
64 | | #undef msIO_fwrite |
65 | | #undef msIO_fread |
66 | | #undef msIO_vfprintf |
67 | | #endif |
68 | | |
69 | | /************************************************************************/ |
70 | | /* msIO_Cleanup() */ |
71 | | /************************************************************************/ |
72 | | |
73 | | void msIO_Cleanup() |
74 | | |
75 | 0 | { |
76 | 0 | if (is_msIO_initialized) |
77 | | |
78 | 0 | { |
79 | 0 | is_msIO_initialized = MS_FALSE; |
80 | 0 | while (io_context_list != NULL) { |
81 | 0 | msIOContextGroup *last = io_context_list; |
82 | 0 | io_context_list = io_context_list->next; |
83 | 0 | free(last); |
84 | 0 | } |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | /************************************************************************/ |
89 | | /* msIO_GetContextGroup() */ |
90 | | /************************************************************************/ |
91 | | |
92 | | static msIOContextGroup *msIO_GetContextGroup() |
93 | | |
94 | 1 | { |
95 | 1 | void *nThreadId = msGetThreadId(); |
96 | 1 | msIOContextGroup *prev = NULL, *group = io_context_list; |
97 | | |
98 | 1 | if (group != NULL && group->thread_id == nThreadId) |
99 | 0 | return group; |
100 | | |
101 | | /* -------------------------------------------------------------------- */ |
102 | | /* Search for group for this thread */ |
103 | | /* -------------------------------------------------------------------- */ |
104 | 1 | msAcquireLock(TLOCK_IOCONTEXT); |
105 | 1 | msIO_Initialize(); |
106 | | |
107 | 1 | group = io_context_list; |
108 | 1 | while (group != NULL && group->thread_id != nThreadId) { |
109 | 0 | prev = group; |
110 | 0 | group = group->next; |
111 | 0 | } |
112 | | |
113 | | /* -------------------------------------------------------------------- */ |
114 | | /* If we found it, make sure it is pushed to the front of the */ |
115 | | /* link for faster finding next time, and return it. */ |
116 | | /* -------------------------------------------------------------------- */ |
117 | 1 | if (group != NULL) { |
118 | 0 | if (prev != NULL) { |
119 | 0 | prev->next = group->next; |
120 | 0 | group->next = io_context_list; |
121 | 0 | io_context_list = group; |
122 | 0 | } |
123 | |
|
124 | 0 | msReleaseLock(TLOCK_IOCONTEXT); |
125 | 0 | return group; |
126 | 0 | } |
127 | | |
128 | | /* -------------------------------------------------------------------- */ |
129 | | /* Create a new context group for this thread. */ |
130 | | /* -------------------------------------------------------------------- */ |
131 | 1 | group = (msIOContextGroup *)calloc(1, sizeof(msIOContextGroup)); |
132 | | |
133 | 1 | group->stdin_context = default_contexts.stdin_context; |
134 | 1 | group->stdout_context = default_contexts.stdout_context; |
135 | 1 | group->stderr_context = default_contexts.stderr_context; |
136 | 1 | group->thread_id = nThreadId; |
137 | | |
138 | 1 | group->next = io_context_list; |
139 | 1 | io_context_list = group; |
140 | | |
141 | 1 | msReleaseLock(TLOCK_IOCONTEXT); |
142 | | |
143 | 1 | return group; |
144 | 1 | } |
145 | | |
146 | | /* returns MS_TRUE if the msIO standard output hasn't been redirected */ |
147 | 0 | int msIO_isStdContext() { |
148 | 0 | msIOContextGroup *group = io_context_list; |
149 | 0 | void *nThreadId = msGetThreadId(); |
150 | 0 | if (!group || group->thread_id != nThreadId) { |
151 | 0 | group = msIO_GetContextGroup(); |
152 | 0 | if (!group) { |
153 | 0 | return MS_FALSE; /* probably a bug */ |
154 | 0 | } |
155 | 0 | } |
156 | 0 | if (group->stderr_context.cbData == (void *)stderr && |
157 | 0 | group->stdin_context.cbData == (void *)stdin && |
158 | 0 | group->stdout_context.cbData == (void *)stdout) |
159 | 0 | return MS_TRUE; |
160 | 0 | return MS_FALSE; |
161 | 0 | } |
162 | | |
163 | | /************************************************************************/ |
164 | | /* msIO_getHandler() */ |
165 | | /************************************************************************/ |
166 | | |
167 | | msIOContext *msIO_getHandler(FILE *fp) |
168 | | |
169 | 38.1k | { |
170 | 38.1k | void *nThreadId = msGetThreadId(); |
171 | 38.1k | msIOContextGroup *group = io_context_list; |
172 | | |
173 | 38.1k | msIO_Initialize(); |
174 | | |
175 | 38.1k | if (group == NULL || group->thread_id != nThreadId) { |
176 | 1 | group = msIO_GetContextGroup(); |
177 | 1 | if (group == NULL) |
178 | 0 | return NULL; |
179 | 1 | } |
180 | | |
181 | 38.1k | if (fp == stdin || fp == NULL || strcmp((const char *)fp, "stdin") == 0) |
182 | 0 | return &(group->stdin_context); |
183 | 38.1k | else if (fp == stdout || strcmp((const char *)fp, "stdout") == 0) |
184 | 2.64k | return &(group->stdout_context); |
185 | 35.5k | else if (fp == stderr || strcmp((const char *)fp, "stderr") == 0) |
186 | 4.10k | return &(group->stderr_context); |
187 | 31.4k | else |
188 | 31.4k | return NULL; |
189 | 38.1k | } |
190 | | |
191 | | /************************************************************************/ |
192 | | /* msIO_setHeaderEnabled() */ |
193 | | /************************************************************************/ |
194 | | |
195 | 0 | void msIO_setHeaderEnabled(int bFlag) { is_msIO_header_enabled = bFlag; } |
196 | | |
197 | | /************************************************************************/ |
198 | | /* msIO_setHeader() */ |
199 | | /************************************************************************/ |
200 | | |
201 | 0 | void msIO_setHeader(const char *header, const char *value, ...) { |
202 | 0 | va_list args; |
203 | 0 | va_start(args, value); |
204 | | #ifdef MOD_WMS_ENABLED |
205 | | msIOContext *ioctx = msIO_getHandler(stdout); |
206 | | if (ioctx && !strcmp(ioctx->label, "apache")) { |
207 | | |
208 | | request_rec *r = (request_rec *)(ioctx->cbData); |
209 | | char *fullvalue = apr_pvsprintf(r->pool, value, args); |
210 | | if (strcasecmp(header, "Content-Type") == 0) { |
211 | | r->content_type = fullvalue; |
212 | | } else if (strcasecmp(header, "Status") == 0) { |
213 | | r->status = atoi(fullvalue); |
214 | | } else { |
215 | | apr_table_setn(r->headers_out, apr_pstrdup(r->pool, header), fullvalue); |
216 | | } |
217 | | } else { |
218 | | #endif // MOD_WMS_ENABLED |
219 | 0 | if (is_msIO_header_enabled) { |
220 | 0 | msIO_fprintf(stdout, "%s: ", header); |
221 | 0 | msIO_vfprintf(stdout, value, args); |
222 | 0 | msIO_fprintf(stdout, "\r\n"); |
223 | 0 | } |
224 | | #ifdef MOD_WMS_ENABLED |
225 | | } |
226 | | #endif |
227 | 0 | va_end(args); |
228 | 0 | } |
229 | | |
230 | 0 | void msIO_sendHeaders() { |
231 | | #ifdef MOD_WMS_ENABLED |
232 | | msIOContext *ioctx = msIO_getHandler(stdout); |
233 | | if (ioctx && !strcmp(ioctx->label, "apache")) |
234 | | return; |
235 | | #endif // !MOD_WMS_ENABLED |
236 | 0 | if (is_msIO_header_enabled) { |
237 | 0 | msIO_printf("\r\n"); |
238 | 0 | fflush(stdout); |
239 | 0 | } |
240 | 0 | } |
241 | | |
242 | | /************************************************************************/ |
243 | | /* msIO_installHandlers() */ |
244 | | /************************************************************************/ |
245 | | |
246 | | int msIO_installHandlers(msIOContext *stdin_context, |
247 | | msIOContext *stdout_context, |
248 | | msIOContext *stderr_context) |
249 | | |
250 | 0 | { |
251 | 0 | msIOContextGroup *group; |
252 | |
|
253 | 0 | msIO_Initialize(); |
254 | |
|
255 | 0 | group = msIO_GetContextGroup(); |
256 | |
|
257 | 0 | if (stdin_context == NULL) |
258 | 0 | group->stdin_context = default_contexts.stdin_context; |
259 | 0 | else if (stdin_context != &group->stdin_context) |
260 | 0 | group->stdin_context = *stdin_context; |
261 | |
|
262 | 0 | if (stdout_context == NULL) |
263 | 0 | group->stdout_context = default_contexts.stdout_context; |
264 | 0 | else if (stdout_context != &group->stdout_context) |
265 | 0 | group->stdout_context = *stdout_context; |
266 | |
|
267 | 0 | if (stderr_context == NULL) |
268 | 0 | group->stderr_context = default_contexts.stderr_context; |
269 | 0 | else if (stderr_context != &group->stderr_context) |
270 | 0 | group->stderr_context = *stderr_context; |
271 | |
|
272 | 0 | return MS_TRUE; |
273 | 0 | } |
274 | | |
275 | | /************************************************************************/ |
276 | | /* msIO_contextRead() */ |
277 | | /************************************************************************/ |
278 | | |
279 | | int msIO_contextRead(msIOContext *context, void *data, int byteCount) |
280 | | |
281 | 0 | { |
282 | 0 | if (context->write_channel == MS_TRUE) |
283 | 0 | return 0; |
284 | 0 | else |
285 | 0 | return context->readWriteFunc(context->cbData, data, byteCount); |
286 | 0 | } |
287 | | |
288 | | /************************************************************************/ |
289 | | /* msIO_contextWrite() */ |
290 | | /************************************************************************/ |
291 | | |
292 | | int msIO_contextWrite(msIOContext *context, const void *data, int byteCount) |
293 | | |
294 | 6.74k | { |
295 | 6.74k | if (context->write_channel == MS_FALSE) |
296 | 0 | return 0; |
297 | 6.74k | else |
298 | 6.74k | return context->readWriteFunc(context->cbData, (void *)data, byteCount); |
299 | 6.74k | } |
300 | | |
301 | | /* ==================================================================== */ |
302 | | /* ==================================================================== */ |
303 | | /* Stdio-like cover functions. */ |
304 | | /* ==================================================================== */ |
305 | | /* ==================================================================== */ |
306 | | |
307 | | /************************************************************************/ |
308 | | /* _ms_vsprintf() */ |
309 | | /************************************************************************/ |
310 | | |
311 | | static int _ms_vsprintf(char **workBufPtr, const char *format, va_list ap) |
312 | | |
313 | 0 | { |
314 | 0 | int ret_val; |
315 | 0 | int workBufSize = 16000; |
316 | |
|
317 | 0 | *workBufPtr = (char *)malloc(workBufSize * sizeof(char)); |
318 | 0 | if (*workBufPtr == NULL) { |
319 | 0 | msSetError(MS_MEMERR, NULL, "_ms_vsprintf()"); |
320 | 0 | return -1; |
321 | 0 | } |
322 | | |
323 | 0 | #if defined(HAVE_VSNPRINTF) |
324 | | /* This should grow a big enough buffer to hold any formatted result. */ |
325 | 0 | { |
326 | 0 | va_list wrk_args; |
327 | |
|
328 | 0 | #ifdef va_copy |
329 | 0 | va_copy(wrk_args, ap); |
330 | | #else |
331 | | wrk_args = ap; |
332 | | #endif |
333 | |
|
334 | 0 | while ((ret_val = vsnprintf(*workBufPtr, workBufSize, format, wrk_args)) >= |
335 | 0 | workBufSize - 1 || |
336 | 0 | ret_val == -1) { |
337 | 0 | workBufSize *= 4; |
338 | 0 | *workBufPtr = (char *)realloc(*workBufPtr, workBufSize); |
339 | 0 | if (*workBufPtr == NULL) { |
340 | 0 | msSetError(MS_MEMERR, NULL, "_ms_vsprintf()"); |
341 | 0 | va_end(wrk_args); |
342 | 0 | return -1; |
343 | 0 | } |
344 | 0 | #ifdef va_copy |
345 | 0 | va_end(wrk_args); |
346 | 0 | va_copy(wrk_args, ap); |
347 | | #else |
348 | | wrk_args = ap; |
349 | | #endif |
350 | 0 | } |
351 | 0 | va_end(wrk_args); |
352 | 0 | } |
353 | | #else |
354 | | /* We do not have vsnprintf()... there is a risk of buffer overrun */ |
355 | | ret_val = vsprintf(*workBufPtr, format, ap); |
356 | | |
357 | | if (ret_val < 0 || ret_val >= workBufSize) { |
358 | | msSetError(MS_MISCERR, "Possible buffer overrun.", "_ms_vsprintf()"); |
359 | | msFree(*workBufPtr); |
360 | | *workBufPtr = NULL; |
361 | | return -1; |
362 | | } |
363 | | #endif |
364 | | |
365 | 0 | return ret_val; |
366 | 0 | } |
367 | | |
368 | | /************************************************************************/ |
369 | | /* msIO_printf() */ |
370 | | /************************************************************************/ |
371 | | |
372 | | int msIO_printf(const char *format, ...) |
373 | | |
374 | 0 | { |
375 | 0 | va_list args; |
376 | 0 | int ret; |
377 | 0 | va_start(args, format); |
378 | 0 | ret = msIO_vfprintf(stdout, format, args); |
379 | 0 | va_end(args); |
380 | 0 | return ret; |
381 | 0 | } |
382 | | |
383 | | /************************************************************************/ |
384 | | /* msIO_fprintf() */ |
385 | | /************************************************************************/ |
386 | | |
387 | | int msIO_fprintf(FILE *fp, const char *format, ...) |
388 | | |
389 | 38.1k | { |
390 | 38.1k | va_list args; |
391 | 38.1k | int ret; |
392 | 38.1k | va_start(args, format); |
393 | 38.1k | ret = msIO_vfprintf(fp, format, args); |
394 | 38.1k | va_end(args); |
395 | 38.1k | return ret; |
396 | 38.1k | } |
397 | | |
398 | | /************************************************************************/ |
399 | | /* msIO_vfprintf() */ |
400 | | /************************************************************************/ |
401 | | |
402 | | int msIO_vfprintf(FILE *fp, const char *format, va_list ap) |
403 | | |
404 | 38.1k | { |
405 | 38.1k | int return_val; |
406 | 38.1k | msIOContext *context; |
407 | 38.1k | char workBuf[8000]; |
408 | | |
409 | | #if !defined(HAVE_VSNPRINTF) |
410 | | return_val = vsprintf(workBuf, format, ap); |
411 | | |
412 | | if (return_val < 0 || return_val >= sizeof(workBuf)) { |
413 | | msSetError(MS_MISCERR, "Possible buffer overrun.", "msIO_vfprintf()"); |
414 | | return -1; |
415 | | } |
416 | | |
417 | | char *outbuf = workBuf; |
418 | | |
419 | | #else |
420 | | |
421 | 38.1k | va_list args_copy; |
422 | 38.1k | #ifdef va_copy |
423 | 38.1k | va_copy(args_copy, ap); |
424 | | #else |
425 | | args_copy = ap; |
426 | | #endif /* va_copy */ |
427 | | |
428 | 38.1k | char *largerBuf = NULL; |
429 | 38.1k | return_val = vsnprintf(workBuf, sizeof(workBuf), format, ap); |
430 | 38.1k | if (return_val == -1 || return_val >= (int)sizeof(workBuf) - 1) { |
431 | 0 | return_val = _ms_vsprintf(&largerBuf, format, args_copy); |
432 | 0 | } |
433 | 38.1k | va_end(args_copy); |
434 | | |
435 | 38.1k | if (return_val < 0) |
436 | 0 | return -1; |
437 | | |
438 | 38.1k | char *outbuf = largerBuf ? largerBuf : workBuf; |
439 | | |
440 | 38.1k | #endif /* HAVE_VSNPRINTF */ |
441 | | |
442 | 38.1k | context = msIO_getHandler(fp); |
443 | 38.1k | if (context == NULL) |
444 | 31.4k | return_val = fwrite(outbuf, 1, return_val, fp); |
445 | 6.74k | else |
446 | 6.74k | return_val = msIO_contextWrite(context, outbuf, return_val); |
447 | | |
448 | 38.1k | #if defined(HAVE_VSNPRINTF) |
449 | 38.1k | msFree(largerBuf); |
450 | 38.1k | #endif |
451 | | |
452 | 38.1k | return return_val; |
453 | 38.1k | } |
454 | | |
455 | | /************************************************************************/ |
456 | | /* msIO_fwrite() */ |
457 | | /************************************************************************/ |
458 | | |
459 | | int msIO_fwrite(const void *data, size_t size, size_t nmemb, FILE *fp) |
460 | | |
461 | 0 | { |
462 | 0 | msIOContext *context; |
463 | |
|
464 | 0 | if (size == 0 || nmemb == 0) |
465 | 0 | return 0; |
466 | | |
467 | 0 | context = msIO_getHandler(fp); |
468 | 0 | if (context == NULL) |
469 | 0 | return fwrite(data, size, nmemb, fp); |
470 | 0 | else |
471 | 0 | return msIO_contextWrite(context, data, size * nmemb) / size; |
472 | 0 | } |
473 | | |
474 | | /************************************************************************/ |
475 | | /* msIO_fread() */ |
476 | | /************************************************************************/ |
477 | | |
478 | | int msIO_fread(void *data, size_t size, size_t nmemb, FILE *fp) |
479 | | |
480 | 0 | { |
481 | 0 | msIOContext *context; |
482 | |
|
483 | 0 | if (size == 0 || nmemb == 0) |
484 | 0 | return 0; |
485 | | |
486 | 0 | context = msIO_getHandler(fp); |
487 | 0 | if (context == NULL) |
488 | 0 | return fread(data, size, nmemb, fp); |
489 | 0 | else |
490 | 0 | return msIO_contextRead(context, data, size * nmemb) / size; |
491 | 0 | } |
492 | | |
493 | | /* ==================================================================== */ |
494 | | /* ==================================================================== */ |
495 | | /* Internal default callbacks implementing stdio reading and */ |
496 | | /* writing. */ |
497 | | /* ==================================================================== */ |
498 | | /* ==================================================================== */ |
499 | | |
500 | | /************************************************************************/ |
501 | | /* msIO_stdioRead() */ |
502 | | /* */ |
503 | | /* This is the default implementation via stdio. */ |
504 | | /************************************************************************/ |
505 | | |
506 | | static int msIO_stdioRead(void *cbData, void *data, int byteCount) |
507 | | |
508 | 0 | { |
509 | 0 | return fread(data, 1, byteCount, (FILE *)cbData); |
510 | 0 | } |
511 | | |
512 | | /************************************************************************/ |
513 | | /* msIO_stdioWrite() */ |
514 | | /* */ |
515 | | /* This is the default implementation via stdio. */ |
516 | | /************************************************************************/ |
517 | | |
518 | | static int msIO_stdioWrite(void *cbData, void *data, int byteCount) |
519 | | |
520 | 6.74k | { |
521 | 6.74k | return fwrite(data, 1, byteCount, (FILE *)cbData); |
522 | 6.74k | } |
523 | | |
524 | | /************************************************************************/ |
525 | | /* msIO_Initialize() */ |
526 | | /************************************************************************/ |
527 | | |
528 | | static void msIO_Initialize(void) |
529 | | |
530 | 38.1k | { |
531 | 38.1k | if (is_msIO_initialized == MS_TRUE) |
532 | 38.1k | return; |
533 | | |
534 | 1 | default_contexts.stdin_context.label = "stdio"; |
535 | 1 | default_contexts.stdin_context.write_channel = MS_FALSE; |
536 | 1 | default_contexts.stdin_context.readWriteFunc = msIO_stdioRead; |
537 | 1 | default_contexts.stdin_context.cbData = (void *)stdin; |
538 | | |
539 | 1 | default_contexts.stdout_context.label = "stdio"; |
540 | 1 | default_contexts.stdout_context.write_channel = MS_TRUE; |
541 | 1 | default_contexts.stdout_context.readWriteFunc = msIO_stdioWrite; |
542 | 1 | default_contexts.stdout_context.cbData = (void *)stdout; |
543 | | |
544 | 1 | default_contexts.stderr_context.label = "stdio"; |
545 | 1 | default_contexts.stderr_context.write_channel = MS_TRUE; |
546 | 1 | default_contexts.stderr_context.readWriteFunc = msIO_stdioWrite; |
547 | 1 | default_contexts.stderr_context.cbData = (void *)stderr; |
548 | | |
549 | 1 | default_contexts.next = NULL; |
550 | 1 | default_contexts.thread_id = 0; |
551 | | |
552 | 1 | is_msIO_initialized = MS_TRUE; |
553 | 1 | } |
554 | | |
555 | | /* ==================================================================== */ |
556 | | /* ==================================================================== */ |
557 | | /* FastCGI output redirection functions. */ |
558 | | /* ==================================================================== */ |
559 | | /* ==================================================================== */ |
560 | | |
561 | | /************************************************************************/ |
562 | | /* msIO_needBinaryStdout() */ |
563 | | /* */ |
564 | | /* This function is intended to ensure that stdout is in binary */ |
565 | | /* mode. */ |
566 | | /* */ |
567 | | /* But don't do it we are using FastCGI. We will take care of */ |
568 | | /* doing it in the libfcgi library in that case for the normal */ |
569 | | /* cgi case, and for the fastcgi case the _setmode() call */ |
570 | | /* causes a crash. */ |
571 | | /************************************************************************/ |
572 | | |
573 | | int msIO_needBinaryStdout() |
574 | | |
575 | 0 | { |
576 | | #if defined(_WIN32) && !defined(USE_FASTCGI) |
577 | | if (_setmode(_fileno(stdout), _O_BINARY) == -1) { |
578 | | msSetError(MS_IOERR, "Unable to change stdout to binary mode.", |
579 | | "msIO_needBinaryStdout()"); |
580 | | return (MS_FAILURE); |
581 | | } |
582 | | #endif |
583 | |
|
584 | 0 | return MS_SUCCESS; |
585 | 0 | } |
586 | | |
587 | | /************************************************************************/ |
588 | | /* msIO_needBinaryStdin() */ |
589 | | /* */ |
590 | | /* This function is intended to ensure that stdin is in binary */ |
591 | | /* mode. */ |
592 | | /* */ |
593 | | /* But don't do it we are using FastCGI. We will take care of */ |
594 | | /* doing it in the libfcgi library in that case for the normal */ |
595 | | /* cgi case, and for the fastcgi case the _setmode() call */ |
596 | | /* causes a crash. */ |
597 | | /************************************************************************/ |
598 | | |
599 | | int msIO_needBinaryStdin() |
600 | | |
601 | 0 | { |
602 | | #if defined(_WIN32) && !defined(USE_FASTCGI) |
603 | | if (_setmode(_fileno(stdin), _O_BINARY) == -1) { |
604 | | msSetError(MS_IOERR, "Unable to change stdin to binary mode.", |
605 | | "msIO_needBinaryStdin()"); |
606 | | return (MS_FAILURE); |
607 | | } |
608 | | #endif |
609 | |
|
610 | 0 | return MS_SUCCESS; |
611 | 0 | } |
612 | | |
613 | | /* ==================================================================== */ |
614 | | /* memory buffer io handling functions. */ |
615 | | /* ==================================================================== */ |
616 | | |
617 | | /************************************************************************/ |
618 | | /* msIO_resetHandlers() */ |
619 | | /************************************************************************/ |
620 | | |
621 | | void msIO_resetHandlers() |
622 | | |
623 | 0 | { |
624 | 0 | msIOContextGroup *group = msIO_GetContextGroup(); |
625 | |
|
626 | 0 | if (group == NULL) |
627 | 0 | return; |
628 | | |
629 | 0 | if (strcmp(group->stdin_context.label, "buffer") == 0) { |
630 | 0 | msIOBuffer *buf = (msIOBuffer *)group->stdin_context.cbData; |
631 | |
|
632 | 0 | if (buf->data != NULL) |
633 | 0 | free(buf->data); |
634 | 0 | free(buf); |
635 | 0 | } |
636 | |
|
637 | 0 | if (strcmp(group->stdout_context.label, "buffer") == 0) { |
638 | 0 | msIOBuffer *buf = (msIOBuffer *)group->stdout_context.cbData; |
639 | |
|
640 | 0 | if (buf->data != NULL) |
641 | 0 | free(buf->data); |
642 | 0 | free(buf); |
643 | 0 | } |
644 | |
|
645 | 0 | if (strcmp(group->stderr_context.label, "buffer") == 0) { |
646 | 0 | msIOBuffer *buf = (msIOBuffer *)group->stderr_context.cbData; |
647 | |
|
648 | 0 | if (buf->data != NULL) |
649 | 0 | free(buf->data); |
650 | 0 | free(buf); |
651 | 0 | } |
652 | |
|
653 | 0 | msIO_installHandlers(NULL, NULL, NULL); |
654 | 0 | } |
655 | | |
656 | | /************************************************************************/ |
657 | | /* msIO_installStdoutToBuffer() */ |
658 | | /************************************************************************/ |
659 | | |
660 | | void msIO_installStdoutToBuffer() |
661 | | |
662 | 0 | { |
663 | 0 | msIOContextGroup *group = msIO_GetContextGroup(); |
664 | 0 | msIOContext context; |
665 | |
|
666 | 0 | context.label = "buffer"; |
667 | 0 | context.write_channel = MS_TRUE; |
668 | 0 | context.readWriteFunc = msIO_bufferWrite; |
669 | 0 | context.cbData = calloc(1, sizeof(msIOBuffer)); |
670 | |
|
671 | 0 | msIO_installHandlers(&group->stdin_context, &context, &group->stderr_context); |
672 | 0 | } |
673 | | |
674 | | /************************************************************************/ |
675 | | /* msIO_pushStdoutToBufferAndGetOldContext() */ |
676 | | /* */ |
677 | | /* This function installs a temporary buffer I/O context and returns */ |
678 | | /* previously installed stdout handler. This previous stdout handler */ |
679 | | /* should later be restored with msIO_restoreOldStdoutContext(). */ |
680 | | /* This function can be for example used when wanting to ingest into */ |
681 | | /* libxml objects XML generated by msIO_fprintf() */ |
682 | | /************************************************************************/ |
683 | | |
684 | | msIOContext *msIO_pushStdoutToBufferAndGetOldContext() |
685 | | |
686 | 0 | { |
687 | 0 | msIOContextGroup *group = msIO_GetContextGroup(); |
688 | 0 | msIOContext *old_context; |
689 | | |
690 | | /* Backup current context */ |
691 | 0 | old_context = (msIOContext *)msSmallMalloc(sizeof(msIOContext)); |
692 | 0 | memcpy(old_context, &group->stdout_context, sizeof(msIOContext)); |
693 | |
|
694 | 0 | msIO_installStdoutToBuffer(); |
695 | |
|
696 | 0 | return old_context; |
697 | 0 | } |
698 | | |
699 | | /************************************************************************/ |
700 | | /* msIO_restoreOldStdoutContext() */ |
701 | | /************************************************************************/ |
702 | | |
703 | 0 | void msIO_restoreOldStdoutContext(msIOContext *context_to_restore) { |
704 | 0 | msIOContextGroup *group = msIO_GetContextGroup(); |
705 | 0 | msIOContext *prev_context = &group->stdout_context; |
706 | 0 | msIOBuffer *buffer; |
707 | | |
708 | | /* Free memory associated to our temporary context */ |
709 | 0 | assert(strcmp(prev_context->label, "buffer") == 0); |
710 | | |
711 | 0 | buffer = (msIOBuffer *)prev_context->cbData; |
712 | 0 | msFree(buffer->data); |
713 | 0 | msFree(buffer); |
714 | | |
715 | | /* Restore old context */ |
716 | 0 | msIO_installHandlers(&group->stdin_context, context_to_restore, |
717 | 0 | &group->stderr_context); |
718 | |
|
719 | 0 | msFree(context_to_restore); |
720 | 0 | } |
721 | | |
722 | | /************************************************************************/ |
723 | | /* msIO_installStdinFromBuffer() */ |
724 | | /************************************************************************/ |
725 | | |
726 | | void msIO_installStdinFromBuffer() |
727 | | |
728 | 0 | { |
729 | 0 | msIOContextGroup *group = msIO_GetContextGroup(); |
730 | 0 | msIOContext context; |
731 | |
|
732 | 0 | context.label = "buffer"; |
733 | 0 | context.write_channel = MS_FALSE; |
734 | 0 | context.readWriteFunc = msIO_bufferRead; |
735 | 0 | context.cbData = calloc(1, sizeof(msIOBuffer)); |
736 | |
|
737 | 0 | msIO_installHandlers(&context, &group->stdout_context, |
738 | 0 | &group->stderr_context); |
739 | 0 | } |
740 | | |
741 | | /************************************************************************/ |
742 | | /* msIO_getAndStripStdoutBufferMimeHeaders() */ |
743 | | /* */ |
744 | | /************************************************************************/ |
745 | | |
746 | 0 | hashTableObj *msIO_getAndStripStdoutBufferMimeHeaders() { |
747 | | /* -------------------------------------------------------------------- */ |
748 | | /* Find stdout buffer. */ |
749 | | /* -------------------------------------------------------------------- */ |
750 | 0 | msIOContext *ctx = msIO_getHandler((FILE *)"stdout"); |
751 | 0 | msIOBuffer *buf; |
752 | 0 | int start_of_mime_header, current_pos; |
753 | 0 | hashTableObj *hashTable; |
754 | |
|
755 | 0 | if (ctx == NULL || ctx->write_channel == MS_FALSE || |
756 | 0 | strcmp(ctx->label, "buffer") != 0) { |
757 | 0 | msSetError(MS_MISCERR, "Can't identify msIO buffer.", |
758 | 0 | "msIO_getAndStripStdoutBufferMimeHeaders"); |
759 | 0 | return NULL; |
760 | 0 | } |
761 | | |
762 | 0 | buf = (msIOBuffer *)ctx->cbData; |
763 | |
|
764 | 0 | hashTable = msCreateHashTable(); |
765 | | |
766 | | /* -------------------------------------------------------------------- */ |
767 | | /* Loop over all headers. */ |
768 | | /* -------------------------------------------------------------------- */ |
769 | 0 | current_pos = 0; |
770 | 0 | while (MS_TRUE) { |
771 | 0 | int pos_of_column = -1; |
772 | 0 | char *key, *value; |
773 | |
|
774 | 0 | start_of_mime_header = current_pos; |
775 | 0 | while (current_pos < buf->data_offset) { |
776 | 0 | if (buf->data[current_pos] == '\r') { |
777 | 0 | if (current_pos + 1 == buf->data_offset || |
778 | 0 | buf->data[current_pos + 1] != '\n') { |
779 | 0 | pos_of_column = -1; |
780 | 0 | break; |
781 | 0 | } |
782 | 0 | break; |
783 | 0 | } |
784 | 0 | if (buf->data[current_pos] == ':') { |
785 | 0 | pos_of_column = current_pos; |
786 | 0 | if (current_pos + 1 == buf->data_offset || |
787 | 0 | buf->data[current_pos + 1] != ' ') { |
788 | 0 | pos_of_column = -1; |
789 | 0 | break; |
790 | 0 | } |
791 | 0 | } |
792 | 0 | current_pos++; |
793 | 0 | } |
794 | |
|
795 | 0 | if (pos_of_column < 0 || current_pos == buf->data_offset) { |
796 | 0 | msSetError(MS_MISCERR, "Corrupt mime headers.", |
797 | 0 | "msIO_getAndStripStdoutBufferMimeHeaders"); |
798 | 0 | msFreeHashTable(hashTable); |
799 | 0 | return NULL; |
800 | 0 | } |
801 | | |
802 | 0 | key = (char *)malloc(pos_of_column - start_of_mime_header + 1); |
803 | 0 | memcpy(key, buf->data + start_of_mime_header, |
804 | 0 | pos_of_column - start_of_mime_header); |
805 | 0 | key[pos_of_column - start_of_mime_header] = '\0'; |
806 | |
|
807 | 0 | value = (char *)malloc(current_pos - (pos_of_column + 2) + 1); |
808 | 0 | memcpy(value, buf->data + pos_of_column + 2, |
809 | 0 | current_pos - (pos_of_column + 2)); |
810 | 0 | value[current_pos - (pos_of_column + 2)] = '\0'; |
811 | |
|
812 | 0 | msInsertHashTable(hashTable, key, value); |
813 | |
|
814 | 0 | msFree(key); |
815 | 0 | msFree(value); |
816 | | |
817 | | /* -------------------------------------------------------------------- */ |
818 | | /* Go to next line. */ |
819 | | /* -------------------------------------------------------------------- */ |
820 | 0 | current_pos += 2; |
821 | 0 | if (current_pos == buf->data_offset) { |
822 | 0 | msSetError(MS_MISCERR, "Corrupt mime headers.", |
823 | 0 | "msIO_getAndStripStdoutBufferMimeHeaders"); |
824 | 0 | msFreeHashTable(hashTable); |
825 | 0 | return NULL; |
826 | 0 | } |
827 | | |
828 | | /* If next line is a '\r', this is the end of mime headers. */ |
829 | 0 | if (buf->data[current_pos] == '\r') { |
830 | 0 | current_pos++; |
831 | 0 | if (current_pos == buf->data_offset || buf->data[current_pos] != '\n') { |
832 | 0 | msSetError(MS_MISCERR, "Corrupt mime headers.", |
833 | 0 | "msIO_getAndStripStdoutBufferMimeHeaders"); |
834 | 0 | msFreeHashTable(hashTable); |
835 | 0 | return NULL; |
836 | 0 | } |
837 | 0 | current_pos++; |
838 | 0 | break; |
839 | 0 | } |
840 | 0 | } |
841 | | |
842 | | /* -------------------------------------------------------------------- */ |
843 | | /* Move data to front of buffer, and reset length. */ |
844 | | /* -------------------------------------------------------------------- */ |
845 | 0 | memmove(buf->data, buf->data + current_pos, buf->data_offset - current_pos); |
846 | 0 | buf->data[buf->data_offset - current_pos] = '\0'; |
847 | 0 | buf->data_offset -= current_pos; |
848 | |
|
849 | 0 | return hashTable; |
850 | 0 | } |
851 | | |
852 | | /************************************************************************/ |
853 | | /* msIO_stripStdoutBufferContentType() */ |
854 | | /* */ |
855 | | /* Strip off Content-Type header from buffer, and return to */ |
856 | | /* caller. Returned string is the callers responsibility to */ |
857 | | /* call msFree() on to deallocate. This function will return */ |
858 | | /* NULL if there is no Content-Type header. */ |
859 | | /************************************************************************/ |
860 | | |
861 | | char *msIO_stripStdoutBufferContentType() |
862 | | |
863 | 0 | { |
864 | | /* -------------------------------------------------------------------- */ |
865 | | /* Find stdout buffer. */ |
866 | | /* -------------------------------------------------------------------- */ |
867 | 0 | msIOContext *ctx = msIO_getHandler((FILE *)"stdout"); |
868 | 0 | msIOBuffer *buf; |
869 | 0 | char *content_type = NULL; |
870 | 0 | int end_of_ct, start_of_data; |
871 | |
|
872 | 0 | if (ctx == NULL || ctx->write_channel == MS_FALSE || |
873 | 0 | strcmp(ctx->label, "buffer") != 0) { |
874 | 0 | msSetError(MS_MISCERR, "Can't identify msIO buffer.", |
875 | 0 | "msIO_stripStdoutBufferContentType"); |
876 | 0 | return NULL; |
877 | 0 | } |
878 | | |
879 | 0 | buf = (msIOBuffer *)ctx->cbData; |
880 | | |
881 | | /* -------------------------------------------------------------------- */ |
882 | | /* Return NULL if we don't have a Content-Type header. */ |
883 | | /* -------------------------------------------------------------------- */ |
884 | 0 | if (buf->data_offset < 14 || |
885 | 0 | strncasecmp((const char *)buf->data, "Content-Type: ", 14) != 0) |
886 | 0 | return NULL; |
887 | | |
888 | | /* -------------------------------------------------------------------- */ |
889 | | /* Find newline marker at end of content type argument. */ |
890 | | /* -------------------------------------------------------------------- */ |
891 | 0 | end_of_ct = 13; |
892 | 0 | while (end_of_ct + 1 < buf->data_offset && buf->data[end_of_ct + 1] != '\r') |
893 | 0 | end_of_ct++; |
894 | |
|
895 | 0 | if (end_of_ct + 1 == buf->data_offset) { |
896 | 0 | msSetError(MS_MISCERR, "Corrupt Content-Type header.", |
897 | 0 | "msIO_stripStdoutBufferContentType"); |
898 | 0 | return NULL; |
899 | 0 | } |
900 | | |
901 | | /* -------------------------------------------------------------------- */ |
902 | | /* Continue on to the start of data ... */ |
903 | | /* Go to next line and skip if empty. */ |
904 | | /* -------------------------------------------------------------------- */ |
905 | 0 | start_of_data = end_of_ct + 3; |
906 | 0 | if (start_of_data < buf->data_offset && buf->data[start_of_data] == '\r') |
907 | 0 | start_of_data += 2; |
908 | |
|
909 | 0 | if (start_of_data == buf->data_offset) { |
910 | 0 | msSetError(MS_MISCERR, "Corrupt Content-Type header.", |
911 | 0 | "msIO_stripStdoutBufferContentType"); |
912 | 0 | return NULL; |
913 | 0 | } |
914 | | |
915 | | /* -------------------------------------------------------------------- */ |
916 | | /* Copy out content type. Note we go against the coding guidelines */ |
917 | | /* here and use strncpy() instead of strlcpy() as the source */ |
918 | | /* buffer may not be NULL terminated - strlcpy() requires NULL */ |
919 | | /* terminated sources (see issue #4672). */ |
920 | | /* -------------------------------------------------------------------- */ |
921 | 0 | content_type = (char *)malloc(end_of_ct - 14 + 2); |
922 | 0 | strncpy(content_type, (const char *)buf->data + 14, end_of_ct - 14 + 2); |
923 | 0 | content_type[end_of_ct - 14 + 1] = '\0'; |
924 | | |
925 | | /* -------------------------------------------------------------------- */ |
926 | | /* Move data to front of buffer, and reset length. */ |
927 | | /* -------------------------------------------------------------------- */ |
928 | 0 | memmove(buf->data, buf->data + start_of_data, |
929 | 0 | buf->data_offset - start_of_data); |
930 | 0 | buf->data[buf->data_offset - start_of_data] = '\0'; |
931 | 0 | buf->data_offset -= start_of_data; |
932 | |
|
933 | 0 | return content_type; |
934 | 0 | } |
935 | | |
936 | | /************************************************************************/ |
937 | | /* msIO_stripStdoutBufferContentHeaders() */ |
938 | | /* */ |
939 | | /* Strip off Content-* headers from buffer. */ |
940 | | /************************************************************************/ |
941 | | |
942 | 0 | void msIO_stripStdoutBufferContentHeaders() { |
943 | | /* -------------------------------------------------------------------- */ |
944 | | /* Find stdout buffer. */ |
945 | | /* -------------------------------------------------------------------- */ |
946 | 0 | msIOContext *ctx = msIO_getHandler((FILE *)"stdout"); |
947 | 0 | msIOBuffer *buf; |
948 | 0 | int start_of_data; |
949 | |
|
950 | 0 | if (ctx == NULL || ctx->write_channel == MS_FALSE || |
951 | 0 | strcmp(ctx->label, "buffer") != 0) { |
952 | 0 | msSetError(MS_MISCERR, "Can't identify msIO buffer.", |
953 | 0 | "msIO_stripStdoutBufferContentHeaders"); |
954 | 0 | return; |
955 | 0 | } |
956 | | |
957 | 0 | buf = (msIOBuffer *)ctx->cbData; |
958 | | |
959 | | /* -------------------------------------------------------------------- */ |
960 | | /* Exit if we don't have any content-* header. */ |
961 | | /* -------------------------------------------------------------------- */ |
962 | 0 | if (buf->data_offset < 8 || |
963 | 0 | strncasecmp((const char *)buf->data, "Content-", 8) != 0) |
964 | 0 | return; |
965 | | |
966 | | /* -------------------------------------------------------------------- */ |
967 | | /* Loop over all content-* headers. */ |
968 | | /* -------------------------------------------------------------------- */ |
969 | 0 | start_of_data = 0; |
970 | 0 | while (buf->data_offset > start_of_data && |
971 | 0 | strncasecmp((const char *)buf->data + start_of_data, "Content-", 8) == |
972 | 0 | 0) { |
973 | | /* -------------------------------------------------------------------- */ |
974 | | /* Find newline marker at end of content-* header argument. */ |
975 | | /* -------------------------------------------------------------------- */ |
976 | 0 | start_of_data += 7; |
977 | 0 | while (start_of_data + 1 < buf->data_offset && |
978 | 0 | buf->data[start_of_data + 1] != '\r') |
979 | 0 | start_of_data++; |
980 | |
|
981 | 0 | if (start_of_data + 1 == buf->data_offset) { |
982 | 0 | msSetError(MS_MISCERR, "Corrupt Content-* header.", |
983 | 0 | "msIO_stripStdoutBufferContentHeaders"); |
984 | 0 | return; |
985 | 0 | } |
986 | | /* -------------------------------------------------------------------- */ |
987 | | /* Go to next line. */ |
988 | | /* -------------------------------------------------------------------- */ |
989 | 0 | start_of_data += 3; |
990 | 0 | } |
991 | | |
992 | | /* -------------------------------------------------------------------- */ |
993 | | /* Continue on to the start of data ... */ |
994 | | /* Skip next line if empty. */ |
995 | | /* -------------------------------------------------------------------- */ |
996 | 0 | if (start_of_data < buf->data_offset && buf->data[start_of_data] == '\r') |
997 | 0 | start_of_data += 2; |
998 | |
|
999 | 0 | if (start_of_data == buf->data_offset) { |
1000 | 0 | msSetError(MS_MISCERR, "Corrupt Content-* header.", |
1001 | 0 | "msIO_stripStdoutBufferContentHeaders"); |
1002 | 0 | return; |
1003 | 0 | } |
1004 | | |
1005 | | /* -------------------------------------------------------------------- */ |
1006 | | /* Move data to front of buffer, and reset length. */ |
1007 | | /* -------------------------------------------------------------------- */ |
1008 | 0 | memmove(buf->data, buf->data + start_of_data, |
1009 | 0 | buf->data_offset - start_of_data); |
1010 | 0 | buf->data[buf->data_offset - start_of_data] = '\0'; |
1011 | 0 | buf->data_offset -= start_of_data; |
1012 | |
|
1013 | 0 | return; |
1014 | 0 | } |
1015 | | |
1016 | | /************************************************************************/ |
1017 | | /* msIO_bufferWrite() */ |
1018 | | /************************************************************************/ |
1019 | | |
1020 | | int msIO_bufferWrite(void *cbData, void *data, int byteCount) |
1021 | | |
1022 | 0 | { |
1023 | 0 | msIOBuffer *buf = (msIOBuffer *)cbData; |
1024 | | |
1025 | | /* |
1026 | | ** Grow buffer if needed (reserve one extra byte to put nul character) |
1027 | | */ |
1028 | 0 | if (buf->data_offset + byteCount >= buf->data_len) { |
1029 | 0 | buf->data_len = buf->data_len * 2 + byteCount + 100; |
1030 | 0 | if (buf->data == NULL) |
1031 | 0 | buf->data = (unsigned char *)malloc(buf->data_len); |
1032 | 0 | else |
1033 | 0 | buf->data = (unsigned char *)realloc(buf->data, buf->data_len); |
1034 | |
|
1035 | 0 | if (buf->data == NULL) { |
1036 | 0 | msSetError(MS_MEMERR, "Failed to allocate %d bytes for capture buffer.", |
1037 | 0 | "msIO_bufferWrite()", buf->data_len); |
1038 | 0 | buf->data_len = 0; |
1039 | 0 | return 0; |
1040 | 0 | } |
1041 | 0 | } |
1042 | | |
1043 | | /* |
1044 | | ** Copy result into buffer. |
1045 | | */ |
1046 | | |
1047 | 0 | memcpy(buf->data + buf->data_offset, data, byteCount); |
1048 | 0 | buf->data_offset += byteCount; |
1049 | 0 | buf->data[buf->data_offset] = '\0'; |
1050 | |
|
1051 | 0 | return byteCount; |
1052 | 0 | } |
1053 | | |
1054 | | /************************************************************************/ |
1055 | | /* msIO_bufferRead() */ |
1056 | | /************************************************************************/ |
1057 | | |
1058 | | int msIO_bufferRead(void *cbData, void *data, int byteCount) |
1059 | | |
1060 | 0 | { |
1061 | 0 | (void)cbData; |
1062 | 0 | (void)data; |
1063 | 0 | (void)byteCount; |
1064 | | /* not implemented yet. */ |
1065 | 0 | return 0; |
1066 | 0 | } |