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