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