Fix possible security vulnerability and fatal error
[xonotic/netradiant.git] / tools / heretic2 / common / inout.c
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 //-----------------------------------------------------------------------------
23 //
24 //
25 // DESCRIPTION:
26 // deal with in/out tasks, for either stdin/stdout or network/XML stream
27 //
28
29 #include "globaldefs.h"
30 #include "cmdlib.h"
31 #include "mathlib.h"
32 #include "polylib.h"
33 #include "inout.h"
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #if GDEF_OS_WINDOWS
38 #include <direct.h>
39 #include <windows.h>
40 #endif
41
42 // network broadcasting
43 #include "l_net/l_net.h"
44 #include "libxml/tree.h"
45
46 #if GDEF_OS_WINDOWS
47 HWND hwndOut = NULL;
48 qboolean lookedForServer = false;
49 UINT wm_BroadcastCommand = -1;
50 #endif
51
52 socket_t *brdcst_socket;
53 netmessage_t msg;
54
55 qboolean verbose = false;
56
57 // our main document
58 // is streamed through the network to Radiant
59 // possibly written to disk at the end of the run
60 //++timo FIXME: need to be global, required when creating nodes?
61 xmlDocPtr doc;
62 xmlNodePtr tree;
63
64 // some useful stuff
65 xmlNodePtr xml_NodeForVec( vec3_t v ){
66         xmlNodePtr ret;
67         char buf[1024];
68
69         sprintf( buf, "%f %f %f", v[0], v[1], v[2] );
70         ret = xmlNewNode( NULL, "point" );
71         xmlNodeAddContent( ret, buf );
72         return ret;
73 }
74
75 // send a node down the stream, add it to the document
76 void xml_SendNode( xmlNodePtr node ){
77         xmlBufferPtr xml_buf;
78         char xmlbuf[MAX_NETMESSAGE]; // we have to copy content from the xmlBufferPtr into an aux buffer .. that sucks ..
79         // this index loops through the node buffer
80         int pos = 0;
81         int size;
82
83         xmlAddChild( doc->children, node );
84
85         if ( brdcst_socket ) {
86                 xml_buf = xmlBufferCreate();
87                 xmlNodeDump( xml_buf, doc, node, 0, 0 );
88
89                 // the XML node might be too big to fit in a single network message
90                 // l_net library defines an upper limit of MAX_NETMESSAGE
91                 // there are some size check errors, so we use MAX_NETMESSAGE-10 to be safe
92                 // if the size of the buffer exceeds MAX_NETMESSAGE-10 we'll send in several network messages
93                 while ( pos < xml_buf->use )
94                 {
95                         // what size are we gonna send now?
96                         ( xml_buf->use - pos < MAX_NETMESSAGE - 10 ) ? ( size = xml_buf->use - pos ) : ( size = MAX_NETMESSAGE - 10 );
97                         //++timo just a debug thing
98                         if ( size == MAX_NETMESSAGE - 10 ) {
99                                 Sys_FPrintf( SYS_NOXML, "Got to split the buffer\n" );
100                         }
101                         memcpy( xmlbuf, xml_buf->content + pos, size );
102                         xmlbuf[size] = '\0';
103                         NMSG_Clear( &msg );
104                         NMSG_WriteString( &msg, xmlbuf );
105                         Net_Send( brdcst_socket, &msg );
106                         // now that the thing is sent prepare to loop again
107                         pos += size;
108                 }
109
110 #if 0
111                 // NOTE: the NMSG_WriteString is limited to MAX_NETMESSAGE
112                 // we will need to split into chunks
113                 // (we could also go lower level, in the end it's using send and receiv which are not size limited)
114                 //++timo FIXME: MAX_NETMESSAGE is not exactly the max size we can stick in the message
115                 //  there's some tweaking to do in l_net for that .. so let's give us a margin for now
116
117                 //++timo we need to handle the case of a buffer too big to fit in a single message
118                 // try without checks for now
119                 if ( xml_buf->use > MAX_NETMESSAGE - 10 ) {
120                         // if we send that we are probably gonna break the stream at the other end..
121                         // and Error will call right there
122                         //Error( "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use);
123                         Sys_FPrintf( SYS_NOXML, "MAX_NETMESSAGE exceeded for XML feedback stream in FPrintf (%d)\n", xml_buf->use );
124                         xml_buf->content[xml_buf->use] = '\0'; //++timo this corrupts the buffer but we don't care it's for printing
125                         Sys_FPrintf( SYS_NOXML, xml_buf->content );
126
127                 }
128
129                 size = xml_buf->use;
130                 memcpy( xmlbuf, xml_buf->content, size );
131                 xmlbuf[size] = '\0';
132                 NMSG_Clear( &msg );
133                 NMSG_WriteString( &msg, xmlbuf );
134                 Net_Send( brdcst_socket, &msg );
135 #endif
136
137                 xmlBufferFree( xml_buf );
138         }
139 }
140
141 void xml_Select( char *msg, int entitynum, int brushnum, qboolean bError ){
142         xmlNodePtr node, select;
143         char buf[1024];
144         char level[2];
145
146         // now build a proper "select" XML node
147         sprintf( buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg );
148         node = xmlNewNode( NULL, "select" );
149         xmlNodeAddContent( node, buf );
150         level[0] = (int)'0' + ( bError ? SYS_ERR : SYS_WRN )  ;
151         level[1] = 0;
152         xmlSetProp( node, "level", (char *)&level );
153         // a 'select' information
154         sprintf( buf, "%i %i", entitynum, brushnum );
155         select = xmlNewNode( NULL, "brush" );
156         xmlNodeAddContent( select, buf );
157         xmlAddChild( node, select );
158         xml_SendNode( node );
159
160         sprintf( buf, "Entity %i, Brush %i: %s", entitynum, brushnum, msg );
161         if ( bError ) {
162                 Error( buf );
163         }
164         else{
165                 Sys_FPrintf( SYS_NOXML, "%s\n", buf );
166         }
167
168 }
169
170 void xml_Point( char *msg, vec3_t pt ){
171         xmlNodePtr node, point;
172         char buf[1024];
173         char level[2];
174
175         node = xmlNewNode( NULL, "pointmsg" );
176         xmlNodeAddContent( node, msg );
177         level[0] = (int)'0' + SYS_ERR;
178         level[1] = 0;
179         xmlSetProp( node, "level", (char *)&level );
180         // a 'point' node
181         sprintf( buf, "%g %g %g", pt[0], pt[1], pt[2] );
182         point = xmlNewNode( NULL, "point" );
183         xmlNodeAddContent( point, buf );
184         xmlAddChild( node, point );
185         xml_SendNode( node );
186
187         sprintf( buf, "%s (%g %g %g)", msg, pt[0], pt[1], pt[2] );
188         Error( buf );
189 }
190
191 #define WINDING_BUFSIZE 2048
192 void xml_Winding( char *msg, vec3_t p[], int numpoints, qboolean die ){
193         xmlNodePtr node, winding;
194         char buf[WINDING_BUFSIZE];
195         char smlbuf[128];
196         char level[2];
197         int i;
198
199         node = xmlNewNode( NULL, "windingmsg" );
200         xmlNodeAddContent( node, msg );
201         level[0] = (int)'0' + SYS_ERR;
202         level[1] = 0;
203         xmlSetProp( node, "level", (char *)&level );
204         // a 'winding' node
205         sprintf( buf, "%i ", numpoints );
206         for ( i = 0; i < numpoints; i++ )
207         {
208                 sprintf( smlbuf, "(%g %g %g)", p[i][0], p[i][1], p[i][2] );
209                 // don't overflow
210                 if ( strlen( buf ) + strlen( smlbuf ) > WINDING_BUFSIZE ) {
211                         break;
212                 }
213                 strcat( buf, smlbuf );
214         }
215
216         winding = xmlNewNode( NULL, "winding" );
217         xmlNodeAddContent( winding, buf );
218         xmlAddChild( node, winding );
219         xml_SendNode( node );
220
221         if ( die ) {
222                 Error( msg );
223         }
224         else
225         {
226                 Sys_Printf( msg );
227                 Sys_Printf( "\n" );
228         }
229 }
230
231 // in include
232 #include "stream_version.h"
233
234 void Broadcast_Setup( const char *dest ){
235         address_t address;
236         char sMsg[1024];
237
238         Net_Setup();
239         Net_StringToAddress( (char *)dest, &address );
240         brdcst_socket = Net_Connect( &address, 0 );
241         if ( brdcst_socket ) {
242                 // send in a header
243                 sprintf( sMsg, "<?xml version=\"1.0\"?><q3map_feedback version=\"" Q3MAP_STREAM_VERSION "\">" );
244                 NMSG_Clear( &msg );
245                 NMSG_WriteString( &msg, sMsg );
246                 Net_Send( brdcst_socket, &msg );
247         }
248 }
249
250 void Broadcast_Shutdown(){
251         if ( brdcst_socket ) {
252                 Sys_Printf( "Disconnecting\n" );
253                 Net_Disconnect( brdcst_socket );
254                 brdcst_socket = NULL;
255         }
256 }
257
258 // all output ends up through here
259 void FPrintf( int flag, char *buf ){
260         xmlNodePtr node;
261         static qboolean bGotXML = false;
262         char level[2];
263
264         printf( "%s", buf );
265
266         // the following part is XML stuff only.. but maybe we don't want that message to go down the XML pipe?
267         if ( flag == SYS_NOXML ) {
268                 return;
269         }
270
271         // ouput an XML file of the run
272         // use the DOM interface to build a tree
273         /*
274            <message level='flag'>
275            message string
276            .. various nodes to describe corresponding geometry ..
277            </message>
278          */
279         if ( !bGotXML ) {
280                 // initialize
281                 doc = xmlNewDoc( "1.0" );
282                 doc->children = xmlNewDocRawNode( doc, NULL, "q3map_feedback", NULL );
283                 bGotXML = true;
284         }
285         node = xmlNewNode( NULL, "message" );
286         xmlNodeAddContent( node, buf );
287         level[0] = (int)'0' + flag;
288         level[1] = 0;
289         xmlSetProp( node, "level", (char *)&level );
290
291         xml_SendNode( node );
292 }
293
294 #ifdef DBG_XML
295 void DumpXML(){
296         xmlSaveFile( "XMLDump.xml", doc );
297 }
298 #endif
299
300 void Sys_FPrintf( int flag, const char *format, ... ){
301         char out_buffer[4096];
302         va_list argptr;
303
304         if ( ( flag == SYS_VRB ) && ( verbose == false ) ) {
305                 return;
306         }
307
308         va_start( argptr, format );
309         vsprintf( out_buffer, format, argptr );
310         va_end( argptr );
311
312         FPrintf( flag, out_buffer );
313 }
314
315 void Sys_Printf( const char *format, ... ){
316         char out_buffer[4096];
317         va_list argptr;
318
319         va_start( argptr, format );
320         vsprintf( out_buffer, format, argptr );
321         va_end( argptr );
322
323         FPrintf( SYS_STD, out_buffer );
324 }
325
326 /*
327    =================
328    Error
329
330    For abnormal program terminations
331    =================
332  */
333 void Error( const char *error, ... ){
334         char out_buffer[4096];
335         char tmp[4096];
336         va_list argptr;
337
338         va_start( argptr,error );
339         vsprintf( tmp, error, argptr );
340         va_end( argptr );
341
342         sprintf( out_buffer, "************ ERROR ************\n%s\n", tmp );
343
344         FPrintf( SYS_ERR, out_buffer );
345
346 #ifdef DBG_XML
347         DumpXML();
348 #endif
349
350         //++timo HACK ALERT .. if we shut down too fast the xml stream won't reach the listener.
351         // a clean solution is to send a sync request node in the stream and wait for an answer before exiting
352         Sys_Sleep( 1000 );
353
354         Broadcast_Shutdown();
355
356         exit( 1 );
357 }