Coverage Report

Created: 2025-06-13 06:29

/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
}