1 //
2 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014,
3 //   2016 Free Software Foundation, Inc
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 
19 
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
23 
24 #include <boost/format.hpp>
25 #include <boost/algorithm/string/replace.hpp>
26 #include <boost/algorithm/string/find.hpp>
27 #define BOOST_IOSTREAMS_USE_DEPRECATED
28 #include <boost/iostreams/device/file_descriptor.hpp>
29 #include <boost/iostreams/stream.hpp>
30 #include <cassert>
31 #include <string>
32 #include <cstdlib> // getenv
33 #include <stdlib.h> // putenv
34 #include <sys/types.h>
35 #include "GnashSleep.h" // usleep
36 
37 #if defined(HAVE_WINSOCK_H) && !defined(__OS2__)
38 # include <winsock2.h>
39 # include <windows.h>
40 #else
41 # include <netinet/in.h>
42 # include <arpa/inet.h>
43 # include <netdb.h>
44 # include <sys/socket.h>
45 #endif
46 
47 
48 #define MIME_TYPES_HANDLED  "application/x-shockwave-flash"
49 // The name must be this value to get flash movies that check the
50 // plugin version to load.
51 #define PLUGIN_NAME    "Shockwave Flash"
52 #define MIME_TYPES_DESCRIPTION  MIME_TYPES_HANDLED ":swf:" PLUGIN_NAME
53 
54 // Some javascript plugin detectors use the description
55 // to decide the flash version to display. They expect the
56 // form (major version).(minor version) r(revision).
57 // e.g. "8.0 r99."
58 #define FLASH_VERSION DEFAULT_FLASH_MAJOR_VERSION "." \
59     DEFAULT_FLASH_MINOR_VERSION " r" DEFAULT_FLASH_REV_NUMBER "."
60 
61 #define PLUGIN_DESCRIPTION \
62   "Shockwave Flash " FLASH_VERSION "<br>Gnash " VERSION ", the GNU SWF Player. \
63   Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 \
64   <a href=\"http://www.fsf.org\">Free \
65   Software Foundation</a>, Inc. <br> \
66   Gnash comes with NO WARRANTY, to the extent permitted by law. \
67   You may redistribute copies of Gnash under the terms of the \
68   <a href=\"http://www.gnu.org/licenses/gpl.html\">GNU General Public \
69   License</a>. For more information about Gnash, see <a \
70   href=\"http://www.gnu.org/software/gnash/\"> \
71   http://www.gnu.org/software/gnash</a>. \
72   <br>\
73   Compatible Shockwave Flash " FLASH_VERSION
74 
75 #include "plugin.h"
76 #include "GnashSystemIOHeaders.h"
77 #include "StringPredicates.h"
78 #include "external.h"
79 #include "callbacks.h"
80 #if NPAPI_VERSION == 190
81 #include "npupp.h"
82 #else
83 #include "npapi.h"
84 #include "npruntime.h"
85 #include "npfunctions.h"
86 #endif
87 #include "GnashNPVariant.h"
88 
89 #include <boost/tokenizer.hpp>
90 #include <boost/algorithm/string/join.hpp>
91 #include <boost/algorithm/string/split.hpp>
92 #include <boost/algorithm/string/classification.hpp>
93 #include <boost/algorithm/string/trim.hpp>
94 #include <boost/format.hpp>
95 #include <sys/param.h>
96 #include <csignal>
97 #include <cstdio>
98 #include <cstddef>
99 #include <cstring>
100 #include <sys/types.h>
101 #include <sys/stat.h>
102 #include <sys/wait.h>
103 #include <fcntl.h>
104 #include <cerrno>
105 #include <climits>
106 #include <string>
107 #include <vector>
108 #include <iostream>
109 #include <fstream>
110 #include <sstream>
111 
112 // Macro to prevent repeated logging calls for the same
113 // event
114 #define LOG_ONCE(x) { \
115     static bool warned = false; \
116     if (!warned) { warned = true; x; } \
117 }
118 
119 // For scriptable plugin support
120 #include "pluginScriptObject.h"
121 
122 extern NPNetscapeFuncs NPNFuncs;
123 extern NPPluginFuncs NPPFuncs;
124 
125 namespace gnash {
126 NPBool plugInitialized = FALSE;
127 std::string cookiefile;
128 }
129 
130 /// \brief Return the MIME Type description for this plugin.
131 #ifdef NPAPI_CONST
132 const
133 #endif
134 char*
NPP_GetMIMEDescription(void)135 NPP_GetMIMEDescription(void)
136 {
137     return const_cast<char *>(MIME_TYPES_DESCRIPTION);
138 }
139 
140 static bool waitforgdb = false;
141 static bool createSaLauncher = false;
142 
143 static std::map<int, std::string> cookiemap;
144 
145 static const char* getPluginDescription();
146 
147 static const char*
getPluginDescription()148 getPluginDescription()
149 {
150     static const char* desc = nullptr;
151     if (!desc) {
152         desc = std::getenv("GNASH_PLUGIN_DESCRIPTION");
153         if (desc == nullptr) desc = PLUGIN_DESCRIPTION;
154     }
155     return desc;
156 }
157 
158 boost::iostreams::file_descriptor_sink getfdsink(char mkstemplate[]);
159 
160 boost::iostreams::file_descriptor_sink
getfdsink(char mksTemplate[])161 getfdsink(char mksTemplate[])
162 {
163   int fd, suffix = std::string(mksTemplate).size() - std::string(mksTemplate).find("XXXXXX") - 6;
164 #ifdef HAVE_MKSTEMPS
165   fd = mkstemps (mksTemplate, suffix);
166 #else
167   if (suffix > 0) {
168     char *mksTNoSuff = const_cast<char*>(std::string(mksTemplate).substr(0, std::string(mksTemplate).size() - suffix).c_str());
169     fd = mkstemp (mksTNoSuff);
170     const char *mksTSuffix = std::string(mksTemplate).substr(std::string(mksTemplate).size() - suffix, suffix).c_str();
171     std::stringstream mksTFull;
172     mksTFull << mksTNoSuff << mksTSuffix;
173     if (rename (mksTNoSuff, mksTFull.str().c_str()) != 0) {
174       gnash::log_error("Failed to rename %s", mksTNoSuff);
175     }
176     strcpy (mksTemplate, mksTFull.str().c_str());
177   } else {
178     fd = mkstemp (mksTemplate);
179   }
180 #endif
181 #if BOOST_VERSION < 104400
182   boost::iostreams::file_descriptor_sink fdsink(fd, true);
183 #else
184   boost::iostreams::file_descriptor_sink fdsink(fd, boost::iostreams::close_handle);
185 #endif
186   return fdsink;
187 }
188 
189 //
190 // general initialization and shutdown
191 //
192 
193 /// \brief Initialize the plugin
194 ///
195 /// This C++ function gets called once when the plugin is loaded,
196 /// regardless of how many instantiations there is actually playing
197 /// movies. So this is where all the one time only initialization
198 /// stuff goes.
199 NPError
NS_PluginInitialize()200 NS_PluginInitialize()
201 {
202 
203     if ( gnash::plugInitialized ) {
204         gnash::log_debug("NS_PluginInitialize called, but ignored (we already initialized)");
205         return NPERR_NO_ERROR;
206     }
207 
208     gnash::log_debug("NS_PluginInitialize call ---------------------------");
209 
210     // Browser Functionality Checks
211 
212     NPError err = NPERR_NO_ERROR;
213     NPBool supportsXEmbed = TRUE;
214 
215     // First, check for XEmbed support. The NPAPI Gnash plugin
216     // only works with XEmbed, so tell the plugin API to fail if
217     // XEmbed is not found.
218     err = NPN_GetValue(nullptr,NPNVSupportsXEmbedBool,
219                        (void *)&supportsXEmbed);
220 
221     if (err != NPERR_NO_ERROR || !supportsXEmbed) {
222         gnash::log_error("NPAPI ERROR: No xEmbed support in this browser!");
223         return NPERR_INCOMPATIBLE_VERSION_ERROR;
224     } else {
225         gnash::log_debug("xEmbed supported in this browser");
226     }
227 
228     // GTK is not strictly required, but we do use the Glib main event loop,
229     // so lack of GTK means reduced functionality.
230     NPNToolkitType toolkit;
231     err = NPN_GetValue(nullptr, NPNVToolkit, &toolkit);
232 
233     if (err != NPERR_NO_ERROR || toolkit != NPNVGtk2) {
234         gnash::log_error("NPAPI ERROR: No GTK2 support in this browser! Have version %d", (int)toolkit);
235     } else {
236         gnash::log_debug("GTK2 supported in this browser");
237     }
238 
239     /*
240     Check for environment variables.
241     */
242     char* opts = std::getenv("GNASH_OPTIONS");
243     if (opts != nullptr) {
244         gnash::log_debug("GNASH_OPTIONS: %s", opts);
245 
246         // Should the plugin wait for gdb to be attached?
247         if ( strstr(opts, "waitforgdb") ) {
248             waitforgdb = true;
249         }
250 
251         // Should the plugin write a script to invoke
252         // the standalone player for debugging ?
253         if ( strstr(opts, "writelauncher") ) {
254             createSaLauncher = true;
255         }
256 
257     }
258 
259     // Append SYSCONFDIR/gnashpluginrc and ~/.gnashpluginrc to GNASHRC
260 
261     std::string newGnashRc;
262 
263 #if !defined(__OS2__ ) && ! defined(__amigaos4__)
264     newGnashRc.append(SYSCONFDIR);
265     newGnashRc.append("/gnashpluginrc");
266 #endif
267 
268     const char *home = nullptr;
269 #if defined(__amigaos4__)
270     //on AmigaOS we have a GNASH: assign that point to program dir
271     home = "/gnash";
272 #elif defined(__HAIKU__)
273     BPath bp;
274     if (B_OK != find_directory(B_USER_SETTINGS_DIRECTORY, &bp))
275     {
276         std::cerr << "Failed to find user settings directory" << std::endl;
277     } else {
278         bp.Append("Gnash");
279         home = bp.Path();
280     }
281 #else
282     home = std::getenv("HOME");
283 #endif
284     if ( home ) {
285         newGnashRc.append(":");
286         newGnashRc.append(home);
287 #ifdef __HAIKU__
288         newGnashRc.append("/gnashpluginrc");
289 #else
290         newGnashRc.append("/.gnashpluginrc");
291 #endif
292     } else {
293         gnash::log_error("WARNING: NPAPI plugin could not find user home dir");
294     }
295 
296     const char *gnashrc = std::getenv("GNASHRC");
297     if ( gnashrc ) {
298         newGnashRc.append(":");
299         newGnashRc.append(gnashrc);
300     }
301 
302     if ( setenv("GNASHRC", newGnashRc.c_str(), 1) ) {
303         gnash::log_debug("WARNING: NPAPI plugin could not append to the GNASHRC env variable");
304     } else {
305         gnash::log_debug("NOTE: NPAPI plugin set GNASHRC to %d", newGnashRc);
306     }
307 
308     /* Success */
309 
310     gnash::plugInitialized = TRUE;
311 
312     return NPERR_NO_ERROR;
313 }
314 
315 /// \brief Shutdown the plugin
316 ///
317 /// This C++ function gets called once when the plugin is being
318 /// shutdown, regardless of how many instantiations actually are
319 /// playing movies. So this is where all the one time only
320 /// shutdown stuff goes.
321 void
NS_PluginShutdown()322 NS_PluginShutdown()
323 {
324 #if 0
325     if (!plugInitialized) {
326         gnash::log_debug("Plugin already shut down");
327         return;
328     }
329 
330     plugInitialized = FALSE;
331 #endif
332 }
333 
334 /// \brief Retrieve values from the plugin for the Browser
335 ///
336 /// This C++ function is called by the browser to get certain
337 /// information is needs from the plugin. This information is the
338 /// plugin name, a description, etc...
339 NPError
NS_PluginGetValue(NPPVariable aVariable,void * aValue)340 NS_PluginGetValue(NPPVariable aVariable, void *aValue)
341 {
342     NPError err = NPERR_NO_ERROR;
343 
344     switch (aVariable) {
345       case NPPVpluginNameString:
346           *static_cast<const char **> (aValue) = PLUGIN_NAME;
347           break;
348 
349           // This becomes the description field you see below the opening
350           // text when you type about:plugins and in
351           // navigator.plugins["Shockwave Flash"].description, used in
352           // many flash version detection scripts.
353       case NPPVpluginDescriptionString:
354           *static_cast<const char **>(aValue) = getPluginDescription();
355           break;
356 
357       case NPPVpluginWindowBool:
358           break;
359 
360       case NPPVpluginTimerInterval:
361           break;
362 
363       case NPPVpluginKeepLibraryInMemory:
364           break;
365 
366       case NPPVpluginNeedsXEmbed:
367 #ifdef HAVE_GTK2
368           *static_cast<NPBool *>(aValue) = TRUE;
369 #else
370           *static_cast<NPBool *>(aValue) = FALSE;
371 #endif
372           break;
373 
374       case NPPVpluginScriptableNPObject:
375           break;
376 
377 #if NPAPI_VERSION != 190
378       case NPPVpluginUrlRequestsDisplayedBool:
379           break;
380       case NPPVpluginWantsAllNetworkStreams:
381           break;
382 #endif
383 
384       default:
385           err = NPERR_INVALID_PARAM;
386           break;
387     }
388     return err;
389 }
390 
391 /// \brief construct our plugin instance object
392 ///
393 /// This instantiates a new object via a C++ function used by the
394 /// browser.
395 nsPluginInstanceBase *
NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)396 NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)
397 {
398     // gnash::log_debug(__PRETTY_FUNCTION__);
399 
400     if(!aCreateDataStruct) {
401         return nullptr;
402     }
403 
404     return new gnash::nsPluginInstance(aCreateDataStruct);
405 }
406 
407 /// \brief destroy our plugin instance object
408 ///
409 /// This destroys our instantiated object via a C++ function used by the
410 /// browser.
411 void
NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)412 NS_DestroyPluginInstance(nsPluginInstanceBase* aPlugin)
413 {
414     delete static_cast<gnash::nsPluginInstance *> (aPlugin);
415 }
416 
417 namespace gnash {
418 
HasScripting()419 inline bool HasScripting()
420 {
421     return (NPNFuncs.version >= NPVERS_HAS_NPRUNTIME_SCRIPTING);
422 }
423 
424 
425 //
426 // nsPluginInstance class implementation
427 //
428 
429 /// \brief Constructor
nsPluginInstance(nsPluginCreateData * data)430 nsPluginInstance::nsPluginInstance(nsPluginCreateData* data)
431     :
432     nsPluginInstanceBase(),
433     _instance(data->instance),
434     _window(0),
435     _width(0),
436     _height(0),
437     _streamfd(-1),
438     _childpid(0),
439     _filefd(-1),
440     _name(),
441     _scriptObject(nullptr)
442 {
443     // gnash::log_debug("%s: %x", __PRETTY_FUNCTION__, (void *)this);
444 
445     for (size_t i=0, n=data->argc; i<n; ++i) {
446         std::string name, val;
447         gnash::StringNoCaseEqual noCaseCompare;
448 
449         if (data->argn[i]) {
450             name = data->argn[i];
451         }
452 
453         if (data->argv[i]) {
454             val = data->argv[i];
455         }
456 
457         if (noCaseCompare(name, "name")) {
458             _name = val;
459         }
460 
461         _params[name] = val;
462     }
463 
464     if (HasScripting()) {
465         _scriptObject = (GnashPluginScriptObject*) NPN_CreateObject( _instance,
466             GnashPluginScriptObject::marshalGetNPClass());
467     }
468 
469     return;
470 }
471 
472 gboolean
cleanup_childpid(gpointer data)473 cleanup_childpid(gpointer data)
474 {
475     int* pid = static_cast<int*>(data);
476 
477     int status;
478     int rv = waitpid(*pid, &status, WNOHANG);
479 
480     if (rv <= 0) {
481         // The child process has not exited; it may be deadlocked. Kill it.
482 
483         kill(*pid, SIGKILL);
484         waitpid(*pid, &status, 0);
485     }
486 
487     gnash::log_debug("Child process exited with status %s", status);
488 
489     delete pid;
490 
491     return FALSE;
492 }
493 
494 /// \brief Destructor
~nsPluginInstance()495 nsPluginInstance::~nsPluginInstance()
496 {
497 //    gnash::log_debug("plugin instance destruction");
498 
499     if (_scriptObject) {
500         NPN_ReleaseObject(_scriptObject);
501     }
502 
503     do { } while (g_source_remove_by_user_data(this));
504 
505     if (_childpid > 0) {
506         // When the child has terminated (signaled by GTK through GtkSocket), it
507         // remains as a defunct process and we remove it from the kernel table now.
508 
509         // FIXME: we should ideally do this before the GtkSocket goes away, but
510         // after the delete signal has been sent.
511 
512         // If all goes well, Gnash will already have terminated.
513         int status;
514         int rv = waitpid(_childpid, &status, WNOHANG);
515 
516         if (rv <= 0) {
517              int* pid = new int(_childpid);
518              gnashSleep(1000);
519 	     cleanup_childpid(pid);
520         } else {
521             gnash::log_debug("Child process exited with status %d", status);
522         }
523 
524         // Remove cookiefile if writelauncher not set
525         const char* options = std::getenv("GNASH_OPTIONS");
526         if ((!options || !strstr(options, "writelauncher")) &&
527             (!cookiefile.empty())) {
528             std::map<int, std::string>::iterator it = cookiemap.find(_childpid);
529             if (it != cookiemap.end()) {
530                 if (remove(it->second.c_str())) {
531                     gnash::log_error("Cookiefile %s removal failed [pid %d]",
532                         it->second, _childpid);
533                 } else {
534                     gnash::log_debug("Cookiefile %s removed [pid %d]",
535                         it->second, _childpid);
536                 }
537                 cookiemap.erase(it);
538             }
539         }
540     }
541     _childpid = 0;
542 }
543 
544 /// \brief Initialize an instance of the plugin object
545 ///
546 /// This methods initializes the plugin object, and is called for
547 /// every movie that gets played. This is where the movie playing
548 /// specific initialization goes.
549 NPBool
init(NPWindow * aWindow)550 nsPluginInstance::init(NPWindow* aWindow)
551 {
552     if(!aWindow) {
553         gnash::log_error("%s: ERROR: Window handle was bogus!", __PRETTY_FUNCTION__);
554         return FALSE;
555     } else {
556 #if GNASH_PLUGIN_DEBUG > 1
557         std::cout << "X origin: = " << aWindow->x
558             << ", Y Origin = " << aWindow->y
559             << ", Width = " << aWindow->width
560             << ", Height = " << aWindow->height
561             << ", WindowID = " << aWindow->window
562             << ", this = " << static_cast<void*>(this) << std::endl;
563 #endif
564     }
565 
566     return TRUE;
567 }
568 
569 /// \brief Shutdown an instantiated object
570 ///
571 /// This shuts down an object, and is called for every movie that gets
572 /// played. This is where the movie playing specific shutdown code
573 /// goes.
574 void
shut()575 nsPluginInstance::shut()
576 {
577     gnash::log_debug("Gnash plugin shutting down");
578 
579     if (_streamfd != -1) {
580         if (close(_streamfd) == -1) {
581             perror("closing _streamfd");
582         } else {
583             _streamfd = -1;
584         }
585     }
586 
587 }
588 /// \brief Set the window to be used to render in
589 ///
590 /// This sets up the window the plugin is supposed to render
591 /// into. This calls passes in various information used by the plugin
592 /// to setup the window. This may get called multiple times by each
593 /// instantiated object, so it can't do much but window specific
594 /// setup here.
595 NPError
SetWindow(NPWindow * aWindow)596 nsPluginInstance::SetWindow(NPWindow* aWindow)
597 {
598     if(!aWindow) {
599         gnash::log_error(std::string(__FUNCTION__) + ": ERROR: Window handle was bogus!");
600         return NPERR_INVALID_PARAM;
601 #if 0
602     } else {
603         gnash::log_debug("%s: X origin = %d, Y Origin = %d, Width = %d,"
604             " Height = %d, WindowID = %p, this = %p",
605             __FUNCTION__,
606             aWindow->x, aWindow->y, aWindow->width, aWindow->height,
607             aWindow->window, this);
608 #endif
609     }
610 
611     if (_window) {
612         return NPERR_GENERIC_ERROR;
613     }
614 
615     _width = aWindow->width;
616     _height = aWindow->height;
617 
618     _window = reinterpret_cast<Window> (aWindow->window);
619 
620     // When testing the interface to the plugin, don't start the player
621     // as a debug client "nc -l 1111" is used instead.
622     if (!_childpid && !_swf_url.empty()) {
623         return startProc();
624     }
625 
626     return NPERR_NO_ERROR;
627 }
628 
629 NPError
GetValue(NPPVariable aVariable,void * aValue)630 nsPluginInstance::GetValue(NPPVariable aVariable, void *aValue)
631 {
632 
633     if (aVariable == NPPVpluginScriptableNPObject) {
634         if (_scriptObject) {
635             void **v = (void **)aValue;
636             NPN_RetainObject(_scriptObject);
637             *v = _scriptObject;
638         } else {
639             gnash::log_debug("_scriptObject is not assigned");
640         }
641     }
642 
643     // log_debug("SCRIPT OBJECT getValue: %x, ns: %x", (void *)_scriptObject, (void *)this);
644 
645     return NS_PluginGetValue(aVariable, aValue);
646 }
647 
648 /// \brief Open a new data stream
649 ///
650 /// Opens a new incoming data stream, which is the flash movie we want
651 /// to play.
652 /// A URL can be pretty ugly, like in this example:
653 /// http://www.shockwave.com/swf/navbar/navbar_sw.swf?atomfilms=http%3a//www.atomfilms.com/af/home/&shockwave=http%3a//www.shockwave.com&gameblast=http%3a//gameblast.shockwave.com/gb/gbHome.jsp&known=0
654 /// ../flash/gui.swf?ip_addr=foobar.com&ip_port=3660&show_cursor=true&path_prefix=../flash/&trapallkeys=true"
655 ///
656 
657 NPError
NewStream(NPMIMEType,NPStream * stream,NPBool,uint16_t *)658 nsPluginInstance::NewStream(NPMIMEType /*type*/, NPStream* stream,
659                             NPBool /*seekable*/, uint16_t* /*stype*/)
660 {
661     // gnash::log_debug("%s: %x", __PRETTY_FUNCTION__, (void *)this);
662 
663     if (_childpid) {
664         // Apparently the child process has already been started for this
665         // plugin instance. It is puzzling that this method gets called
666         // again. Starting a new process for the same movie will cause
667         // problems later, so we'll stop here.
668         return NPERR_GENERIC_ERROR;
669     }
670     _swf_url = stream->url;
671 
672     if (!_swf_url.empty() && _window) {
673         return startProc();
674     }
675 
676     return NPERR_NO_ERROR;
677 }
678 
679 /// \brief Destroy the data stream we've been reading.
680 NPError
DestroyStream(NPStream *,NPError)681 nsPluginInstance::DestroyStream(NPStream* /*stream*/, NPError /*reason*/)
682 {
683     if (_streamfd != -1) {
684         if (close(_streamfd) == -1) {
685             perror("closing _streamfd");
686         } else {
687             _streamfd = -1;
688         }
689     }
690 
691     return NPERR_NO_ERROR;
692 }
693 
694 /// \brief Return how many bytes we can read into the buffer
695 int32_t
WriteReady(NPStream *)696 nsPluginInstance::WriteReady(NPStream* /* stream */ )
697 {
698     //gnash::log_debug("Stream for %s is ready", stream->url);
699     if ( _streamfd != -1 ) {
700 	return 1024;
701     } else {
702 	return 0;
703     }
704 }
705 
706 /// \brief Read the data stream from Mozilla/Firefox
707 ///
708 int32_t
Write(NPStream *,int32_t,int32_t len,void * buffer)709 nsPluginInstance::Write(NPStream* /*stream*/, int32_t /*offset*/, int32_t len,
710         void* buffer)
711 {
712     int written = write(_streamfd, buffer, len);
713     return written;
714 }
715 
716 bool
handlePlayerRequestsWrapper(GIOChannel * iochan,GIOCondition cond,nsPluginInstance * plugin)717 nsPluginInstance::handlePlayerRequestsWrapper(GIOChannel* iochan,
718         GIOCondition cond, nsPluginInstance* plugin)
719 {
720     return plugin->handlePlayerRequests(iochan, cond);
721 }
722 
723 bool
handlePlayerRequests(GIOChannel * iochan,GIOCondition cond)724 nsPluginInstance::handlePlayerRequests(GIOChannel* iochan, GIOCondition cond)
725 {
726     // gnash::log_debug("%s: %d: %x", __PRETTY_FUNCTION__, __LINE__, (void *)this);
727 
728     if ( cond & G_IO_HUP ) {
729         gnash::log_debug("Player control socket hang up");
730         return false;
731     }
732 
733     assert(cond & G_IO_IN);
734 
735     gnash::log_debug("Checking player requests on FD #%d",
736               g_io_channel_unix_get_fd(iochan));
737 
738     const size_t buf_size = 1;
739     gchar buffer[buf_size];
740 
741     do {
742         GError* error = nullptr;
743         gsize bytes_read = 0;
744 
745         GIOStatus status = g_io_channel_read_chars(iochan, buffer, buf_size,
746                                                    &bytes_read, &error);
747 
748         switch (status) {
749           case G_IO_STATUS_ERROR:
750               gnash::log_error("error reading request line: %s",
751                                error ? error->message : "unspecified error");
752               g_error_free(error);
753               return false;
754           case G_IO_STATUS_EOF:
755               gnash::log_error("EOF (error: %s)",
756                                error ? error->message : "unspecified error");
757               g_error_free(error);
758               return false;
759           case G_IO_STATUS_AGAIN:
760               gnash::log_debug("read again");
761               continue;
762           case G_IO_STATUS_NORMAL:
763               // process request
764               _requestbuf.append(buffer, buffer+bytes_read);
765 #if 0
766               gnash::log_debug("Normal read: %s", std::string(buffer, buffer+bytes_read));
767 #endif
768               break;
769           default:
770               gnash::log_error("Abnormal status!");
771               return false;
772         }
773     } while (g_io_channel_get_buffer_condition(iochan) & G_IO_IN);
774 
775     // process request..
776     processPlayerRequest();
777 
778     return true;
779 }
780 
781 // This GIOFunc handler removes the source from the mainloop.
782 gboolean
remove_handler(GIOChannel *,GIOCondition,gpointer)783 remove_handler(GIOChannel*, GIOCondition, gpointer)
784 {
785     return FALSE;
786 }
787 
788 
789 // There may be multiple Invoke messages in a single packet, so each
790 // packet needs to be broken up into separate messages to be parsed.
791 bool
processPlayerRequest()792 nsPluginInstance::processPlayerRequest()
793 {
794 #if 0
795      gnash::log_debug(__PRETTY_FUNCTION__);
796 
797      log_debug("SCRIPT OBJECT %d: %x", __LINE__, this->getScriptObject());
798 #endif
799 
800     if ( _requestbuf.size() < 4 ) {
801         gnash::log_error("Invalid player request (too short): %s", _requestbuf);
802         return false;
803     }
804 
805 
806     std::string& packet = _requestbuf;
807     do {
808         boost::trim_left(packet);
809         if (packet.empty()) {
810             return false;
811         }
812 
813         std::string term = "</invoke>";
814         std::string::size_type pos = packet.find(term);
815         // no terminator, bad message
816         if (pos == std::string::npos) {
817             gnash::log_debug("Incomplete Invoke message. Probably a fragment.");
818             return false;
819         }
820 
821         // Extract a message from the packet
822         std::string msg = packet.substr(0, pos + term.size());
823         std::shared_ptr<plugin::ExternalInterface::invoke_t> invoke =
824             plugin::ExternalInterface::parseInvoke(_scriptObject, msg);
825 
826         // drop the parsed message from the packet
827         packet.erase(0, msg.size());
828 
829         if (!invoke) {
830             log_error("Failed to parse invoke message: %s", msg);
831             return false;
832         }
833 
834         if (!invoke->name.empty()) {
835             gnash::log_debug("Requested method is: %s", invoke->name);
836         } else {
837             gnash::log_error("Invoke request missing a name to invoke.");
838             continue;
839         }
840 
841         if (invoke->name == "getURL") {
842 
843             assert(invoke->args.size() > 1);
844 
845             // The first argument is the URL string.
846             std::string url = NPVariantToString(invoke->args[0].get());
847 #if 0
848             gnash::log_debug("Got a getURL() request: %s", url);
849 #endif
850             // The second is the method, namely GET or POST.
851             std::string op = NPVariantToString(invoke->args[1].get());
852             // The third is the optional target, which is something like
853             // _blank or _self.
854             std::string target;
855 
856             // The fourth is the optional data. If there is data, the target
857             // field is always set so this argument is on the correct index.
858             std::string data;
859 
860             if (invoke->args.size() >= 3) {
861                 target = NPVariantToString(invoke->args[2].get());
862             }
863 
864             // An empty target defaults to "_self"
865             // This is _required_ for chromium,
866             // see https://savannah.gnu.org/bugs/?32425
867             if ( target.empty() ) target = "_self";
868 
869             if (invoke->args.size() == 4) {
870                 data = NPVariantToString(invoke->args[3].get());
871             }
872             if (op == "GET") {
873                 gnash::log_debug("Asked to getURL '%s' in target %s", url,
874                                  target);
875                 NPN_GetURL(_instance, url.c_str(), target.c_str());
876             } else if (op == "POST") {
877                 gnash::log_debug("Asked to postURL '%s' this data %s", url,
878                                  data);
879                 NPN_PostURL(_instance, url.c_str(), target.c_str(), data.size(),
880                             data.c_str(), false);
881             } else {
882                 log_error("Unexpected op in getURL (expected POST or GET).");
883             }
884 
885             continue;
886         } else if (invoke->name == "fsCommand") {
887 
888             assert(invoke->args.size() > 1);
889             std::string command = NPVariantToString(invoke->args[0].get());
890             std::string arg = NPVariantToString(invoke->args[1].get());
891             std::string name = _name;
892             std::stringstream jsurl;
893             jsurl << "javascript:" << name << "_DoFSCommand('" << command
894                   << "','" << arg <<"')";
895 
896             // TODO: check if _self is a good target for this
897             static const char* tgt = "_self";
898 
899             gnash::log_debug("Calling NPN_GetURL(%s, %s)",
900                              jsurl.str(), tgt);
901 
902             NPN_GetURL(_instance, jsurl.str().c_str(), tgt);
903             continue;
904         } else if (invoke->name == "addMethod") {
905 
906             assert(!invoke->args.empty());
907 
908             if (!HasScripting()) {
909                LOG_ONCE(log_debug("Ignoring addMethod, no scripting."));
910                continue;
911             }
912             // Make this flash function accessible to Javascript. The
913             // actual callback lives in libcore/movie_root, but it
914             // needs to be on the list of supported remote methods so
915             // it can be called by Javascript.
916             std::string method = NPVariantToString(invoke->args[0].get());
917             NPIdentifier id = NPN_GetStringIdentifier(method.c_str());
918             // log_debug("SCRIPT OBJECT addMethod: %x, %s", (void *)_scriptObject, method);
919             this->getScriptObject()->AddMethod(id, remoteCallback);
920             continue;
921         }
922 
923         if (!HasScripting()) {
924            LOG_ONCE(log_debug("Ignoring invoke, no scripting."));
925            continue;
926         }
927 
928         NPVariant result;
929         VOID_TO_NPVARIANT(result);
930         bool invokeResult = false;
931 
932         // This is the player invoking a method in Javascript
933         if (!invoke->name.empty() && !invoke->args.empty()) {
934             //Convert the as_value argument to NPVariant
935             const size_t count = invoke->args.size() - 1;
936             std::unique_ptr<NPVariant[]> args(new NPVariant[count]);
937             //Skip the first argument
938             for (size_t i = 0; i < count; ++i) {
939                 invoke->args[i+1].copy(args[i]);
940             }
941 
942             NPIdentifier id = NPN_GetStringIdentifier(invoke->name.c_str());
943             gnash::log_debug("Invoking JavaScript method %s", invoke->name);
944             NPObject* windowObject;
945             NPN_GetValue(_instance, NPNVWindowNPObject, &windowObject);
946             invokeResult=NPN_Invoke(_instance, windowObject, id, args.get(),
947                                     count, &result);
948             NPN_ReleaseObject(windowObject);
949         }
950         // We got a result from invoking the Javascript method
951         std::stringstream ss;
952 
953         ss << plugin::ExternalInterface::convertNPVariant(&result);
954         NPN_ReleaseVariantValue(&result);
955         size_t ret = _scriptObject->writePlayer(ss.str());
956         if (ret != ss.str().size()) {
957             log_error("Couldn't write the response to Gnash, network problems.");
958             return false;
959         }
960     } while (!packet.empty());
961 
962     return true;
963 }
964 
965 std::string
getGnashExecutable()966 getGnashExecutable()
967 {
968     std::string procname;
969     bool process_found = false;
970     struct stat procstats;
971 
972     char *gnash_env = std::getenv("GNASH_PLAYER");
973 
974     if (gnash_env) {
975         procname = gnash_env;
976         process_found = (0 == stat(procname.c_str(), &procstats));
977         if (!process_found) {
978             gnash::log_error("Invalid path to gnash executable: ");
979             return "";
980         }
981     }
982 
983     if (!process_found) {
984         procname = GNASHBINDIR "/gtk-gnash";
985         process_found = (0 == stat(procname.c_str(), &procstats));
986     }
987     if (!process_found) {
988         procname = GNASHBINDIR "/qt4-gnash";
989         process_found = (0 == stat(procname.c_str(), &procstats));
990     }
991 
992     if (!process_found) {
993         gnash::log_error(std::string("Unable to find Gnash in ") + GNASHBINDIR);
994         return "";
995     }
996 
997     return procname;
998 }
999 
1000 void
create_standalone_launcher(const std::string & page_url,const std::string & swf_url,const std::map<std::string,std::string> & params)1001 create_standalone_launcher(const std::string& page_url, const std::string& swf_url,
1002                            const std::map<std::string, std::string>& params)
1003 {
1004 #ifdef CREATE_STANDALONE_GNASH_LAUNCHER
1005     if (!createSaLauncher) {
1006         return;
1007     }
1008 
1009     char debugname[] = "/tmp/gnash-debug-XXXXXX.sh";
1010     boost::iostreams::file_descriptor_sink fdsink = getfdsink(debugname);
1011 #if BOOST_VERSION >= 104400
1012     if (fdsink.handle() == -1) {
1013         gnash::log_error("Failed to create sink: %s", debugname);
1014         return;
1015     }
1016 #endif
1017     boost::iostreams::stream<boost::iostreams::file_descriptor_sink>
1018         saLauncher (fdsink);
1019 
1020     if (!saLauncher) {
1021         gnash::log_error("Failed to open new file for standalone launcher: %s", debugname);
1022         return;
1023     }
1024 
1025     saLauncher << "#!/bin/sh" << std::endl
1026                << getGnashExecutable() << " ";
1027 
1028     if (!page_url.empty()) {
1029         saLauncher << "-U '" << page_url << "' ";
1030     }
1031     if (!cookiefile.empty()) {
1032         saLauncher << "-C " << cookiefile << " ";
1033     }
1034     for (std::map<std::string,std::string>::const_iterator it = params.begin(),
1035         itEnd = params.end(); it != itEnd; ++it) {
1036         const std::string& nam = it->first;
1037         const std::string& val = it->second;
1038         saLauncher << "-P '"
1039                    << boost::algorithm::replace_all_copy(nam, "'", "'\\''")
1040                    << "="
1041                    << boost::algorithm::replace_all_copy(val, "'", "'\\''")
1042                    << "' ";
1043     }
1044 
1045     saLauncher << "'" << swf_url << "' "
1046                << "$@"       // allow caller to pass any additional argument
1047                << std::endl;
1048 
1049     saLauncher.close();
1050     fdsink.close();
1051 #endif
1052 }
1053 
1054 std::string
getDocumentProp(const std::string & propname) const1055 nsPluginInstance::getDocumentProp(const std::string& propname) const
1056 {
1057     std::string rv;
1058 
1059     if (!HasScripting()) {
1060         LOG_ONCE( gnash::log_debug("Browser doesn't support scripting") );
1061         return rv;
1062     }
1063 
1064     NPObject* windowobj;
1065     NPError err = NPN_GetValue(_instance, NPNVWindowNPObject, &windowobj);
1066     if (err != NPERR_NO_ERROR || !windowobj) {
1067         return rv;
1068     }
1069 
1070     std::shared_ptr<NPObject> window_obj(windowobj, NPN_ReleaseObject);
1071 
1072     NPIdentifier doc_id = NPN_GetStringIdentifier("document");
1073 
1074     NPVariant docvar;
1075     if(! NPN_GetProperty(_instance, windowobj, doc_id, &docvar) ) {
1076         return rv;
1077     }
1078 
1079     std::shared_ptr<NPVariant> doc_var(&docvar, NPN_ReleaseVariantValue);
1080 
1081     if (!NPVARIANT_IS_OBJECT(docvar)) {
1082         return rv;
1083     }
1084 
1085     NPObject* doc_obj = NPVARIANT_TO_OBJECT(docvar);
1086 
1087     NPIdentifier prop_id = NPN_GetStringIdentifier(propname.c_str());
1088 
1089     NPVariant propvar;
1090     if (!NPN_GetProperty(_instance, doc_obj, prop_id, &propvar)) {
1091         return rv;
1092     }
1093 
1094     std::shared_ptr<NPVariant> prop_var(&propvar, NPN_ReleaseVariantValue);
1095 
1096     if (!NPVARIANT_IS_STRING(propvar)) {
1097         return rv;
1098     }
1099 
1100     const NPString& prop_str = NPVARIANT_TO_STRING(propvar);
1101 
1102     rv = NPStringToString(prop_str);
1103     return rv;
1104 }
1105 
1106 
1107 
1108 void
setupCookies(const std::string & pageurl)1109 nsPluginInstance::setupCookies(const std::string& pageurl)
1110 {
1111     // Cookie appear to drop anything past the domain, so we strip
1112     // that off.
1113     std::string::size_type pos;
1114     pos = pageurl.find("/", pageurl.find("//", 0) + 2) + 1;
1115     std::string url = pageurl.substr(0, pos);
1116 
1117     std::string ncookie;
1118 
1119     char *cookie = nullptr;
1120     uint32_t length = 0;
1121 
1122     NPError rv = NPERR_GENERIC_ERROR;
1123 #if NPAPI_VERSION != 190
1124     if (NPNFuncs.getvalueforurl) {
1125         rv = NPN_GetValueForURL(_instance, NPNURLVCookie, url.c_str(),
1126                                 &cookie, &length);
1127     } else {
1128         LOG_ONCE( gnash::log_debug("Browser doesn't support getvalueforurl") );
1129     }
1130 #endif
1131 
1132     // Firefox does not (always) return the cookies that are associated
1133     // with a domain name through GetValueForURL.
1134     if (rv == NPERR_GENERIC_ERROR) {
1135         log_debug("Trying window.document.cookie for cookies");
1136         ncookie = getDocumentProp("cookie");
1137     }
1138 
1139     if (cookie) {
1140         ncookie.assign(cookie, length);
1141         NPN_MemFree(cookie);
1142     }
1143 
1144     if (ncookie.empty()) {
1145         gnash::log_debug("No stored Cookie for %s", url);
1146         cookiefile.clear();
1147         return;
1148     }
1149 
1150     gnash::log_debug("The Cookie for %s is %s", url, ncookie);
1151     char mkstemplate[] = "/tmp/gnash-cookie.XXXXXX";
1152     boost::iostreams::file_descriptor_sink fdsink = getfdsink(mkstemplate);
1153 #if BOOST_VERSION >= 104400
1154     if (fdsink.handle() == -1) {
1155         gnash::log_error("Failed to create sink: %s", mkstemplate);
1156         return;
1157     }
1158 #endif
1159     cookiefile = mkstemplate;
1160     boost::iostreams::stream<boost::iostreams::file_descriptor_sink>
1161         cookiestream (fdsink);
1162 
1163     // Firefox provides cookies in the following format:
1164     //
1165     // cookie1=value1;cookie2=value2;cookie3=value3
1166     //
1167     // Whereas libcurl expects cookies in the following format:
1168     //
1169     // Set-Cookie: cookie1=value1;
1170     // Set-Cookie: cookie2=value2;
1171 
1172     typedef boost::char_separator<char> char_sep;
1173     typedef boost::tokenizer<char_sep> tokenizer;
1174     tokenizer tok(ncookie, char_sep(";"));
1175 
1176     for (tokenizer::iterator it=tok.begin(); it != tok.end(); ++it) {
1177         cookiestream << "Set-Cookie: " << *it << std::endl;
1178     }
1179 
1180     cookiestream.close();
1181     fdsink.close();
1182     gnash::log_debug("Cookiefile is %s", cookiefile);
1183 }
1184 
1185 void
setupProxy(const std::string & url)1186 nsPluginInstance::setupProxy(const std::string& url)
1187 {
1188     // In pre xulrunner 1.9, (Firefox 3.1) this function does not exist,
1189     // so we can't use it to read the proxy information.
1190 #if NPAPI_VERSION != 190
1191     if (!NPNFuncs.getvalueforurl) return;
1192 #endif
1193 
1194     char *proxy = nullptr;
1195     uint32_t length = 0;
1196 #if NPAPI_VERSION != 190
1197     NPN_GetValueForURL(_instance, NPNURLVProxy, url.c_str(),
1198                        &proxy, &length);
1199 #endif
1200     if (!proxy) {
1201         gnash::log_debug("No proxy setting for %s", url);
1202         return;
1203     }
1204 
1205     std::string nproxy (proxy, length);
1206     NPN_MemFree(proxy);
1207 
1208     gnash::log_debug("Proxy setting for %s is %s", url, nproxy);
1209 
1210     std::vector<std::string> parts;
1211     boost::split(parts, nproxy,
1212         boost::is_any_of(" "), boost::token_compress_on);
1213     if ( parts[0] == "DIRECT" ) {
1214         // no proxy
1215     }
1216     else if ( parts[0] == "PROXY" ) {
1217         // authenticated proxies: need to specify proxy credentials through
1218         // http_proxy env var set to user:pass@proxy:port. If set, it won't be
1219         // overridden. See https://savannah.gnu.org/bugs/?26713
1220         char *http_proxy=getenv("http_proxy");
1221         if (!http_proxy) {
1222             gnash::log_debug("Setting http_proxy to %s", parts[1].c_str());
1223             if (setenv("http_proxy", parts[1].c_str(), 1) < 0) {
1224                 gnash::log_error(
1225                     "Couldn't set environment variable http_proxy to %s",
1226                     parts[1].c_str());
1227             }
1228         } else {
1229             gnash::log_debug("http_proxy already set to %s", http_proxy);
1230         }
1231     }
1232     else {
1233         gnash::log_error("Unknown proxy type: %s", nproxy);
1234     }
1235 
1236 }
1237 
1238 std::vector<std::string>
getCmdLine(int hostfd,int controlfd)1239 nsPluginInstance::getCmdLine(int hostfd, int controlfd)
1240 {
1241     std::vector<std::string> arg_vec;
1242 
1243     std::string cmd = getGnashExecutable();
1244     if (cmd.empty()) {
1245         gnash::log_error("Failed to locate the Gnash executable!");
1246         return arg_vec;
1247     }
1248     arg_vec.push_back(cmd);
1249 
1250     arg_vec.push_back("-u");
1251     arg_vec.push_back(_swf_url);
1252 
1253     std::string pageurl = getCurrentPageURL();
1254     if (pageurl.empty()) {
1255         gnash::log_error("Could not get current page URL!");
1256     } else {
1257         arg_vec.push_back("-U");
1258         arg_vec.push_back(pageurl);
1259     }
1260 
1261     setupCookies(pageurl);
1262     setupProxy(pageurl);
1263 
1264     std::stringstream pars;
1265     pars << "-x "  <<  _window          // X window ID to render into
1266          << " -j " << _width            // Width of window
1267          << " -k " << _height;           // Height of window
1268 #if GNASH_PLUGIN_DEBUG > 1
1269     pars << " -vv ";
1270 #endif
1271     if ((hostfd > 0) && (controlfd)) {
1272         pars << " -F " << hostfd            // Socket to send commands to
1273              << ":"    << controlfd;        // Socket determining lifespan
1274     }
1275     if (!cookiefile.empty()) {
1276         pars << " -C " << cookiefile;
1277     }
1278     std::string pars_str = pars.str();
1279     typedef boost::char_separator<char> char_sep;
1280     boost::tokenizer<char_sep> tok(pars_str, char_sep(" "));
1281     arg_vec.insert(arg_vec.end(), tok.begin(), tok.end());
1282 
1283     for (std::map<std::string,std::string>::const_iterator it = _params.begin(),
1284         itEnd = _params.end(); it != itEnd; ++it) {
1285         const std::string& nam = it->first;
1286         const std::string& val = it->second;
1287         arg_vec.push_back("-P");
1288         arg_vec.push_back(nam + "=" + val);
1289     }
1290     arg_vec.push_back("-");
1291 
1292     create_standalone_launcher(pageurl, _swf_url, _params);
1293 
1294     return arg_vec;
1295 }
1296 
1297 template <std::size_t N>
1298 void
close_fds(const int (& except)[N])1299 close_fds(const int (& except)[N])
1300 {
1301     // Rather than close all the thousands of possible file
1302     // descriptors, we start after stderr and keep closing higher numbers
1303     // until we encounter ten fd's in a row that
1304     // aren't open. This will tend to close most fd's in most programs.
1305     int numfailed = 0, closed = 0;
1306     for (int anfd = fileno(stderr)+1; numfailed < 10; anfd++) {
1307         if (std::find(except, except+N, anfd) != except+N) {
1308             continue;
1309         }
1310         if (close(anfd) < 0) {
1311             numfailed++;
1312         } else {
1313             numfailed = 0;
1314             closed++;
1315         }
1316     }
1317     gnash::log_debug("Closed %d files.", closed);
1318 }
1319 
1320 void
wait_for_gdb()1321 wait_for_gdb()
1322 {
1323     if (!waitforgdb) {
1324         return;
1325     }
1326 
1327     // For debugging the plugin (GNASH_OPTIONS=waitforgdb)
1328     // Block here until gdb is attached and sets waitforgdb to false.
1329 
1330     std::cout << std::endl << "  Attach GDB to PID " << getpid()
1331               << " to debug!" << std::endl
1332               << "  This thread will block until then!" << std::endl
1333               << "  Once blocked here, you can set other breakpoints."
1334               << std::endl
1335               << "  Do a \"set variable waitforgdb=$false\" to continue"
1336               << std::endl << std::endl;
1337 
1338     while (waitforgdb) {
1339         sleep(1);
1340     }
1341 }
1342 
1343 void
setupIOChannel(int fd,GIOFunc handler,GIOCondition signals)1344 nsPluginInstance::setupIOChannel(int fd, GIOFunc handler, GIOCondition signals)
1345 {
1346     GIOChannel* ichan = g_io_channel_unix_new(fd);
1347     g_io_channel_set_close_on_unref(ichan, true);
1348 
1349     gnash::log_debug("New IO Channel for fd #%d",
1350                      g_io_channel_unix_get_fd(ichan));
1351     g_io_add_watch(ichan, signals, handler, this);
1352     g_io_channel_unref(ichan);
1353 }
1354 
1355 
1356 NPError
startProc()1357 nsPluginInstance::startProc()
1358 {
1359 
1360     int p2c_pipe[2];
1361     int c2p_pipe[2];
1362     int p2c_controlpipe[2];
1363 
1364     int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, p2c_pipe);
1365     if (ret == -1) {
1366         gnash::log_error("socketpair(p2c) failed: %s", strerror(errno));
1367         return NPERR_GENERIC_ERROR;
1368     }
1369     _streamfd = p2c_pipe[1];
1370 
1371     ret = socketpair(AF_UNIX, SOCK_STREAM, 0, c2p_pipe);
1372     if (ret == -1) {
1373         gnash::log_error("socketpair(c2p) failed: %s", strerror(errno));
1374         return NPERR_GENERIC_ERROR;
1375     }
1376 
1377     ret = socketpair(AF_UNIX, SOCK_STREAM, 0, p2c_controlpipe);
1378     if (ret == -1) {
1379         gnash::log_error("socketpair(control) failed: %s", strerror(errno));
1380         return NPERR_GENERIC_ERROR;
1381     }
1382 
1383     if (HasScripting() && _scriptObject) {
1384         _scriptObject->setControlFD(p2c_controlpipe[1]);
1385         _scriptObject->setHostFD(c2p_pipe[0]);
1386     }
1387 
1388     // Setup the command line for starting Gnash
1389 
1390     std::vector<std::string> arg_vec = getCmdLine(c2p_pipe[1],
1391                                                   p2c_controlpipe[0]);
1392 
1393     if (arg_vec.empty()) {
1394         gnash::log_error("Failed to obtain command line parameters.");
1395         return NPERR_GENERIC_ERROR;
1396     }
1397 
1398     std::vector<const char*> args;
1399 
1400     std::transform(arg_vec.begin(), arg_vec.end(), std::back_inserter(args),
1401                    std::mem_fun_ref(&std::string::c_str));
1402     args.push_back(nullptr);
1403 
1404     // Argument List prepared, now fork(), close file descriptors and execv()
1405 
1406     _childpid = fork();
1407 
1408     // If the fork failed, childpid is -1. So print out an error message.
1409     if (_childpid == -1) {
1410         gnash::log_error("fork() failed: %s", strerror(errno));
1411         return NPERR_OUT_OF_MEMORY_ERROR;
1412     }
1413 
1414     // If we are the parent and fork() worked, childpid is a positive integer.
1415     if (_childpid > 0) {
1416         // Close the child's end of the pipes.
1417         int fdstoclose[] = {p2c_controlpipe[0], p2c_pipe[0], c2p_pipe[1]};
1418         int num_failed = std::count_if(fdstoclose, fdstoclose+3, close);
1419 
1420         if (num_failed > 0) {
1421             // this is not really a fatal error, so continue best as we can
1422             gnash::log_error("%d fds failed to close: %s (error ignored).",
1423                              num_failed, strerror(errno));
1424         }
1425 
1426         gnash::log_debug("Forked successfully, child process PID is %d",
1427                          _childpid);
1428 
1429         if (!cookiefile.empty()) {
1430             cookiemap.insert(std::make_pair(_childpid, cookiefile));
1431             gnash::log_debug("Pid %d associated with cookiefile %s",
1432                 _childpid, cookiefile);
1433         }
1434 
1435         setupIOChannel(c2p_pipe[0], (GIOFunc)handlePlayerRequestsWrapper,
1436                                     (GIOCondition)(G_IO_IN|G_IO_HUP));
1437 
1438         setupIOChannel(p2c_controlpipe[1], remove_handler, G_IO_HUP);
1439 
1440         return NPERR_NO_ERROR;
1441     }
1442 
1443     // This is the child scope.
1444 
1445     // FF3 uses jemalloc and it has problems after the fork(), do NOT
1446     // use memory functions (malloc()/free()/new/delete) after the fork()
1447     // in the child thread process
1448     ret = close (p2c_pipe[1]);
1449 
1450     // close standard input and direct read-fd1 to standard input
1451     ret = dup2 (p2c_pipe[0], fileno(stdin));
1452 
1453     if (ret == -1) {
1454         gnash::log_error("dup2() failed: %s", strerror(errno));
1455     }
1456 
1457     // Close all of the browser's file descriptors that we just inherited
1458     // (including p2c_pipe[0] that we just dup'd to fd 0), but obviously
1459     // not the file descriptors that we want the child to use.
1460     int dontclose[] = {c2p_pipe[1], c2p_pipe[0], p2c_controlpipe[0]};
1461     close_fds(dontclose);
1462 
1463     // Start the desired executable and go away.
1464 
1465     gnash::log_debug("Starting process: %s", boost::algorithm::join(arg_vec, " "));
1466 
1467     wait_for_gdb();
1468 
1469     execv(args[0], const_cast<char**>(&args.front()));
1470 
1471     // if execv returns, an error has occurred.
1472     perror("executing standalone gnash");
1473 
1474     exit (-1);
1475 }
1476 
1477 
1478 
1479 
1480 
1481 std::string
getCurrentPageURL() const1482 nsPluginInstance::getCurrentPageURL() const
1483 {
1484     // Return:
1485     //  window.document.baseURI
1486     //
1487     // Was (bogus):
1488     //  window.document.location.href
1489 
1490     return getDocumentProp("baseURI");
1491 }
1492 
1493 void
processLog_error(const boost::format & fmt)1494 processLog_error(const boost::format& fmt)
1495 {
1496     std::cerr << "ERROR: " << fmt.str() << std::endl;
1497 }
1498 
1499 #if GNASH_PLUGIN_DEBUG > 1
1500 void
processLog_debug(const boost::format & fmt)1501 processLog_debug(const boost::format& fmt)
1502 {
1503     std::cout << "DEBUG: " << fmt.str() << std::endl;
1504 }
1505 
1506 void
processLog_trace(const boost::format & fmt)1507 processLog_trace(const boost::format& fmt)
1508 {
1509     std::cout << "TRACE: " << fmt.str() << std::endl;
1510 }
1511 #else
1512 void
processLog_debug(const boost::format &)1513 processLog_debug(const boost::format& /* fmt */)
1514 { /* do nothing */ }
1515 
1516 void
processLog_trace(const boost::format &)1517 processLog_trace(const boost::format& /* fmt */)
1518 { /* do nothing */ }
1519 #endif
1520 
1521 } // end of gnash namespace
1522 
1523 // Local Variables:
1524 // mode: C++
1525 // indent-tabs-mode: nil
1526 // End:
1527