2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
3 // 2011 Free Software Foundation, Inc
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.
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.
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
21 #include "gnashconfig.h"
24 #include <cstdlib> // getenv
25 #include <stdlib.h> // putenv
26 #include <sys/types.h>
27 #if defined(HAVE_WINSOCK_H) && !defined(__OS2__)
28 # include <winsock2.h>
31 # include <netinet/in.h>
32 # include <arpa/inet.h>
34 # include <sys/socket.h>
37 #include <boost/format.hpp>
38 #include <boost/algorithm/string/replace.hpp>
40 #define MIME_TYPES_HANDLED "application/x-shockwave-flash"
41 // The name must be this value to get flash movies that check the
42 // plugin version to load.
43 #define PLUGIN_NAME "Shockwave Flash"
44 #define MIME_TYPES_DESCRIPTION MIME_TYPES_HANDLED":swf:"PLUGIN_NAME
46 // Some javascript plugin detectors use the description
47 // to decide the flash version to display. They expect the
48 // form (major version).(minor version) r(revision).
50 #define FLASH_VERSION DEFAULT_FLASH_MAJOR_VERSION"."\
51 DEFAULT_FLASH_MINOR_VERSION" r"DEFAULT_FLASH_REV_NUMBER"."
53 #define PLUGIN_DESCRIPTION \
54 "Shockwave Flash "FLASH_VERSION"<br>Gnash "VERSION", the GNU SWF Player. \
55 Copyright (C) 2006, 2007, 2008, 2009, 2010 \
56 <a href=\"http://www.fsf.org\">Free \
57 Software Foundation</a>, Inc. <br> \
58 Gnash comes with NO WARRANTY, to the extent permitted by law. \
59 You may redistribute copies of Gnash under the terms of the \
60 <a href=\"http://www.gnu.org/licenses/gpl.html\">GNU General Public \
61 License</a>. For more information about Gnash, see <a \
62 href=\"http://www.gnu.org/software/gnash/\"> \
63 http://www.gnu.org/software/gnash</a>. \
65 Compatible Shockwave Flash "FLASH_VERSION
68 #include "GnashSystemIOHeaders.h"
69 #include "StringPredicates.h"
71 #include "callbacks.h"
73 #include "npruntime.h"
74 #include "npfunctions.h"
75 #include "GnashNPVariant.h"
77 #include <boost/tokenizer.hpp>
78 #include <boost/algorithm/string/join.hpp>
79 #include <boost/algorithm/string/split.hpp>
80 #include <boost/algorithm/string/classification.hpp>
81 #include <boost/format.hpp>
82 #include <sys/param.h>
87 #include <sys/types.h>
100 #define PATH_MAX 1024
103 // Macro to prevent repeated logging calls for the same
105 #define LOG_ONCE(x) { \
106 static bool warned = false; \
107 if (!warned) { warned = true; x; } \
110 // For scriptable plugin support
111 #include "pluginScriptObject.h"
113 extern NPNetscapeFuncs NPNFuncs
;
114 extern NPPluginFuncs NPPFuncs
;
117 NPBool plugInitialized
= FALSE
;
120 /// \brief Return the MIME Type description for this plugin.
122 NPP_GetMIMEDescription(void)
124 return const_cast<char *>(MIME_TYPES_DESCRIPTION
);
127 static bool waitforgdb
= false;
128 static bool createSaLauncher
= false;
130 static const char* getPluginDescription();
131 // void GnashLogDebug(const std::string& msg);
132 // void GnashLogError(const std::string& msg);
135 getPluginDescription()
137 static const char* desc
= NULL
;
139 desc
= std::getenv("GNASH_PLUGIN_DESCRIPTION");
140 if (desc
== NULL
) desc
= PLUGIN_DESCRIPTION
;
146 // general initialization and shutdown
149 /// \brief Initialize the plugin
151 /// This C++ function gets called once when the plugin is loaded,
152 /// regardless of how many instantiations there is actually playing
153 /// movies. So this is where all the one time only initialization
156 NS_PluginInitialize()
159 if ( gnash::plugInitialized
) {
160 gnash::log_debug("NS_PluginInitialize called, but ignored (we already initialized)");
161 return NPERR_NO_ERROR
;
164 gnash::log_debug("NS_PluginInitialize call ---------------------------");
166 // Browser Functionality Checks
168 NPError err
= NPERR_NO_ERROR
;
169 NPBool supportsXEmbed
= TRUE
;
171 // First, check for XEmbed support. The NPAPI Gnash plugin
172 // only works with XEmbed, so tell the plugin API to fail if
173 // XEmbed is not found.
174 err
= NPN_GetValue(NULL
,NPNVSupportsXEmbedBool
,
175 (void *)&supportsXEmbed
);
177 if (err
!= NPERR_NO_ERROR
|| !supportsXEmbed
) {
178 gnash::log_error("NPAPI ERROR: No xEmbed support in this browser!");
179 return NPERR_INCOMPATIBLE_VERSION_ERROR
;
181 gnash::log_debug("xEmbed supported in this browser");
184 // GTK is not strictly required, but we do use the Glib main event loop,
185 // so lack of GTK means reduced functionality.
186 NPNToolkitType toolkit
;
187 err
= NPN_GetValue(NULL
, NPNVToolkit
, &toolkit
);
189 if (err
!= NPERR_NO_ERROR
|| toolkit
!= NPNVGtk2
) {
190 gnash::log_error("NPAPI ERROR: No GTK2 support in this browser! Have version %d", (int)toolkit
);
192 gnash::log_debug("GTK2 supported in this browser");
196 Check for environment variables.
198 char* opts
= std::getenv("GNASH_OPTIONS");
200 gnash::log_debug("GNASH_OPTIONS: %s", opts
);
202 // Should the plugin wait for gdb to be attached?
203 if ( strstr(opts
, "waitforgdb") ) {
207 // Should the plugin write a script to invoke
208 // the standalone player for debugging ?
209 if ( strstr(opts
, "writelauncher") ) {
210 createSaLauncher
= true;
215 // Append SYSCONFDIR/gnashpluginrc and ~/.gnashpluginrc to GNASHRC
217 std::string
newGnashRc("GNASHRC=");
219 #if !defined(__OS2__ ) && ! defined(__amigaos4__)
220 newGnashRc
.append(SYSCONFDIR
);
221 newGnashRc
.append("/gnashpluginrc");
224 const char *home
= NULL
;
225 #if defined(__amigaos4__)
226 //on AmigaOS we have a GNASH: assign that point to program dir
228 #elif defined(__HAIKU__)
230 if (B_OK
!= find_directory(B_USER_SETTINGS_DIRECTORY
, &bp
))
232 std::cerr
<< "Failed to find user settings directory" << std::endl
;
238 home
= std::getenv("HOME");
241 newGnashRc
.append(":");
242 newGnashRc
.append(home
);
244 newGnashRc
.append("/gnashpluginrc");
246 newGnashRc
.append("/.gnashpluginrc");
249 gnash::log_error("WARNING: NPAPI plugin could not find user home dir");
252 char *gnashrc
= std::getenv("GNASHRC");
254 newGnashRc
.append(":");
255 newGnashRc
.append(gnashrc
);
258 // putenv doesn't copy the string in standards-conforming implementations
259 gnashrc
= new char[PATH_MAX
];
260 std::strncpy(gnashrc
, newGnashRc
.c_str(), PATH_MAX
);
261 gnashrc
[PATH_MAX
-1] = '\0';
263 if ( putenv(gnashrc
) ) {
264 gnash::log_debug("WARNING: NPAPI plugin could not append to the GNASHRC env variable");
266 gnash::log_debug("NOTE: NPAPI plugin set GNASHRC to %d", newGnashRc
);
271 gnash::plugInitialized
= TRUE
;
273 return NPERR_NO_ERROR
;
276 /// \brief Shutdown the plugin
278 /// This C++ function gets called once when the plugin is being
279 /// shutdown, regardless of how many instantiations actually are
280 /// playing movies. So this is where all the one time only
281 /// shutdown stuff goes.
286 if (!plugInitialized
) {
287 gnash::log_debug("Plugin already shut down");
291 plugInitialized
= FALSE
;
295 /// \brief Retrieve values from the plugin for the Browser
297 /// This C++ function is called by the browser to get certain
298 /// information is needs from the plugin. This information is the
299 /// plugin name, a description, etc...
301 NS_PluginGetValue(NPPVariable aVariable
, void *aValue
)
303 NPError err
= NPERR_NO_ERROR
;
306 case NPPVpluginNameString
:
307 *static_cast<const char **> (aValue
) = PLUGIN_NAME
;
310 // This becomes the description field you see below the opening
311 // text when you type about:plugins and in
312 // navigator.plugins["Shockwave Flash"].description, used in
313 // many flash version detection scripts.
314 case NPPVpluginDescriptionString
:
315 *static_cast<const char **>(aValue
) = getPluginDescription();
318 case NPPVpluginWindowBool
:
321 case NPPVpluginTimerInterval
:
324 case NPPVpluginKeepLibraryInMemory
:
327 case NPPVpluginNeedsXEmbed
:
329 *static_cast<NPBool
*>(aValue
) = TRUE
;
331 *static_cast<NPBool
*>(aValue
) = FALSE
;
335 case NPPVpluginScriptableNPObject
:
338 case NPPVpluginUrlRequestsDisplayedBool
:
340 case NPPVpluginWantsAllNetworkStreams
:
344 err
= NPERR_INVALID_PARAM
;
350 /// \brief construct our plugin instance object
352 /// This instantiates a new object via a C++ function used by the
354 nsPluginInstanceBase
*
355 NS_NewPluginInstance(nsPluginCreateData
* aCreateDataStruct
)
357 // gnash::log_debug(__PRETTY_FUNCTION__);
359 if(!aCreateDataStruct
) {
363 return new gnash::nsPluginInstance(aCreateDataStruct
);
366 /// \brief destroy our plugin instance object
368 /// This destroys our instantiated object via a C++ function used by the
371 NS_DestroyPluginInstance(nsPluginInstanceBase
* aPlugin
)
373 delete static_cast<gnash::nsPluginInstance
*> (aPlugin
);
379 // nsPluginInstance class implementation
382 /// \brief Constructor
383 nsPluginInstance::nsPluginInstance(nsPluginCreateData
* data
)
385 nsPluginInstanceBase(),
386 _instance(data
->instance
),
399 // gnash::log_debug("%s: %x", __PRETTY_FUNCTION__, (void *)this);
401 for (size_t i
=0, n
=data
->argc
; i
<n
; ++i
) {
402 std::string name
, val
;
403 gnash::StringNoCaseEqual noCaseCompare
;
406 name
= data
->argn
[i
];
413 if (noCaseCompare(name
, "name")) {
421 if (NPNFuncs
.version
>= 14) { // since NPAPI start to support
422 _scriptObject
= (GnashPluginScriptObject
*)NPNFuncs
.createobject(
423 _instance
, GnashPluginScriptObject::marshalGetNPClass());
431 cleanup_childpid(gpointer data
)
433 int* pid
= static_cast<int*>(data
);
436 int rv
= waitpid(*pid
, &status
, WNOHANG
);
439 // The child process has not exited; it may be deadlocked. Kill it.
440 // gnash::log_error("BUG: Child process is stuck. Killing it.");
443 waitpid(*pid
, &status
, 0);
446 gnash::log_debug("Child process exited with status %s", status
);
453 /// \brief Destructor
454 nsPluginInstance::~nsPluginInstance()
456 // gnash::log_debug("plugin instance destruction");
458 if ( _ichanWatchId
) {
459 g_source_remove(_ichanWatchId
);
464 // When the child has terminated (signaled by _controlfd), it remains
465 // as a defunct process and we remove it from the kernel table now.
467 // If all goes well, Gnash will already have terminated.
469 int rv
= waitpid(_childpid
, &status
, WNOHANG
);
472 int* pid
= new int(_childpid
);
474 cleanup_childpid(pid
);
476 gnash::log_debug("Child process exited with status %d", status
);
482 /// \brief Initialize an instance of the plugin object
484 /// This methods initializes the plugin object, and is called for
485 /// every movie that gets played. This is where the movie playing
486 /// specific initialization goes.
488 nsPluginInstance::init(NPWindow
* aWindow
)
491 gnash::log_error("%s: ERROR: Window handle was bogus!", __PRETTY_FUNCTION__
);
494 #if GNASH_PLUGIN_DEBUG > 1
495 std::cout
<< "X origin: = " << aWindow
->x
496 << ", Y Origin = " << aWindow
->y
497 << ", Width = " << aWindow
->width
498 << ", Height = " << aWindow
->height
499 << ", WindowID = " << aWindow
->window
500 << ", this = " << static_cast<void*>(this) << std::endl
;
507 /// \brief Shutdown an instantiated object
509 /// This shuts down an object, and is called for every movie that gets
510 /// played. This is where the movie playing specific shutdown code
513 nsPluginInstance::shut()
515 gnash::log_debug("Gnash plugin shutting down");
517 if (_streamfd
!= -1) {
518 if (close(_streamfd
) == -1) {
519 perror("closing _streamfd");
525 if (_controlfd
!= -1) {
526 _scriptObject
->closePipe(_controlfd
);
527 if (close(_controlfd
) != 0) {
528 gnash::log_error("Gnash plugin failed to close the control socket!");
534 /// \brief Set the window to be used to render in
536 /// This sets up the window the plugin is supposed to render
537 /// into. This calls passes in various information used by the plugin
538 /// to setup the window. This may get called multiple times by each
539 /// instantiated object, so it can't do much but window specific
542 nsPluginInstance::SetWindow(NPWindow
* aWindow
)
545 gnash::log_error(std::string(__FUNCTION__
) + ": ERROR: Window handle was bogus!");
546 return NPERR_INVALID_PARAM
;
549 gnash::log_debug("%s: X origin = %d, Y Origin = %d, Width = %d,"
550 " Height = %d, WindowID = %p, this = %p",
552 aWindow
->x
, aWindow
->y
, aWindow
->width
, aWindow
->height
,
553 aWindow
->window
, this);
558 return NPERR_GENERIC_ERROR
;
561 _width
= aWindow
->width
;
562 _height
= aWindow
->height
;
564 _window
= reinterpret_cast<Window
> (aWindow
->window
);
566 // When testing the interface to the plugin, don't start the player
567 // as a debug client "nc -l 1111" is used instead.
568 if (!_childpid
&& !_swf_url
.empty()) {
572 return NPERR_NO_ERROR
;
576 nsPluginInstance::GetValue(NPPVariable aVariable
, void *aValue
)
579 if (aVariable
== NPPVpluginScriptableNPObject
) {
581 void **v
= (void **)aValue
;
582 NPNFuncs
.retainobject(_scriptObject
);
585 gnash::log_debug("_scriptObject is not assigned");
589 // log_debug("SCRIPT OBJECT getValue: %x, ns: %x", (void *)_scriptObject, (void *)this);
591 return NS_PluginGetValue(aVariable
, aValue
);
595 // FIXME: debugging stuff, will be gone soon after I figure how this works
596 void myfunc(void */
* param */
)
598 gnash::log_debug("Here I am!!!\n");
602 /// \brief Open a new data stream
604 /// Opens a new incoming data stream, which is the flash movie we want
606 /// A URL can be pretty ugly, like in this example:
607 /// 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
608 /// ../flash/gui.swf?ip_addr=foobar.com&ip_port=3660&show_cursor=true&path_prefix=../flash/&trapallkeys=true"
612 nsPluginInstance::NewStream(NPMIMEType
/*type*/, NPStream
* stream
,
613 NPBool
/*seekable*/, uint16_t* /*stype*/)
615 // gnash::log_debug("%s: %x", __PRETTY_FUNCTION__, (void *)this);
618 // Apparently the child process has already been started for this
619 // plugin instance. It is puzzling that this method gets called
620 // again. Starting a new process for the same movie will cause
621 // problems later, so we'll stop here.
622 return NPERR_GENERIC_ERROR
;
624 _swf_url
= stream
->url
;
627 // FIXME: debugging crap for now call javascript
628 NPN_PluginThreadAsyncCall(_instance
, myfunc
, NULL
);
629 // gnash::log_debug("FIXME: %s", getEmbedURL());
632 if (!_swf_url
.empty() && _window
) {
636 return NPERR_NO_ERROR
;
639 /// \brief Destroy the data stream we've been reading.
641 nsPluginInstance::DestroyStream(NPStream
* /*stream*/, NPError
/*reason*/)
644 if (_streamfd
!= -1) {
645 if (close(_streamfd
) == -1) {
646 perror("closing _streamfd");
652 return NPERR_NO_ERROR
;
655 /// \brief Return how many bytes we can read into the buffer
657 nsPluginInstance::WriteReady(NPStream
* /* stream */ )
659 //gnash::log_debug("Stream for %s is ready", stream->url);
660 if ( _streamfd
!= -1 ) {
667 /// \brief Read the data stream from Mozilla/Firefox
670 nsPluginInstance::Write(NPStream
* /*stream*/, int32_t /*offset*/, int32_t len
,
673 int written
= write(_streamfd
, buffer
, len
);
678 nsPluginInstance::handlePlayerRequestsWrapper(GIOChannel
* iochan
,
679 GIOCondition cond
, nsPluginInstance
* plugin
)
681 return plugin
->handlePlayerRequests(iochan
, cond
);
685 nsPluginInstance::handlePlayerRequests(GIOChannel
* iochan
, GIOCondition cond
)
687 // gnash::log_debug("%s: %d: %x", __PRETTY_FUNCTION__, __LINE__, (void *)this);
689 if ( cond
& G_IO_HUP
) {
690 gnash::log_debug("Player control socket hang up");
691 // Returning false here will cause the "watch" to be removed. This watch
692 // is the only reference held to the GIOChannel, so it will be
693 // destroyed. We must make sure we don't attempt to destroy it again.
698 assert(cond
& G_IO_IN
);
700 gnash::log_debug("Checking player requests on FD #%d",
701 g_io_channel_unix_get_fd(iochan
));
704 // g_io_channel_set_flags(iochan, G_IO_FLAG_NONBLOCK, &error);
708 gsize requestSize
= 0;
710 // When in non-blocking mode, we'll get several iterations of this
711 // loop while waiting for data. if data never arrives, we'd be stuck
712 // looping here forever, so this is our escape from that loop.
713 if (retries
-- <= 0) {
714 gnash::log_error("Too many attempts to read from the player!");
720 GIOStatus status
= g_io_channel_read_line(iochan
, &request
,
721 &requestSize
, NULL
, &error
);
723 case G_IO_STATUS_ERROR
:
724 gnash::log_error("error reading request line: %s",
728 case G_IO_STATUS_EOF
:
729 gnash::log_error("EOF (error: %s", error
->message
);
732 case G_IO_STATUS_AGAIN
:
733 gnash::log_debug("read again: nonblocking mode set ");
735 case G_IO_STATUS_NORMAL
:
737 // Get rid of the newline on the end if there is one. The string
738 // is also NULL terninated, so the requestSize includes it in
740 if (request
[requestSize
-1] == '\n') {
741 request
[requestSize
-1] = 0;
744 gnash::log_debug("Normal read: %s", request
);
747 gnash::log_error("Abnormal status!");
750 } while (g_io_channel_get_buffer_condition(iochan
) & G_IO_IN
);
753 processPlayerRequest(request
, requestSize
);
760 nsPluginInstance::processPlayerRequest(gchar
* buf
, gsize linelen
)
762 // gnash::log_debug(__PRETTY_FUNCTION__);
764 // log_debug("SCRIPT OBJECT %d: %x", __LINE__, this->getScriptObject());
768 gnash::log_error("Invalid player request (too short): %s", buf
);
770 gnash::log_error("Invalid player request (too short): %d bytes", linelen
);
775 plugin::ExternalInterface::invoke_t
*invoke
= plugin::ExternalInterface::parseInvoke(buf
);
777 if (!invoke
->name
.empty()) {
778 gnash::log_debug("Requested method is: %s", invoke
->name
);
781 // The invoke message is also used for getURL. In this case there are 4
782 // possible arguments.
784 if (invoke
->name
== "getURL") {
785 // gnash::log_debug("Got a getURL() request: %", invoke->args[0].get());
787 // The first argument is the URL string.
788 std::string url
= NPStringToString(NPVARIANT_TO_STRING(
789 invoke
->args
[0].get()));
790 // The second is the method, namely GET or POST.
791 std::string op
= NPStringToString(NPVARIANT_TO_STRING(
792 invoke
->args
[1].get()));
793 // The third is the optional target, which is something like
794 // _blank or _self. NONE means no target.
796 // The fourth is the optional data. If there is data, the target
797 // field is always set so this argument is on the correct index.
798 // No target is "NONE".
800 if (invoke
->args
.size() >= 3) {
801 target
= NPStringToString(NPVARIANT_TO_STRING(
802 invoke
->args
[2].get()));
803 if (target
== "NONE") {
807 if (invoke
->args
.size() == 4) {
808 data
= NPStringToString(NPVARIANT_TO_STRING(
809 invoke
->args
[3].get()));
812 gnash::log_debug("Asked to getURL '%s' in target %s", url
,
814 NPN_GetURL(_instance
, url
.c_str(), target
.c_str());
815 } else if (op
== "POST") {
816 gnash::log_debug("Asked to postURL '%s' this data %s", url
,
818 NPN_PostURL(_instance
, url
.c_str(), target
.c_str(), data
.size(),
819 data
.c_str(), false);
824 } else if (invoke
->name
== "fsCommand") {
825 std::string command
= NPStringToString(NPVARIANT_TO_STRING(
826 invoke
->args
[0].get()));
827 std::string arg
= NPStringToString(NPVARIANT_TO_STRING(
828 invoke
->args
[1].get()));
829 std::string name
= _name
;
830 std::stringstream jsurl
;
831 jsurl
<< "javascript:" << name
<< "_DoFSCommand('" << command
832 << "','" << arg
<<"')";
834 // TODO: check if _self is a good target for this
835 static const char* tgt
= "_self";
837 gnash::log_debug("Calling NPN_GetURL(%s, %s)",
840 NPN_GetURL(_instance
, jsurl
.str().c_str(), tgt
);
842 } else if (invoke
->name
== "addMethod") {
843 // Make this flash function accessible to Javascript. The
844 // actual callback lives in libcore/movie_root, but it
845 // needs to be on the list of supported remote methods so
846 // it can be called by Javascript.
847 std::string method
= NPStringToString(NPVARIANT_TO_STRING(
848 invoke
->args
[0].get()));
849 NPIdentifier id
= NPN_GetStringIdentifier(method
.c_str());
850 // log_debug("SCRIPT OBJECT addMethod: %x, %s", (void *)_scriptObject, method);
851 this->getScriptObject()->AddMethod(id
, remoteCallback
);
855 VOID_TO_NPVARIANT(result
);
856 bool invokeResult
=false;
857 // This is the player invoking a method in Javascript
858 if (!invoke
->name
.empty()) {
859 //Convert the as_value argument to NPVariant
860 uint32_t count
= invoke
->args
.size();
861 if(count
!=0) //The first argument should exists and be the method name
864 NPVariant
* args
= new NPVariant
[count
];
865 //Skip the first argument
866 for(uint32_t i
=0;i
<count
;i
++)
867 invoke
->args
[i
+1].copy(args
[i
]);
868 NPIdentifier id
= NPN_GetStringIdentifier(invoke
->name
.c_str());
869 gnash::log_debug("Invoking JavaScript method %s", invoke
->name
);
870 NPObject
* windowObject
;
871 NPN_GetValue(_instance
, NPNVWindowNPObject
, &windowObject
);
872 invokeResult
=NPN_Invoke(_instance
, windowObject
, id
, args
, count
, &result
);
873 NPN_ReleaseObject(windowObject
);
877 // We got a result from invoking the Javascript method
878 std::stringstream ss
;
880 ss
<< plugin::ExternalInterface::convertNPVariant(&result
);
881 NPN_ReleaseVariantValue(&result
);
884 // FIXME: "securityError" also possible, check domain
885 ss
<< plugin::ExternalInterface::makeString("Error");
887 size_t ret
= _scriptObject
->writePlayer(ss
.str());
888 if (ret
!= ss
.str().size()) {
889 log_error("Couldn't write the response to Gnash, network problems.");
894 gnash::log_error("Unknown player request: " + std::string(buf
));
904 std::string procname
;
905 bool process_found
= false;
906 struct stat procstats
;
908 char *gnash_env
= std::getenv("GNASH_PLAYER");
911 procname
= gnash_env
;
912 process_found
= (0 == stat(procname
.c_str(), &procstats
));
913 if (!process_found
) {
914 gnash::log_error("Invalid path to gnash executable: ");
919 if (!process_found
) {
920 procname
= GNASHBINDIR
"/gtk-gnash";
921 process_found
= (0 == stat(procname
.c_str(), &procstats
));
923 if (!process_found
) {
924 procname
= GNASHBINDIR
"/kde4-gnash";
925 process_found
= (0 == stat(procname
.c_str(), &procstats
));
928 if (!process_found
) {
929 gnash::log_error(std::string("Unable to find Gnash in ") + GNASHBINDIR
);
938 create_standalone_launcher(const std::string
& page_url
, const std::string
& swf_url
,
939 const std::map
<std::string
, std::string
>& params
)
941 #ifdef CREATE_STANDALONE_GNASH_LAUNCHER
942 if (!createSaLauncher
) {
946 std::ofstream saLauncher
;
948 std::stringstream ss
;
949 static int debugno
= 0;
950 debugno
= (debugno
+ 1) % 10;
951 ss
<< "/tmp/gnash-debug-" << debugno
<< ".sh";
952 saLauncher
.open(ss
.str().c_str(), std::ios::out
| std::ios::trunc
);
955 gnash::log_error("Failed to open new file for standalone launcher: " + ss
.str());
959 saLauncher
<< "#!/bin/sh" << std::endl
960 << "export GNASH_COOKIES_IN="
961 << "/tmp/gnash-cookies." << getpid() << std::endl
962 << getGnashExecutable() << " ";
964 if (!page_url
.empty()) {
965 saLauncher
<< "-U '" << page_url
<< "' ";
968 for (std::map
<std::string
,std::string
>::const_iterator it
= params
.begin(),
969 itEnd
= params
.end(); it
!= itEnd
; ++it
) {
970 const std::string
& nam
= it
->first
;
971 const std::string
& val
= it
->second
;
973 << boost::algorithm::replace_all_copy(nam
, "'", "'\\''")
975 << boost::algorithm::replace_all_copy(val
, "'", "'\\''")
979 saLauncher
<< "'" << swf_url
<< "' "
980 << "$@" // allow caller to pass any additional argument
988 nsPluginInstance::setupCookies(const std::string
& pageurl
)
990 // In pre xulrunner 1.9, (Firefox 3.1) this function does not exist,
991 // so we can't use it to read the cookie file. For older browsers
992 // like IceWeasel on Debian lenny, which pre dates the cookie support
993 // in NPAPI, you have to block all Cookie for sites like YouTube to
994 // allow Gnash to work.
995 if (!NPNFuncs
.getvalueforurl
) {
996 LOG_ONCE( gnash::log_debug("Browser doesn't support reading cookies") );
1000 // Cookie appear to drop anything past the domain, so we strip
1002 std::string::size_type pos
;
1003 pos
= pageurl
.find("/", pageurl
.find("//", 0) + 2) + 1;
1004 std::string url
= pageurl
.substr(0, pos
);
1007 uint32_t length
= 0;
1008 NPN_GetValueForURL(_instance
, NPNURLVCookie
, url
.c_str(),
1011 std::string
ncookie (cookie
, length
);
1012 gnash::log_debug("The Cookie for %s is %s", url
, ncookie
);
1013 std::ofstream cookiefile
;
1014 std::stringstream ss
;
1015 ss
<< "/tmp/gnash-cookies." << getpid();
1017 cookiefile
.open(ss
.str().c_str(), std::ios::out
| std::ios::trunc
);
1018 cookiefile
<< "Set-Cookie: " << ncookie
<< std::endl
;
1021 if (setenv("GNASH_COOKIES_IN", ss
.str().c_str(), 1) < 0) {
1023 "Couldn't set environment variable GNASH_COOKIES_IN to %s",
1026 NPN_MemFree(cookie
);
1028 gnash::log_debug("No stored Cookie for %s", url
);
1033 nsPluginInstance::setupProxy(const std::string
& url
)
1035 // In pre xulrunner 1.9, (Firefox 3.1) this function does not exist,
1036 // so we can't use it to read the proxy information.
1037 if (!NPNFuncs
.getvalueforurl
) return;
1040 uint32_t length
= 0;
1041 NPN_GetValueForURL(_instance
, NPNURLVProxy
, url
.c_str(),
1044 gnash::log_debug("No proxy setting for %s", url
);
1048 std::string
nproxy (proxy
, length
);
1051 gnash::log_debug("Proxy setting for %s is %s", url
, nproxy
);
1053 std::vector
<std::string
> parts
;
1054 boost::split(parts
, nproxy
,
1055 boost::is_any_of(" "), boost::token_compress_on
);
1056 if ( parts
[0] == "DIRECT" ) {
1059 else if ( parts
[0] == "PROXY" ) {
1060 if (setenv("http_proxy", parts
[1].c_str(), 1) < 0) {
1062 "Couldn't set environment variable http_proxy to %s",
1067 gnash::log_error("Unknown proxy type: %s", nproxy
);
1072 std::vector
<std::string
>
1073 nsPluginInstance::getCmdLine(int hostfd
, int controlfd
)
1075 std::vector
<std::string
> arg_vec
;
1077 std::string cmd
= getGnashExecutable();
1079 gnash::log_error("Failed to locate the Gnash executable!");
1082 arg_vec
.push_back(cmd
);
1084 arg_vec
.push_back("-u");
1085 arg_vec
.push_back(_swf_url
);
1087 std::string pageurl
= getCurrentPageURL();
1088 if (pageurl
.empty()) {
1089 gnash::log_error("Could not get current page URL!");
1091 arg_vec
.push_back("-U");
1092 arg_vec
.push_back(pageurl
);
1095 setupCookies(pageurl
);
1096 setupProxy(pageurl
);
1098 std::stringstream pars
;
1099 pars
<< "-x " << _window
// X window ID to render into
1100 << " -j " << _width
// Width of window
1101 << " -k " << _height
; // Height of window
1102 #if GNASH_PLUGIN_DEBUG > 1
1105 if ((hostfd
> 0) && (controlfd
)) {
1106 pars
<< " -F " << hostfd
// Socket to send commands to
1107 << ":" << controlfd
; // Socket determining lifespan
1109 std::string pars_str
= pars
.str();
1110 typedef boost::char_separator
<char> char_sep
;
1111 boost::tokenizer
<char_sep
> tok(pars_str
, char_sep(" "));
1112 arg_vec
.insert(arg_vec
.end(), tok
.begin(), tok
.end());
1114 for (std::map
<std::string
,std::string
>::const_iterator it
= _params
.begin(),
1115 itEnd
= _params
.end(); it
!= itEnd
; ++it
) {
1116 const std::string
& nam
= it
->first
;
1117 const std::string
& val
= it
->second
;
1118 arg_vec
.push_back("-P");
1119 arg_vec
.push_back(nam
+ "=" + val
);
1121 arg_vec
.push_back("-");
1123 create_standalone_launcher(pageurl
, _swf_url
, _params
);
1128 template <std::size_t N
>
1130 close_fds(const int (& except
)[N
])
1132 // Rather than close all the thousands of possible file
1133 // descriptors, we start after stderr and keep closing higher numbers
1134 // until we encounter ten fd's in a row that
1135 // aren't open. This will tend to close most fd's in most programs.
1136 int numfailed
= 0, closed
= 0;
1137 for (int anfd
= fileno(stderr
)+1; numfailed
< 10; anfd
++) {
1138 if (std::find(except
, except
+N
, anfd
) != except
+N
) {
1141 if (close(anfd
) < 0) {
1148 gnash::log_debug("Closed %d files.", closed
);
1158 // For debugging the plugin (GNASH_OPTIONS=waitforgdb)
1159 // Block here until gdb is attached and sets waitforgdb to false.
1161 std::cout
<< std::endl
<< " Attach GDB to PID " << getpid()
1162 << " to debug!" << std::endl
1163 << " This thread will block until then!" << std::endl
1164 << " Once blocked here, you can set other breakpoints."
1166 << " Do a \"set variable waitforgdb=$false\" to continue"
1167 << std::endl
<< std::endl
;
1169 while (waitforgdb
) {
1175 nsPluginInstance::startProc()
1180 int p2c_controlpipe
[2];
1182 int ret
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, p2c_pipe
);
1184 gnash::log_error("socketpair(p2c) failed: %s", strerror(errno
));
1187 _streamfd
= p2c_pipe
[1];
1189 ret
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, c2p_pipe
);
1191 gnash::log_error("socketpair(c2p) failed: %s", strerror(errno
));
1195 ret
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, p2c_controlpipe
);
1197 gnash::log_error("socketpair(control) failed: %s", strerror(errno
));
1201 _scriptObject
->setControlFD(p2c_controlpipe
[1]);
1202 _scriptObject
->setHostFD(c2p_pipe
[0]);
1204 // Setup the command line for starting Gnash
1206 std::vector
<std::string
> arg_vec
= getCmdLine(c2p_pipe
[1],
1207 p2c_controlpipe
[0]);
1209 if (arg_vec
.empty()) {
1210 gnash::log_error("Failed to obtain command line parameters.");
1214 std::vector
<const char*> args
;
1216 std::transform(arg_vec
.begin(), arg_vec
.end(), std::back_inserter(args
),
1217 std::mem_fun_ref(&std::string::c_str
));
1220 // Argument List prepared, now fork(), close file descriptors and execv()
1224 // If the fork failed, childpid is -1. So print out an error message.
1225 if (_childpid
== -1) {
1226 gnash::log_error("dup2() failed: %s", strerror(errno
));
1230 // If we are the parent and fork() worked, childpid is a positive integer.
1231 if (_childpid
> 0) {
1233 // we want to write to p2c pipe, so close read-fd0
1234 ret
= close (p2c_pipe
[0]);
1236 // this is not really a fatal error, so continue best as we can
1237 gnash::log_error("p2c_pipe[0] close() failed: %s",
1241 // we want to read from c2p pipe, so close read-fd1
1242 ret
= close (c2p_pipe
[1]);
1244 // this is not really a fatal error, so continue best as we can
1245 gnash::log_error("c2p_pipe[1] close() failed: %s",
1247 gnash::log_debug("Forked successfully but with ignorable errors.");
1249 gnash::log_debug("Forked successfully, child process PID is %d",
1253 GIOChannel
* ichan
= g_io_channel_unix_new(c2p_pipe
[0]);
1254 gnash::log_debug("New IO Channel for fd #%d",
1255 g_io_channel_unix_get_fd(ichan
));
1256 g_io_channel_set_close_on_unref(ichan
, true);
1257 _ichanWatchId
= g_io_add_watch(ichan
,
1258 (GIOCondition
)(G_IO_IN
|G_IO_HUP
),
1259 (GIOFunc
)handlePlayerRequestsWrapper
,
1264 // This is the child scope.
1266 // FF3 uses jemalloc and it has problems after the fork(), do NOT
1267 // use memory functions (malloc()/free()/new/delete) after the fork()
1268 // in the child thread process
1269 ret
= close (p2c_pipe
[1]);
1271 // close standard input and direct read-fd1 to standard input
1272 ret
= dup2 (p2c_pipe
[0], fileno(stdin
));
1275 gnash::log_error("dup2() failed: %s", strerror(errno
));
1278 // Close all of the browser's file descriptors that we just inherited
1279 // (including p2c_pipe[0] that we just dup'd to fd 0), but obviously
1280 // not the file descriptors that we want the child to use.
1281 int dontclose
[] = {c2p_pipe
[1], c2p_pipe
[0], p2c_controlpipe
[0]};
1282 close_fds(dontclose
);
1284 // Start the desired executable and go away.
1286 gnash::log_debug("Starting process: %s", boost::algorithm::join(arg_vec
, " "));
1290 execv(args
[0], const_cast<char**>(&args
.front()));
1292 // if execv returns, an error has occurred.
1293 perror("executing standalone gnash");
1299 nsPluginInstance::getCurrentPageURL() const
1302 // window.document.baseURI
1305 // window.document.location.href
1308 NPP npp
= _instance
;
1310 NPIdentifier sDocument
= NPN_GetStringIdentifier("document");
1313 NPN_GetValue(npp
, NPNVWindowNPObject
, &window
);
1316 NPN_GetProperty(npp
, window
, sDocument
, &vDoc
);
1317 NPN_ReleaseObject(window
);
1319 if (!NPVARIANT_IS_OBJECT(vDoc
)) {
1320 gnash::log_error("Can't get window.document object");
1321 return std::string();
1324 NPObject
* npDoc
= NPVARIANT_TO_OBJECT(vDoc
);
1327 NPIdentifier sLocation = NPN_GetStringIdentifier("location");
1329 NPN_GetProperty(npp, npDoc, sLocation, &vLoc);
1330 NPN_ReleaseObject(npDoc);
1332 if (!NPVARIANT_IS_OBJECT(vLoc)) {
1333 gnash::log_error("Can't get window.document.location object");
1334 return std::string();
1337 NPObject* npLoc = NPVARIANT_TO_OBJECT(vLoc);
1339 NPIdentifier sProperty = NPN_GetStringIdentifier("href");
1341 NPN_GetProperty(npp, npLoc, sProperty, &vProp);
1342 NPN_ReleaseObject(npLoc);
1344 if (!NPVARIANT_IS_STRING(vProp)) {
1345 gnash::log_error("Can't get window.document.location.href string");
1346 return std::string();
1350 NPIdentifier sProperty
= NPN_GetStringIdentifier("baseURI");
1352 NPN_GetProperty(npp
, npDoc
, sProperty
, &vProp
);
1353 NPN_ReleaseObject(npDoc
);
1355 if (!NPVARIANT_IS_STRING(vProp
)) {
1356 gnash::log_error("Can't get window.document.baseURI string");
1357 return std::string();
1360 const NPString
& propValue
= NPVARIANT_TO_STRING(vProp
);
1362 return NPStringToString(propValue
);
1366 processLog_error(const boost::format
& fmt
)
1368 std::cerr
<< "ERROR: " << fmt
.str() << std::endl
;
1371 #if GNASH_PLUGIN_DEBUG > 1
1373 processLog_debug(const boost::format
& fmt
)
1375 std::cout
<< "DEBUG: " << fmt
.str() << std::endl
;
1379 processLog_trace(const boost::format
& fmt
)
1381 std::cout
<< "TRACE: " << fmt
.str() << std::endl
;
1385 processLog_debug(const boost::format
& /* fmt */)
1386 { /* do nothing */ }
1389 processLog_trace(const boost::format
& /* fmt */)
1390 { /* do nothing */ }
1393 } // end of gnash namespace
1397 // indent-tabs-mode: nil