/src/MapServer/src/maperror.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /******************************************************************************  | 
2  |  |  * $Id$  | 
3  |  |  *  | 
4  |  |  * Project:  MapServer  | 
5  |  |  * Purpose:  Implementation of msSetError(), msDebug() and related functions.  | 
6  |  |  * Author:   Steve Lime and the MapServer team.  | 
7  |  |  *  | 
8  |  |  ******************************************************************************  | 
9  |  |  * Copyright (c) 1996-2005 Regents of the University of Minnesota.  | 
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 "mapserver.h"  | 
31  |  | #include "maperror.h"  | 
32  |  | #include "mapthread.h"  | 
33  |  | #include "maptime.h"  | 
34  |  |  | 
35  |  | #include <time.h>  | 
36  |  | #ifndef _WIN32  | 
37  |  | #include <sys/time.h>  | 
38  |  | #include <unistd.h>  | 
39  |  | #endif  | 
40  |  | #include <stdarg.h>  | 
41  |  |  | 
42  |  | #include "cpl_conv.h"  | 
43  |  | #include "cpl_string.h"  | 
44  |  |  | 
45  |  | static char *const ms_errorCodes[MS_NUMERRORCODES] = { | 
46  |  |     "",  | 
47  |  |     "Unable to access file.",  | 
48  |  |     "Memory allocation error.",  | 
49  |  |     "Incorrect data type.",  | 
50  |  |     "Symbol definition error.",  | 
51  |  |     "Regular expression error.",  | 
52  |  |     "TrueType Font error.",  | 
53  |  |     "DBASE file error.",  | 
54  |  |     "GD library error.",  | 
55  |  |     "Unknown identifier.",  | 
56  |  |     "Premature End-of-File.",  | 
57  |  |     "Projection library error.",  | 
58  |  |     "General error message.",  | 
59  |  |     "CGI error.",  | 
60  |  |     "Web application error.",  | 
61  |  |     "Image handling error.",  | 
62  |  |     "Hash table error.",  | 
63  |  |     "Join error.",  | 
64  |  |     "Search returned no results.",  | 
65  |  |     "Shapefile error.",  | 
66  |  |     "Expression parser error.",  | 
67  |  |     "SDE error.",  | 
68  |  |     "OGR error.",  | 
69  |  |     "Query error.",  | 
70  |  |     "WMS server error.",  | 
71  |  |     "WMS connection error.",  | 
72  |  |     "OracleSpatial error.",  | 
73  |  |     "WFS server error.",  | 
74  |  |     "WFS connection error.",  | 
75  |  |     "WMS Map Context error.",  | 
76  |  |     "HTTP request error.",  | 
77  |  |     "Child array error.",  | 
78  |  |     "WCS server error.",  | 
79  |  |     "GEOS library error.",  | 
80  |  |     "Invalid rectangle.",  | 
81  |  |     "Date/time error.",  | 
82  |  |     "GML encoding error.",  | 
83  |  |     "SOS server error.",  | 
84  |  |     "NULL parent pointer error.",  | 
85  |  |     "AGG library error.",  | 
86  |  |     "OWS error.",  | 
87  |  |     "OpenGL renderer error.",  | 
88  |  |     "Renderer error.",  | 
89  |  |     "V8 engine error.",  | 
90  |  |     "OCG API error.",  | 
91  |  |     "Flatgeobuf error."};  | 
92  |  |  | 
93  |  | #ifndef USE_THREAD  | 
94  |  |  | 
95  |  | // Get the MapServer error object  | 
96  | 58.7k  | errorObj *msGetErrorObj() { | 
97  | 58.7k  |   static errorObj ms_error = {MS_NOERR, "", "", "", MS_FALSE, 0, NULL, 0}; | 
98  | 58.7k  |   return &ms_error;  | 
99  | 58.7k  | }  | 
100  |  | #endif  | 
101  |  |  | 
102  |  | #ifdef USE_THREAD  | 
103  |  |  | 
104  |  | typedef struct te_info { | 
105  |  |   struct te_info *next;  | 
106  |  |   void *thread_id;  | 
107  |  |   errorObj ms_error;  | 
108  |  | } te_info_t;  | 
109  |  |  | 
110  |  | static te_info_t *error_list = NULL;  | 
111  |  |  | 
112  |  | errorObj *msGetErrorObj() { | 
113  |  |   te_info_t *link;  | 
114  |  |   void *thread_id;  | 
115  |  |   errorObj *ret_obj;  | 
116  |  |  | 
117  |  |   msAcquireLock(TLOCK_ERROROBJ);  | 
118  |  |  | 
119  |  |   thread_id = msGetThreadId();  | 
120  |  |  | 
121  |  |   /* find link for this thread */  | 
122  |  |  | 
123  |  |   for (link = error_list;  | 
124  |  |        link != NULL && link->thread_id != thread_id && link->next != NULL &&  | 
125  |  |        link->next->thread_id != thread_id;  | 
126  |  |        link = link->next) { | 
127  |  |   }  | 
128  |  |  | 
129  |  |   /* If the target thread link is already at the head of the list were ok */  | 
130  |  |   if (error_list != NULL && error_list->thread_id == thread_id) { | 
131  |  |   }  | 
132  |  |  | 
133  |  |   /* We don't have one ... initialize one. */  | 
134  |  |   else if (link == NULL || link->next == NULL) { | 
135  |  |     te_info_t *new_link;  | 
136  |  |     errorObj error_obj = {MS_NOERR, "", "", "", 0, 0, NULL, 0}; | 
137  |  |  | 
138  |  |     new_link = (te_info_t *)malloc(sizeof(te_info_t));  | 
139  |  |     new_link->next = error_list;  | 
140  |  |     new_link->thread_id = thread_id;  | 
141  |  |     new_link->ms_error = error_obj;  | 
142  |  |  | 
143  |  |     error_list = new_link;  | 
144  |  |   }  | 
145  |  |  | 
146  |  |   /* If the link is not already at the head of the list, promote it */  | 
147  |  |   else { | 
148  |  |     te_info_t *target = link->next;  | 
149  |  |  | 
150  |  |     link->next = link->next->next;  | 
151  |  |     target->next = error_list;  | 
152  |  |     error_list = target;  | 
153  |  |   }  | 
154  |  |  | 
155  |  |   ret_obj = &(error_list->ms_error);  | 
156  |  |  | 
157  |  |   msReleaseLock(TLOCK_ERROROBJ);  | 
158  |  |  | 
159  |  |   return ret_obj;  | 
160  |  | }  | 
161  |  | #endif  | 
162  |  |  | 
163  |  | /* msInsertErrorObj()  | 
164  |  | **  | 
165  |  | ** We maintain a chained list of errorObj in which the first errorObj is  | 
166  |  | ** the most recent (i.e. a stack).  msErrorReset() should be used to clear  | 
167  |  | ** the list.  | 
168  |  | **  | 
169  |  | ** Note that since some code in MapServer will fetch the head of the list and  | 
170  |  | ** keep a handle on it for a while, the head of the chained list is static  | 
171  |  | ** and never changes.  | 
172  |  | ** A new errorObj is always inserted after the head, and only if the  | 
173  |  | ** head of the list already contains some information.  i.e. If the static  | 
174  |  | ** errorObj at the head of the list is empty then it is returned directly,  | 
175  |  | ** otherwise a new object is inserted after the head and the data that was in  | 
176  |  | ** the head is moved to the new errorObj, freeing the head errorObj to receive  | 
177  |  | ** the new error information.  | 
178  |  | */  | 
179  | 20.7k  | static errorObj *msInsertErrorObj(void) { | 
180  | 20.7k  |   errorObj *ms_error;  | 
181  | 20.7k  |   ms_error = msGetErrorObj();  | 
182  |  |  | 
183  | 20.7k  |   if (ms_error->code != MS_NOERR && ms_error->totalerrorcount < 100) { | 
184  |  |     /* Head of the list already in use, insert a new errorObj after the head  | 
185  |  |      * and move head contents to this new errorObj, freeing the errorObj  | 
186  |  |      * for reuse.  | 
187  |  |      */  | 
188  | 4.70k  |     errorObj *new_error;  | 
189  | 4.70k  |     new_error = (errorObj *)malloc(sizeof(errorObj));  | 
190  |  |  | 
191  |  |     /* Note: if malloc() failed then we simply do nothing and the head will  | 
192  |  |      * be overwritten by the caller... we cannot produce an error here  | 
193  |  |      * since we are already inside a msSetError() call.  | 
194  |  |      */  | 
195  | 4.70k  |     if (new_error) { | 
196  | 4.70k  |       new_error->next = ms_error->next;  | 
197  | 4.70k  |       new_error->code = ms_error->code;  | 
198  | 4.70k  |       new_error->isreported = ms_error->isreported;  | 
199  | 4.70k  |       strlcpy(new_error->routine, ms_error->routine,  | 
200  | 4.70k  |               sizeof(new_error->routine));  | 
201  | 4.70k  |       strlcpy(new_error->message, ms_error->message,  | 
202  | 4.70k  |               sizeof(new_error->message));  | 
203  | 4.70k  |       new_error->errorcount = ms_error->errorcount;  | 
204  |  |  | 
205  | 4.70k  |       ms_error->next = new_error;  | 
206  | 4.70k  |       ms_error->code = MS_NOERR;  | 
207  | 4.70k  |       ms_error->isreported = MS_FALSE;  | 
208  | 4.70k  |       ms_error->routine[0] = '\0';  | 
209  | 4.70k  |       ms_error->message[0] = '\0';  | 
210  | 4.70k  |       ms_error->errorcount = 0;  | 
211  | 4.70k  |     }  | 
212  | 4.70k  |     ms_error->totalerrorcount++;  | 
213  | 4.70k  |   }  | 
214  |  |  | 
215  | 20.7k  |   return ms_error;  | 
216  | 20.7k  | }  | 
217  |  |  | 
218  |  | /* msResetErrorList()  | 
219  |  | **  | 
220  |  | ** Clear the list of error objects.  | 
221  |  | */  | 
222  | 16.3k  | void msResetErrorList() { | 
223  | 16.3k  |   errorObj *ms_error, *this_error;  | 
224  | 16.3k  |   ms_error = msGetErrorObj();  | 
225  |  |  | 
226  | 16.3k  |   this_error = ms_error->next;  | 
227  | 21.0k  |   while (this_error != NULL) { | 
228  | 4.70k  |     errorObj *next_error;  | 
229  |  |  | 
230  | 4.70k  |     next_error = this_error->next;  | 
231  | 4.70k  |     msFree(this_error);  | 
232  | 4.70k  |     this_error = next_error;  | 
233  | 4.70k  |   }  | 
234  |  |  | 
235  | 16.3k  |   ms_error->next = NULL;  | 
236  | 16.3k  |   ms_error->code = MS_NOERR;  | 
237  | 16.3k  |   ms_error->isreported = MS_FALSE;  | 
238  | 16.3k  |   ms_error->routine[0] = '\0';  | 
239  | 16.3k  |   ms_error->message[0] = '\0';  | 
240  | 16.3k  |   ms_error->errorcount = 0;  | 
241  | 16.3k  |   ms_error->totalerrorcount = 0;  | 
242  |  |  | 
243  |  |   /* -------------------------------------------------------------------- */  | 
244  |  |   /*      Cleanup our entry in the thread list.  This is mainly           */  | 
245  |  |   /*      imprortant when msCleanup() calls msResetErrorList().           */  | 
246  |  |   /* -------------------------------------------------------------------- */  | 
247  |  | #ifdef USE_THREAD  | 
248  |  |   { | 
249  |  |     void *thread_id = msGetThreadId();  | 
250  |  |     te_info_t *link;  | 
251  |  |  | 
252  |  |     msAcquireLock(TLOCK_ERROROBJ);  | 
253  |  |  | 
254  |  |     /* find link for this thread */  | 
255  |  |  | 
256  |  |     for (link = error_list;  | 
257  |  |          link != NULL && link->thread_id != thread_id && link->next != NULL &&  | 
258  |  |          link->next->thread_id != thread_id;  | 
259  |  |          link = link->next) { | 
260  |  |     }  | 
261  |  |  | 
262  |  |     if (link->thread_id == thread_id) { | 
263  |  |       /* presumably link is at head of list.  */  | 
264  |  |       if (error_list == link)  | 
265  |  |         error_list = link->next;  | 
266  |  |  | 
267  |  |       free(link);  | 
268  |  |     } else if (link->next != NULL && link->next->thread_id == thread_id) { | 
269  |  |       te_info_t *next_link = link->next;  | 
270  |  |       link->next = link->next->next;  | 
271  |  |       free(next_link);  | 
272  |  |     }  | 
273  |  |     msReleaseLock(TLOCK_ERROROBJ);  | 
274  |  |   }  | 
275  |  | #endif  | 
276  | 16.3k  | }  | 
277  |  |  | 
278  | 0  | char *msGetErrorCodeString(int code) { | 
279  |  | 
  | 
280  | 0  |   if (code < 0 || code > MS_NUMERRORCODES - 1)  | 
281  | 0  |     return ("Invalid error code."); | 
282  |  |  | 
283  | 0  |   return (ms_errorCodes[code]);  | 
284  | 0  | }  | 
285  |  |  | 
286  |  | /* -------------------------------------------------------------------- */  | 
287  |  | /*      Adding the displayable error string to a given string           */  | 
288  |  | /*      and reallocates the memory enough to hold the characters.       */  | 
289  |  | /*      If source is null returns a newly allocated string              */  | 
290  |  | /* -------------------------------------------------------------------- */  | 
291  | 0  | char *msAddErrorDisplayString(char *source, errorObj *error) { | 
292  | 0  |   if ((source = msStringConcatenate(source, error->routine)) == NULL)  | 
293  | 0  |     return (NULL);  | 
294  | 0  |   if ((source = msStringConcatenate(source, ": ")) == NULL)  | 
295  | 0  |     return (NULL);  | 
296  | 0  |   if ((source = msStringConcatenate(source, ms_errorCodes[error->code])) ==  | 
297  | 0  |       NULL)  | 
298  | 0  |     return (NULL);  | 
299  | 0  |   if ((source = msStringConcatenate(source, " ")) == NULL)  | 
300  | 0  |     return (NULL);  | 
301  | 0  |   if ((source = msStringConcatenate(source, error->message)) == NULL)  | 
302  | 0  |     return (NULL);  | 
303  | 0  |   if (error->errorcount > 0) { | 
304  | 0  |     char *pszTmp;  | 
305  | 0  |     if ((source = msStringConcatenate(source, " (message repeated ")) == NULL)  | 
306  | 0  |       return (NULL);  | 
307  | 0  |     pszTmp = msIntToString(error->errorcount);  | 
308  | 0  |     if ((source = msStringConcatenate(source, pszTmp)) == NULL) { | 
309  | 0  |       msFree(pszTmp);  | 
310  | 0  |       return (NULL);  | 
311  | 0  |     }  | 
312  | 0  |     msFree(pszTmp);  | 
313  | 0  |     if ((source = msStringConcatenate(source, " times)")) == NULL)  | 
314  | 0  |       return (NULL);  | 
315  | 0  |   }  | 
316  |  |  | 
317  | 0  |   return source;  | 
318  | 0  | }  | 
319  |  |  | 
320  | 0  | char *msGetErrorString(const char *delimiter) { | 
321  | 0  |   char *errstr = NULL;  | 
322  |  | 
  | 
323  | 0  |   errorObj *error = msGetErrorObj();  | 
324  |  | 
  | 
325  | 0  |   if (!delimiter || !error)  | 
326  | 0  |     return (NULL);  | 
327  |  |  | 
328  | 0  |   while (error && error->code != MS_NOERR) { | 
329  | 0  |     if ((errstr = msAddErrorDisplayString(errstr, error)) == NULL)  | 
330  | 0  |       return (NULL);  | 
331  |  |  | 
332  | 0  |     if (error->next &&  | 
333  | 0  |         error->next->code !=  | 
334  | 0  |             MS_NOERR) { /* (peek ahead) more errors, use delimiter */ | 
335  | 0  |       if ((errstr = msStringConcatenate(errstr, delimiter)) == NULL)  | 
336  | 0  |         return (NULL);  | 
337  | 0  |     }  | 
338  | 0  |     error = error->next;  | 
339  | 0  |   }  | 
340  |  |  | 
341  | 0  |   return (errstr);  | 
342  | 0  | }  | 
343  |  |  | 
344  | 81.4k  | static void msRedactString(char *str, const char *keyword) { | 
345  |  |  | 
346  | 81.4k  |   char *password = strstr(str, keyword);  | 
347  | 81.4k  |   if (password != NULL) { | 
348  | 1.41k  |     const char chOptionDelimiter = password - str > 0 ? password[-1] : 0;  | 
349  | 1.41k  |     char *ptr = password + strlen(keyword);  | 
350  | 1.41k  |     char chStringSep = *ptr;  | 
351  | 1.41k  |     if (chStringSep == '\'' || chStringSep == '"') { | 
352  | 473  |       ++ptr;  | 
353  | 937  |     } else if (chOptionDelimiter == ';' && chStringSep == '{' && | 
354  | 937  |                strcmp(keyword, "pwd=") == 0) { | 
355  |  |       // Handle cases like "\\SQL2019;DATABASE=msautotest;Driver={ODBC Driver 17 | 
356  |  |       // for SQL Server};pwd={Password;12!};uid=sa;" | 
357  | 5  |       ++ptr;  | 
358  | 5  |       chStringSep = '}';  | 
359  | 932  |     } else { | 
360  | 932  |       chStringSep = '\0';  | 
361  | 932  |     }  | 
362  |  |     /* Replace all characters from after equal sign to end of line, end of  | 
363  |  |      * string, or end of quoted string.  | 
364  |  |      */  | 
365  | 1.41k  |     char *ptr_first_redacted_char = NULL;  | 
366  | 25.5k  |     while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') { | 
367  | 24.8k  |       if (chStringSep == '\0') { | 
368  | 15.2k  |         if (*ptr == chOptionDelimiter)  | 
369  | 696  |           break;  | 
370  | 15.2k  |       } else { | 
371  | 9.63k  |         if (*ptr == chStringSep) { | 
372  | 66  |           break;  | 
373  | 66  |         }  | 
374  | 9.56k  |         if (*ptr == '\\' && ptr[1] == chStringSep) { | 
375  | 4  |           ptr++;  | 
376  | 4  |         }  | 
377  | 9.56k  |       }  | 
378  | 24.0k  |       if (!ptr_first_redacted_char) { | 
379  | 1.04k  |         ptr_first_redacted_char = ptr;  | 
380  | 1.04k  |         *ptr = '*';  | 
381  | 1.04k  |       }  | 
382  | 24.0k  |       ptr++;  | 
383  | 24.0k  |     }  | 
384  | 1.41k  |     if (ptr_first_redacted_char) { | 
385  | 1.04k  |       memmove(ptr_first_redacted_char + 1, ptr, strlen(ptr) + 1);  | 
386  | 1.04k  |     }  | 
387  | 1.41k  |   }  | 
388  | 81.4k  | }  | 
389  |  |  | 
390  | 40.7k  | void msRedactCredentials(char *str) { | 
391  |  |  | 
392  |  |   // postgres or mssql formats  | 
393  | 40.7k  |   msRedactString(str, "password=");  | 
394  |  |   // ODBC connections can use pwd rather than password  | 
395  | 40.7k  |   msRedactString(str, "pwd=");  | 
396  | 40.7k  | }  | 
397  |  |  | 
398  |  | static void msSetErrorInternal(int ms_errcode, const char *http_status,  | 
399  | 21.6k  |                                const char *message, const char *routine) { | 
400  |  |  | 
401  | 21.6k  |   errorObj *ms_error = msGetErrorObj();  | 
402  |  |  | 
403  |  |   /* Insert the error to the list if it is not the same as the previous error*/  | 
404  | 21.6k  |   if (ms_error->code != ms_errcode || !EQUAL(message, ms_error->message) ||  | 
405  | 21.6k  |       !EQUAL(routine, ms_error->routine)) { | 
406  | 20.7k  |     ms_error = msInsertErrorObj();  | 
407  | 20.7k  |     if (!routine)  | 
408  | 0  |       strcpy(ms_error->routine, "");  | 
409  | 20.7k  |     else { | 
410  | 20.7k  |       strlcpy(ms_error->routine, routine, sizeof(ms_error->routine));  | 
411  | 20.7k  |     }  | 
412  |  |  | 
413  | 20.7k  |     if (!http_status)  | 
414  | 20.7k  |       strcpy(ms_error->http_status, "");  | 
415  | 0  |     else { | 
416  | 0  |       strlcpy(ms_error->http_status, http_status,  | 
417  | 0  |               sizeof(ms_error->http_status));  | 
418  | 0  |     }  | 
419  |  |  | 
420  | 20.7k  |     strlcpy(ms_error->message, message, sizeof(ms_error->message));  | 
421  | 20.7k  |     ms_error->code = ms_errcode;  | 
422  | 20.7k  |     ms_error->errorcount = 0;  | 
423  | 20.7k  |   } else  | 
424  | 851  |     ++ms_error->errorcount;  | 
425  |  |  | 
426  | 21.6k  |   msRedactCredentials(ms_error->message);  | 
427  |  |  | 
428  |  |   /* Log a copy of errors to MS_ERRORFILE if set (handled automatically inside  | 
429  |  |    * msDebug()) */  | 
430  | 21.6k  |   msDebug("%s: %s %s\n", ms_error->routine, ms_errorCodes[ms_error->code], | 
431  | 21.6k  |           ms_error->message);  | 
432  | 21.6k  | }  | 
433  |  |  | 
434  | 21.6k  | void msSetError(int code, const char *message_fmt, const char *routine, ...) { | 
435  | 21.6k  |   va_list args;  | 
436  | 21.6k  |   char message[MESSAGELENGTH];  | 
437  |  |  | 
438  | 21.6k  |   if (!message_fmt)  | 
439  | 1.48k  |     strcpy(message, "");  | 
440  | 20.1k  |   else { | 
441  | 20.1k  |     va_start(args, routine);  | 
442  | 20.1k  |     vsnprintf(message, MESSAGELENGTH, message_fmt, args);  | 
443  | 20.1k  |     va_end(args);  | 
444  | 20.1k  |   }  | 
445  | 21.6k  |   msSetErrorInternal(code, NULL, message, routine);  | 
446  | 21.6k  | }  | 
447  |  |  | 
448  |  | #ifdef _MSC_VER  | 
449  |  | __declspec(thread) int gIsWMS = MS_FALSE;  | 
450  |  | #else  | 
451  |  | static _Thread_local int gIsWMS = MS_FALSE;  | 
452  |  | #endif  | 
453  |  |  | 
454  | 0  | void msSetErrorSetIsWMS(int is_wms) { gIsWMS = is_wms; } | 
455  |  |  | 
456  |  | void msSetErrorWithStatus(int ms_errcode, const char *http_status,  | 
457  | 0  |                           const char *message_fmt, const char *routine, ...) { | 
458  | 0  |   va_list args;  | 
459  | 0  |   char message[MESSAGELENGTH];  | 
460  |  | 
  | 
461  | 0  |   if (!message_fmt)  | 
462  | 0  |     strcpy(message, "");  | 
463  | 0  |   else { | 
464  | 0  |     va_start(args, routine);  | 
465  | 0  |     vsnprintf(message, MESSAGELENGTH, message_fmt, args);  | 
466  | 0  |     va_end(args);  | 
467  | 0  |   }  | 
468  | 0  |   if (http_status) { | 
469  | 0  |     if (gIsWMS) { | 
470  | 0  |       if (!CPLTestBoolean(  | 
471  | 0  |               CPLGetConfigOption("MS_WMS_ERROR_STATUS_CODE", "OFF"))) | 
472  | 0  |         http_status = NULL;  | 
473  | 0  |     } else { | 
474  | 0  |       http_status = NULL;  | 
475  | 0  |     }  | 
476  | 0  |   }  | 
477  | 0  |   msSetErrorInternal(ms_errcode, http_status, message, routine);  | 
478  | 0  | }  | 
479  |  |  | 
480  | 0  | void msWriteError(FILE *stream) { | 
481  | 0  |   errorObj *ms_error = msGetErrorObj();  | 
482  |  | 
  | 
483  | 0  |   while (ms_error && ms_error->code != MS_NOERR) { | 
484  | 0  |     msIO_fprintf(stream, "%s: %s %s <br>\n", ms_error->routine,  | 
485  | 0  |                  ms_errorCodes[ms_error->code], ms_error->message);  | 
486  | 0  |     ms_error->isreported = MS_TRUE;  | 
487  | 0  |     ms_error = ms_error->next;  | 
488  | 0  |   }  | 
489  | 0  | }  | 
490  |  |  | 
491  | 0  | void msWriteErrorXML(FILE *stream) { | 
492  | 0  |   char *message;  | 
493  | 0  |   errorObj *ms_error = msGetErrorObj();  | 
494  |  | 
  | 
495  | 0  |   while (ms_error && ms_error->code != MS_NOERR) { | 
496  | 0  |     message = msEncodeHTMLEntities(ms_error->message);  | 
497  |  | 
  | 
498  | 0  |     msIO_fprintf(stream, "%s: %s %s\n", ms_error->routine,  | 
499  | 0  |                  ms_errorCodes[ms_error->code], message);  | 
500  | 0  |     ms_error->isreported = MS_TRUE;  | 
501  | 0  |     ms_error = ms_error->next;  | 
502  |  | 
  | 
503  | 0  |     msFree(message);  | 
504  | 0  |   }  | 
505  | 0  | }  | 
506  |  |  | 
507  | 0  | void msWriteErrorImage(mapObj *map, char *filename, int blank) { | 
508  | 0  |   imageObj *img;  | 
509  | 0  |   int width = 400, height = 300;  | 
510  | 0  |   const int nMargin = 5;  | 
511  |  | 
  | 
512  | 0  |   char **papszLines = NULL;  | 
513  | 0  |   pointObj pnt = {0}; | 
514  | 0  |   outputFormatObj *format = NULL;  | 
515  | 0  |   char *errormsg = msGetErrorString("; "); | 
516  | 0  |   errorObj *error = msGetErrorObj();  | 
517  | 0  |   char *imagepath = NULL, *imageurl = NULL;  | 
518  | 0  |   colorObj imagecolor, *imagecolorptr = NULL;  | 
519  | 0  |   textSymbolObj ts;  | 
520  | 0  |   labelObj label;  | 
521  | 0  |   int charWidth = 5,  | 
522  | 0  |       charHeight = 8; /* hardcoded, should be looked up from ft face */  | 
523  | 0  |   if (!errormsg) { | 
524  | 0  |     errormsg = msStrdup("No error found sorry. This is likely a bug"); | 
525  | 0  |   }  | 
526  |  | 
  | 
527  | 0  |   if (map) { | 
528  | 0  |     if (map->width > 0 && map->height > 0) { | 
529  | 0  |       width = map->width;  | 
530  | 0  |       height = map->height;  | 
531  | 0  |     }  | 
532  | 0  |     format = map->outputformat;  | 
533  | 0  |     imagepath = map->web.imagepath;  | 
534  | 0  |     imageurl = map->web.imageurl;  | 
535  | 0  |   }  | 
536  |  |  | 
537  |  |   /* Default to GIF if no suitable GD output format set */  | 
538  | 0  |   if (format == NULL || !MS_RENDERER_PLUGIN(format))  | 
539  | 0  |     format = msCreateDefaultOutputFormat(NULL, "AGG/PNG8", "png", NULL);  | 
540  |  | 
  | 
541  | 0  |   if (!format->transparent) { | 
542  | 0  |     if (map && MS_VALID_COLOR(map->imagecolor)) { | 
543  | 0  |       imagecolorptr = &map->imagecolor;  | 
544  | 0  |     } else { | 
545  | 0  |       MS_INIT_COLOR(imagecolor, 255, 255, 255, 255);  | 
546  | 0  |       imagecolorptr = &imagecolor;  | 
547  | 0  |     }  | 
548  | 0  |   }  | 
549  |  | 
  | 
550  | 0  |   img = msImageCreate(width, height, format, imagepath, imageurl,  | 
551  | 0  |                       MS_DEFAULT_RESOLUTION, MS_DEFAULT_RESOLUTION,  | 
552  | 0  |                       imagecolorptr);  | 
553  |  | 
  | 
554  | 0  |   const int nTextLength = strlen(errormsg);  | 
555  | 0  |   const int nWidthTxt = nTextLength * charWidth;  | 
556  | 0  |   const int nUsableWidth = width - (nMargin * 2);  | 
557  |  |  | 
558  |  |   /* Check to see if it all fits on one line. If not, split the text on several  | 
559  |  |    * lines. */  | 
560  | 0  |   if (!blank) { | 
561  | 0  |     int nLines;  | 
562  | 0  |     if (nWidthTxt > nUsableWidth) { | 
563  | 0  |       const int nMaxCharsPerLine = nUsableWidth / charWidth;  | 
564  | 0  |       nLines = (int)ceil((double)nTextLength / (double)nMaxCharsPerLine);  | 
565  | 0  |       if (nLines > 0) { | 
566  | 0  |         papszLines = (char **)malloc(nLines * sizeof(char *));  | 
567  | 0  |         for (int i = 0; i < nLines; i++) { | 
568  | 0  |           papszLines[i] = (char *)malloc((nMaxCharsPerLine + 1) * sizeof(char));  | 
569  | 0  |           papszLines[i][0] = '\0';  | 
570  | 0  |         }  | 
571  | 0  |       }  | 
572  | 0  |       for (int i = 0; i < nLines; i++) { | 
573  | 0  |         const int nStart = i * nMaxCharsPerLine;  | 
574  | 0  |         int nEnd = nStart + nMaxCharsPerLine;  | 
575  | 0  |         if (nStart < nTextLength) { | 
576  | 0  |           if (nEnd > nTextLength)  | 
577  | 0  |             nEnd = nTextLength;  | 
578  | 0  |           const int nLength = nEnd - nStart;  | 
579  |  | 
  | 
580  | 0  |           strncpy(papszLines[i], errormsg + nStart, nLength);  | 
581  | 0  |           papszLines[i][nLength] = '\0';  | 
582  | 0  |         }  | 
583  | 0  |       }  | 
584  | 0  |     } else { | 
585  | 0  |       nLines = 1;  | 
586  | 0  |       papszLines = (char **)malloc(nLines * sizeof(char *));  | 
587  | 0  |       papszLines[0] = msStrdup(errormsg);  | 
588  | 0  |     }  | 
589  | 0  |     initLabel(&label);  | 
590  | 0  |     MS_INIT_COLOR(label.color, 0, 0, 0, 255);  | 
591  | 0  |     MS_INIT_COLOR(label.outlinecolor, 255, 255, 255, 255);  | 
592  | 0  |     label.outlinewidth = 1;  | 
593  |  | 
  | 
594  | 0  |     label.size = MS_SMALL;  | 
595  | 0  |     MS_REFCNT_INCR((&label));  | 
596  | 0  |     for (int i = 0; i < nLines; i++) { | 
597  | 0  |       pnt.y = charHeight * ((i * 2) + 1);  | 
598  | 0  |       pnt.x = charWidth;  | 
599  | 0  |       initTextSymbol(&ts);  | 
600  | 0  |       msPopulateTextSymbolForLabelAndString(&ts, &label, papszLines[i], 1, 1,  | 
601  | 0  |                                             0);  | 
602  | 0  |       if (MS_LIKELY(MS_SUCCESS == msComputeTextPath(map, &ts))) { | 
603  | 0  |         if (MS_SUCCESS != msDrawTextSymbol(NULL, img, pnt, &ts)) { | 
604  |  |           /* an error occurred, but there's nothing much we can do about it here  | 
605  |  |            * as we are already handling an error condition */  | 
606  | 0  |         }  | 
607  | 0  |         freeTextSymbol(&ts);  | 
608  | 0  |       }  | 
609  | 0  |     }  | 
610  | 0  |     if (papszLines) { | 
611  | 0  |       free(papszLines);  | 
612  | 0  |     }  | 
613  | 0  |   }  | 
614  |  |  | 
615  |  |   /* actually write the image */  | 
616  | 0  |   if (!filename) { | 
617  | 0  |     msIO_setHeader("Content-Type", "%s", MS_IMAGE_MIME_TYPE(format)); | 
618  | 0  |     msIO_sendHeaders();  | 
619  | 0  |   }  | 
620  | 0  |   msSaveImage(NULL, img, filename);  | 
621  | 0  |   msFreeImage(img);  | 
622  |  |  | 
623  |  |   /* the errors are reported */  | 
624  | 0  |   while (error && error->code != MS_NOERR) { | 
625  | 0  |     error->isreported = MS_TRUE;  | 
626  | 0  |     error = error->next;  | 
627  | 0  |   }  | 
628  |  | 
  | 
629  | 0  |   if (format->refcount == 0)  | 
630  | 0  |     msFreeOutputFormat(format);  | 
631  | 0  |   msFree(errormsg);  | 
632  | 0  | }  | 
633  |  |  | 
634  | 0  | char *msGetVersion() { | 
635  | 0  |   static char version[2048];  | 
636  |  | 
  | 
637  | 0  |   sprintf(version, "MapServer version %s", MS_VERSION);  | 
638  |  |  | 
639  |  |   // add versions of required dependencies  | 
640  | 0  |   static char PROJVersion[20];  | 
641  | 0  |   sprintf(PROJVersion, " PROJ version %d.%d", PROJ_VERSION_MAJOR,  | 
642  | 0  |           PROJ_VERSION_MINOR);  | 
643  | 0  |   strcat(version, PROJVersion);  | 
644  |  | 
  | 
645  | 0  |   static char GDALVersion[20];  | 
646  | 0  |   sprintf(GDALVersion, " GDAL version %d.%d", GDAL_VERSION_MAJOR,  | 
647  | 0  |           GDAL_VERSION_MINOR);  | 
648  | 0  |   strcat(version, GDALVersion);  | 
649  |  | 
  | 
650  | 0  | #if (defined USE_PNG)  | 
651  | 0  |   strcat(version, " OUTPUT=PNG");  | 
652  | 0  | #endif  | 
653  | 0  | #if (defined USE_JPEG)  | 
654  | 0  |   strcat(version, " OUTPUT=JPEG");  | 
655  | 0  | #endif  | 
656  |  | #ifdef USE_KML  | 
657  |  |   strcat(version, " OUTPUT=KML");  | 
658  |  | #endif  | 
659  | 0  |   strcat(version, " SUPPORTS=PROJ");  | 
660  | 0  |   strcat(version, " SUPPORTS=AGG");  | 
661  | 0  |   strcat(version, " SUPPORTS=FREETYPE");  | 
662  |  | #ifdef USE_CAIRO  | 
663  |  |   strcat(version, " SUPPORTS=CAIRO");  | 
664  |  | #endif  | 
665  |  | #if defined(USE_SVG_CAIRO) || defined(USE_RSVG)  | 
666  |  |   strcat(version, " SUPPORTS=SVG_SYMBOLS");  | 
667  |  | #ifdef USE_SVG_CAIRO  | 
668  |  |   strcat(version, " SUPPORTS=SVGCAIRO");  | 
669  |  | #else  | 
670  |  |   strcat(version, " SUPPORTS=RSVG");  | 
671  |  | #endif  | 
672  |  | #endif  | 
673  |  | #ifdef USE_OGL  | 
674  |  |   strcat(version, " SUPPORTS=OPENGL");  | 
675  |  | #endif  | 
676  | 0  | #ifdef USE_ICONV  | 
677  | 0  |   strcat(version, " SUPPORTS=ICONV");  | 
678  | 0  | #endif  | 
679  |  | #ifdef USE_EXEMPI  | 
680  |  |   strcat(version, " SUPPORTS=XMP");  | 
681  |  | #endif  | 
682  |  | #ifdef USE_FRIBIDI  | 
683  |  |   strcat(version, " SUPPORTS=FRIBIDI");  | 
684  |  | #endif  | 
685  | 0  | #ifdef USE_WMS_SVR  | 
686  | 0  |   strcat(version, " SUPPORTS=WMS_SERVER");  | 
687  | 0  | #endif  | 
688  |  | #ifdef USE_WMS_LYR  | 
689  |  |   strcat(version, " SUPPORTS=WMS_CLIENT");  | 
690  |  | #endif  | 
691  | 0  | #ifdef USE_WFS_SVR  | 
692  | 0  |   strcat(version, " SUPPORTS=WFS_SERVER");  | 
693  | 0  | #endif  | 
694  |  | #ifdef USE_WFS_LYR  | 
695  |  |   strcat(version, " SUPPORTS=WFS_CLIENT");  | 
696  |  | #endif  | 
697  | 0  | #ifdef USE_WCS_SVR  | 
698  | 0  |   strcat(version, " SUPPORTS=WCS_SERVER");  | 
699  | 0  | #endif  | 
700  |  | #ifdef USE_SOS_SVR  | 
701  |  |   strcat(version, " SUPPORTS=SOS_SERVER");  | 
702  |  | #endif  | 
703  | 0  | #ifdef USE_OGCAPI_SVR  | 
704  | 0  |   strcat(version, " SUPPORTS=OGCAPI_SERVER");  | 
705  | 0  | #endif  | 
706  |  | #ifdef USE_FASTCGI  | 
707  |  |   strcat(version, " SUPPORTS=FASTCGI");  | 
708  |  | #endif  | 
709  |  | #ifdef USE_THREAD  | 
710  |  |   strcat(version, " SUPPORTS=THREADS");  | 
711  |  | #endif  | 
712  |  | #ifdef USE_GEOS  | 
713  |  |   strcat(version, " SUPPORTS=GEOS");  | 
714  |  | #endif  | 
715  |  | #ifdef USE_V8_MAPSCRIPT  | 
716  |  |   strcat(version, " SUPPORTS=V8");  | 
717  |  | #endif  | 
718  |  | #ifdef USE_PBF  | 
719  |  |   strcat(version, " SUPPORTS=PBF");  | 
720  |  | #endif  | 
721  | 0  | #ifdef USE_JPEG  | 
722  | 0  |   strcat(version, " INPUT=JPEG");  | 
723  | 0  | #endif  | 
724  |  | #ifdef USE_SDE  | 
725  |  |   strcat(version, " INPUT=SDE");  | 
726  |  | #endif  | 
727  |  | #ifdef USE_POSTGIS  | 
728  |  |   strcat(version, " INPUT=POSTGIS");  | 
729  |  | #endif  | 
730  |  | #ifdef USE_ORACLESPATIAL  | 
731  |  |   strcat(version, " INPUT=ORACLESPATIAL");  | 
732  |  | #endif  | 
733  | 0  |   strcat(version, " INPUT=OGR");  | 
734  | 0  |   strcat(version, " INPUT=GDAL");  | 
735  | 0  |   strcat(version, " INPUT=SHAPEFILE");  | 
736  | 0  |   strcat(version, " INPUT=FLATGEOBUF");  | 
737  | 0  |   return (version);  | 
738  | 0  | }  | 
739  |  |  | 
740  | 0  | int msGetVersionInt() { return MS_VERSION_NUM; } |