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