FreeWRL / FreeX3D 4.3.0
pluginUtils.c
1/*
2
3 FreeWRL support library.
4 Plugin interaction.
5
6*/
7
8
9/****************************************************************************
10 This file is part of the FreeWRL/FreeX3D Distribution.
11
12 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
13
14 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
15 it under the terms of the GNU Lesser Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
26****************************************************************************/
27
28#include <config.h>
29#include <system.h>
30#include <display.h>
31#include <internal.h>
32#include "../threads.h"
33
34#include <libFreeWRL.h>
35#include <list.h>
36#include <io_files.h>
37#include <signal.h>
38
39#include "../vrml_parser/Structs.h"
40#include "../main/headers.h"
41#include "../main/MainLoop.h" /* for fwl_replaceWorldNeededRes() */
42#include "../input/EAIHeaders.h" /* for implicit declarations */
43
44#include "../x3d_parser/Bindable.h"
45#include "../opengl/OpenGL_Utils.h"
46#include "../scenegraph/RenderFuncs.h"
47#include "../main/ProdCon.h"
48#include "pluginUtils.h"
49#include "PluginSocket.h"
50
51#define UNUSED(v) ((void) v)
52
53static int checkIfX3DVRMLFile(char *fn);
54// OLDCODE static int checkIfHTMLFile(char *fn);
55
56/* get all system commands, and pass them through here. What we do
57 * is take parameters and execl them, in specific formats, to stop
58 * people (or, to try to stop) from typing malicious code. */
59
60/* keep a list of children; if one hangs, fwl_doQuit will hang, also. */
61#if !( defined(_MSC_VER) )
62#define MAXPROCESSLIST 128
63pid_t childProcess[MAXPROCESSLIST];
64int lastchildProcess = 0;
65int childProcessListInit = FALSE;
66#else
67#include <process.h>
68#endif
69
70typedef struct ppluginUtils{
71 /* we keep polling here, if we are loading a url...*/
72 int waitingForURLtoLoad;// = FALSE;
73 resource_item_t *plugin_res;// = NULL; /* If this res is valid, then we can replace root_res with it */
75void *pluginUtils_constructor(){
76 void *v = MALLOCV(sizeof(struct ppluginUtils));
77 memset(v,0,sizeof(struct ppluginUtils));
78 return v;
79}
80void pluginUtils_init(struct tpluginUtils *t){
81 //public
82 //private
83 t->prv = pluginUtils_constructor();
84 {
85 pppluginUtils p = (pppluginUtils)t->prv;
86 /* we keep polling here, if we are loading a url...*/
87 p->waitingForURLtoLoad = FALSE;
88 p->plugin_res = NULL; /* If this res is valid, then we can replace root_res with it */
89 }
90}
91
92
93
94void killErrantChildren(void) {
95#ifndef _MSC_VER
96 int count;
97
98 for (count = 0; count < MAXPROCESSLIST; count++) {
99 if (childProcess[count] != 0) {
100 /* printf ("trying to kill %d\n",childProcess[count]); */
101 /* http://www.opengroup.org/onlinepubs/000095399/functions/kill.html */
102 kill (childProcess[count],SIGINT);
103 }
104 }
105#endif
106}
107
108/* implement Anchor/Browser actions */
109
110void goToViewpoint(char *vp) {
111 struct X3D_Node *localNode;
112 /* unused int tableIndex; */
113 int flen;
114 struct tProdCon *t = &gglobal()->ProdCon;
115
116 /* see if we can get a node that matches this DEF name */
117 localNode = EAI_GetViewpoint(vp);
118
119 /* did we find a match with known Viewpoints?*/
120 if (localNode != NULL) {
121 for (flen=0; flen<vectorSize(t->viewpointNodes);flen++) {
122 if (localNode == vector_get(struct X3D_Node *,t->viewpointNodes,flen)) {
123 struct X3D_Viewpoint *vp;
124 /* unbind current, and bind this one */
125 vp = (struct X3D_Viewpoint*)vector_get(struct X3D_Node *,t->viewpointNodes,t->currboundvpno);
126 send_bind_to((struct X3D_Node*)vp,0);
127 t->currboundvpno=flen;
128 vp = (struct X3D_Viewpoint *)vector_get(struct X3D_Node *,t->viewpointNodes,t->currboundvpno);
129 send_bind_to((struct X3D_Node*)vp,1);
130 return;
131 }
132 }
133 }
134 /* printf ("goToViewpoint - failed to match local Viewpoint\n"); */
135}
136
137#if !defined(_MSC_VER) && !defined(ANDROIDNDK)
138void startNewHTMLWindow(char *url) {
139 const char *browser;
140#define LINELEN 4000
141#define ERRLINELEN 4200
142 char sysline[LINELEN];
143 int sysReturnCode;
144 char syslineFailed[ERRLINELEN];
145 int testlen;
146
147 browser = NULL;
148
149// OLD_IPHONE_AQUA #ifdef AQUA
150// OLD_IPHONE_AQUA if (RUNNINGASPLUGIN) {
151// OLD_IPHONE_AQUA /* printf ("Anchor, running as a plugin - load non-vrml file\n"); */
152// OLD_IPHONE_AQUA requestNewWindowfromPlugin(_fw_browser_plugin, _fw_instance, url);
153// OLD_IPHONE_AQUA } else {
154// OLD_IPHONE_AQUA #endif
155
156 browser = freewrl_get_browser_program();
157 if (!browser) {
158 ConsoleMessage ("Error: no Internet browser found.");
159 return;
160 }
161
162 /* bounds check here */
163 testlen = 0;
164 if (browser) testlen = (int) strlen(browser);
165
166 testlen += (int) strlen(url) + 10;
167 if (testlen > LINELEN) {
168 ConsoleMessage ("Anchor: combination of browser name and file name too long.");
169 } else {
170
171 if (browser) strcpy (sysline, browser);
172 else strcpy (sysline, browser);
173 strcat (sysline, " ");
174 strcat (sysline, url);
175 strcat (sysline, " &");
176 freewrlSystem (sysline);
177 }
178
179
180 if (browser) sprintf(sysline, "%s %s &", browser, url);
181 else sprintf(sysline, "open %s &", url);
182 sysReturnCode = system (sysline);
183 if (sysReturnCode < 0) {
184 sprintf(syslineFailed ,"ERR %s %d system call failed, returned %d. Was: %s\n",__FILE__,__LINE__,sysReturnCode,sysline);
185 ConsoleMessage (syslineFailed);
186 }
187// OLD_IPHONE_AQUA #ifdef AQUA
188// OLD_IPHONE_AQUA
189// OLD_IPHONE_AQUA }
190// OLD_IPHONE_AQUA #endif
191}
192#endif
193
194
196//static int waitingForURLtoLoad = FALSE;
197//static resource_item_t *plugin_res = NULL; /* If this res is valid, then we can replace root_res with it */
198
199static int urlLoadingStatus() {
200 pppluginUtils p = (pppluginUtils)gglobal()->pluginUtils.prv;
201
202 /* printf ("urlLoadingStatus %s\n",resourceStatusToString(plugin_res->status)); */
203 /* printf ("and we have %d children in root.\n",X3D_GROUP(rootNode)->children.n); */
204
205 switch (p->plugin_res->status) {
206 case ress_downloaded:
207 case ress_parsed:
208 EAI_Anchor_Response(TRUE);
209 p->waitingForURLtoLoad = FALSE;
210 break;
211 case ress_failed:
212 ConsoleMessage ("Failed to load URL\n");
213 EAI_Anchor_Response(FALSE);
214 p->waitingForURLtoLoad = FALSE;
215 break;
216 default: {}
217 }
218
219 return p->waitingForURLtoLoad;
220}
221
222/* returns FALSE if we are DONE the action, whether or not it was successful;
223 TRUE if we want to hit this next time through the event loop */
224
225int doBrowserAction()
226{
227 int i;
228 struct Multi_String Anchor_url;
229 char *description;
230 resource_item_t * parentPath;
232 ttglobal tg = gglobal();
233 p = (pppluginUtils)tg->pluginUtils.prv;
234
235
236 /* are we in the process of polling for a new X3D URL to load? */
237 if (p->waitingForURLtoLoad) return urlLoadingStatus();
238
239 UNUSED(description); // compiler warning mitigation
240
241 if (AnchorsAnchor() != NULL) {
242
243 Anchor_url = AnchorsAnchor()->url;
244 description = AnchorsAnchor()->description->strptr;
245
246 TRACE_MSG("doBrowserAction: description: %s\n", description);
247
248 /* are we going to load up a new VRML/X3D world, or are we going to just go and load up a new web page ? */
249 if (Anchor_url.n <= 0) {
250 /* printf ("have Anchor, empty URL\n"); */
251 setAnchorsAnchor( NULL );
252 return FALSE; /* done the action, the url is just not good */
253 }
254
255 /* June 2016 goal: make frontend handle .html anchors, so it can be done in a platform-specific way
256 x we can't handle mixed anchors (see next comment)
257 * so we still need to determine media_type here:
258 scene, external (ie html - for frontend to do external anchor), or viewpoint.
259
260 [future: allow mixed anchors: Anchor url [ "missing.wrl" "bad.exe" "funny.html" "#viewpoint" ]
261 and have resource.c iterate over SF till one loads.
262 Problem for future: it takes us several steps to unload current scene, and those steps conflict
263 with any attempt to load a new scene at the same time. So in a resource.c/ProdCon.c driven
264 mixed anchor, if we suddenly find a scene succeeds download, we can't properly unload
265 current scene, due to all the perl arrays and implicit 'bindings' ie one global route array,
266 one global node array, one global texture array, navigation last drag locations in scene rather than mouse coords,
267 and the resource and parsing queues that will think current scene parts are for the new scene.
268 Ideally all those implicit bindings would be hot-swappable, resources would know their scene, so when
269 the are dequeued if their scene is gone they'll be disposed -
270 so a new scene can be parsed while old one running, and swapped in ProdCon after parse, or on next frame.]
271 */
272
273
274 /* first test case - url is ONLY a viewpoint change */
275 if (Anchor_url.p[0]->strptr[0] == '#') {
276 setAnchorsAnchor( NULL );
277 fwl_gotoViewpoint(&(Anchor_url.p[0]->strptr[1]));
278 return TRUE;
279 }
280 /* We have a url, lets go and get the first one of them */
281 parentPath = (resource_item_t *)AnchorsAnchor()->_parentResource;
282 p->plugin_res = resource_create_multi0(&AnchorsAnchor()->url);
283#define DO_THIS_BLOC 1
284#ifdef DO_THIS_BLOC //EXPERIMENT_FAILED
285 {
286 /*Goal: avoid blocking UI thread
287 Method: schedule a scene change whereby the file part gets sent to the resource worker thread
288 while the UI thread keeps looping
289 - first, make sure it's a scene file (not .html, .img which are handled in the html browser in new window)
290 */
291 BOOL isScene; //, isHTML;
292
293 // June 2016 change: avoid mixed anchors, must be uniform ie all SF are scene, or all SF are html
294 // for it to be scene, all need to be scene (could split out non-scene to get scene-only MF / non-mixed anchor)
295 isScene = TRUE; //was FALSE
296 for (i = 0; i < Anchor_url.n; i++)
297 isScene = isScene && checkIfX3DVRMLFile(Anchor_url.p[i]->strptr); // was ||
298 if (isScene){
299 resource_identify(parentPath, p->plugin_res);
300 fwl_replaceWorldNeededRes(p->plugin_res);
301 return FALSE;
302 }
303 // June 2016: if not scene or viewpoint, it might be html, plain images, audio
304 // send to frontend for platform-specific anchoring to web browser
305 p->plugin_res->actions = resa_download; //just to get it to the frontend
306 p->plugin_res->media_type = resm_external;
307 p->plugin_res->type = rest_multi;
308 resource_identify(parentPath, p->plugin_res);
309 resitem_enqueue(ml_new(p->plugin_res));
310 }
311#else //EXPERIMENT
312
313#ifdef TEXVERBOSE
314 PRINTF("url: ");
315 Multi_String_print(&AnchorsAnchor()->url);
316 PRINTF("parent resource: \n");
317 resource_dump(parentPath);
318 PRINTF("file resource: \n");
319 resource_dump(p->plugin_res);
320#endif
321 {
322 s_list_t *head_of_list;
323
324 /* hold on to the top of the list so we can delete it later */
325 head_of_list = p->plugin_res->m_request;
326
327 /* go through the urls until we have a success, or total failure */
328 do {
329 /* Setup parent */
330 resource_identify(parentPath, p->plugin_res);
331
332 /* Setup media type */
333 p->plugin_res->media_type = resm_image; /* quick hack */
334
335 if (resource_fetch(p->plugin_res)) {
336 /* printf ("really loading anchor from %s\n", plugin_res->actual_file); */
337
338 /* we have the file; res->actual_file is the file on the local system;
339 plugin_res->parsed_request is the file that might be remote */
340
341 if (checkIfX3DVRMLFile(p->plugin_res->actual_file)) {
342 resource_item_t *resToLoad;
343
344 /* out with the old... */
345 kill_oldWorld(TRUE,TRUE,__FILE__,__LINE__);
346
347 /* tell the new world which viewpoint to go to */
348 //fwl_gotoViewpoint (p->plugin_res->afterPoundCharacters);
349 resToLoad = resource_create_single(p->plugin_res->actual_file);
350 resToLoad->afterPoundCharacters = p->plugin_res->afterPoundCharacters;
351 /* in with the new... */
352 send_resource_to_parser(resToLoad);
353 p->waitingForURLtoLoad = TRUE;
354 return TRUE; /* keep the browser ticking along here */
355 } else {
356#ifdef _MSC_VER
357 resource_item_t *resToLoad;
358 //we don't want to launch a new IE browser or IE tab,
359 //just load a new scene in freewrl
360 //analogous to what happens when we have file://...AnchorA.x3d
361 //the following is the only way I know to do that right now, same as
362 //below several lines:
363 kill_oldWorld(TRUE,TRUE,__FILE__,__LINE__);
364
365 /* we want to clean out the old world AND load a new one in */
366
367 resToLoad = resource_create_single(p->plugin_res->parsed_request);
368 resToLoad->afterPoundCharacters = p->plugin_res->afterPoundCharacters;
369
370 send_resource_to_parser(resToLoad);
371
372 p->waitingForURLtoLoad = TRUE;
373 return TRUE; /* keep the browser ticking along here */
374#else
375 p->plugin_res->complete = TRUE;
376 startNewHTMLWindow(p->plugin_res->parsed_request);
377#endif
378 }
379 } else {
380 /* we had a problem with that URL, set this so we can try the next */
381 p->plugin_res->type=rest_multi;
382 }
383 } while ((p->plugin_res->status != ress_downloaded) && (p->plugin_res->m_request != NULL));
384
385 /* destroy the m_request, if it exists */
386 if (head_of_list != NULL) {
387 ml_delete_all(head_of_list);
388 }
389
390 /* were we successful?? */
391 if (p->plugin_res->status != ress_loaded) {
392 ERROR_MSG("Could not load new world: %s\n", p->plugin_res->actual_file);
393 return FALSE;
394 }
395 }
396#endif //EXPERIMENT
397
398/*********************************************************/
399
400
401 } else {
402 /* printf ("\nwe have a single replacement here\n"); */
403 }
404 return FALSE; /* we are done the action */
405}
406
407/*
408 * Check to see if the file name is a geometry file.
409 * return TRUE if it looks like it is, false otherwise
410 *
411 * This should be kept in line with the plugin register code in
412 * Plugin/netscape/source/npfreewrl.c
413 */
414
415static int checkIfX3DVRMLFile(char *fn) {
416 if ((strstr(fn,".wrl") > 0) ||
417 (strstr(fn,".WRL") > 0) ||
418 (strstr(fn,".x3d") > 0) ||
419 (strstr(fn,".x3z") > 0) ||
420 (strstr(fn,".x3dv") > 0) ||
421 (strstr(fn,".x3db") > 0) ||
422 (strstr(fn,".X3DV") > 0) ||
423 (strstr(fn,".X3DB") > 0) ||
424 (strstr(fn,".X3D") > 0)) {
425 return TRUE;
426 }
427 return FALSE;
428}
429// OLDCODE static int checkIfHTMLFile(char *fn) {
430// OLDCODE if ((strstr(fn,".html") > 0) ||
431// OLDCODE (strstr(fn,".HTML") > 0)
432// OLDCODE ) {
433// OLDCODE return TRUE;
434// OLDCODE }
435// OLDCODE return FALSE;
436// OLDCODE }
437
438
439void new_root();
440/* we are an Anchor, and we are not running in a browser, and we are
441 * trying to do an external VRML or X3D world.
442 */
443/* void Anchor_ReplaceWorld (char *name) */
444bool Anchor_ReplaceWorld(const char *name)
445{
446 gglobal()->Mainloop.replaceWorldRequest = STRDUP(name);
447 //resource_item_t *AR_res;
448
450
451 //AR_res = resource_create_single(name);
453 //new_root();
454 //send_resource_to_parser(AR_res);
455 //resource_wait(AR_res);
456
457 //if (AR_res->status != ress_loaded) {
458 // /* FIXME: destroy this new node tree */
459 // return FALSE;
460 //}
461 return TRUE;
462}
463
464/* send in a 0 to 15, return a char representation */
465char tohex (int mychar) {
466 if (mychar <10) return (mychar + '0');
467 else return (mychar - 10 + 'A');
468}
469
470/* should this character be encoded? */
471int URLmustEncode(int ch) {
472 if (
473 (ch == 0x20)
474 || (ch == 0x22)
475 || (ch == 0x3c)
476 || (ch == 0x3e)
477 || (ch == 0x23)
478 || (ch == 0x25)
479 || (ch == 0x7b)
480 || (ch == 0x7d)
481 || (ch == 0x7c)
482 || (ch == 0x5c)
483 || (ch == 0x5e)
484 || (ch == 0x7e)
485 || (ch == 0x5b)
486 || (ch == 0x5d)
487 || (ch == 0x60)) return TRUE;
488 return FALSE;
489}
490
491
492/***************/
493/* static FILE * tty = NULL; */
494
495/* prints to a log file if we are running as a plugin */
496void URLprint (const char *m, const char *p) {
497#undef URLPRINTDEBUG
498#ifdef URLPRINTDEBUG
499 if (tty == NULL) {
500 tty = fopen("/home/luigi/logURLencod", "w");
501 if (tty == NULL)
502 abort();
503 fprintf (tty, "\nplugin restarted\n");
504 }
505
506 fprintf (tty,"%f URLprint: ",TickTime());
507 fprintf(tty, m,p);
508 fflush(tty);
509#endif
510}
511
512/* loop about waiting for the Browser to send us some stuff. */
513/* Change a string to encode spaces for getting URLS. */
514void URLencod (char *dest, const char *src, int maxlen) {
515 int mylen;
516 int sctr;
517 int destctr;
518 int curchar;
519
520#ifdef URLPRINTDEBUG
521 char *orig;
522
523 orig = dest;
524
525 /* get the length of the source and bounds check */
526 URLprint ("going to start URLencod %s\n","on a string");
527 URLprint ("start, src is %s\n",src);
528 /* URLprint ("maxlen is %d\n",maxlen); */
529#endif
530
531 destctr = 0; /* ensure we dont go over dest length */
532 mylen = (int) strlen(src);
533 if (mylen == 0) {
534 dest[0]= '\0';
535 return;
536 }
537
538 /* encode the string */
539 for (sctr = 0; sctr < mylen; sctr ++) {
540 curchar = (int) *src; src++;
541 /* should we encode this one? */
542
543 if (URLmustEncode(curchar)) {
544 *dest = '%'; dest ++;
545 *dest = tohex ((curchar >> 4) & 0x0f); dest++;
546 *dest = tohex (curchar & 0x0f); dest++;
547 destctr +=3;
548 } else {
549 *dest = (char) curchar; destctr++; dest++;
550 }
551
552
553
554 /* bounds check */
555 if (destctr > (maxlen - 5)) {
556 *dest = '\0';
557 return;
558 }
559 }
560 *dest = '\0'; /* null terminate this one */
561#ifdef URLPRINTDEBUG
562 URLprint ("encoded string is %s\n",orig);
563#endif
564
565}
566
567/* this is for Unix only */
568// OLD_IPHONE_AQUA #if !defined(AQUA) && !defined(_MSC_VER) && !defined(_ANDROID) && !defined(ANDROIDNDK) && !defined(GLES2)
569#if !defined(AQUA) && !defined(_MSC_VER) && !defined(_ANDROID) && !defined(ANDROIDNDK) && !defined(GLES2)
570
571void sendXwinToPlugin()
572{
573 int writeSizeThrowAway ;
574 UNUSED(writeSizeThrowAway); // compiler warning mitigation
575
576 /* send the window id back to the plugin parent */
577 DEBUG_MSG("Executing sendXwinToPlugin...\n");
578
579#if KEEP_X11_INLIB
580 XWindowAttributes mywin;
581
582 XGetWindowAttributes(Xdpy,Xwin, &mywin);
583 DEBUG_MSG("sendXwinToPlugin: sendXwin starting, mapped_state %d, IsUnmapped %d, isUnviewable %d isViewable %d\n",mywin.map_state, IsUnmapped, IsUnviewable, IsViewable);
584
585 DEBUG_MSG("sendXwinToPlugin: sending Xwin ID back to plugin - %lu bytes\n",sizeof (Xwin));
586
587 writeSizeThrowAway = write (_fw_pipe,&Xwin,sizeof(Xwin));
588 close (_fw_pipe);
589
590 /* wait for the plugin to change the map_state */
591 XGetWindowAttributes(Xdpy,Xwin, &mywin);
592
593 while (mywin.map_state == IsUnmapped) {
594 usleep (100);
595 XGetWindowAttributes(Xdpy,Xwin, &mywin);
596 #ifdef URLPRINTDEBUG
597 printf ("sendXwin in sleep loope, mapped_state %d, IsUnmapped %d, isUnviewable %d isViewable %d\n",mywin.map_state, IsUnmapped, IsUnviewable, IsViewable);
598 #endif
599
600 }
601
602 #ifdef URLPRINTDEBUG
603 XGetWindowAttributes(Xdpy,Xwin, &mywin);
604 printf ("sendXwin at end, mapped_state %d, IsUnmapped %d, isUnviewable %d isViewable %d\n",mywin.map_state, IsUnmapped, IsUnviewable, IsViewable);
605 printf ("x %d y %d wid %d height %d\n",mywin.x,mywin.y,mywin.width,mywin.height);
606 #endif
607#endif /* KEEP_X11_INLIB */
608
609}
610#endif
611