]> 91.132.146.200 Git - insipid.git/commitdiff
code cleanup
authorBanana <banana@starscream.de>
Wed, 15 Jun 2011 09:30:34 +0000 (11:30 +0200)
committerBanana <banana@starscream.de>
Wed, 15 Jun 2011 09:30:34 +0000 (11:30 +0200)
lib/Insipid/Main.pm

index 724ffda12c8d742656f0a0699360e83a7bfdc9aa..47d16ef36202c0c0ef3b4479c3a51d568879c7d0 100755 (executable)
-#!/usr/bin/perl\r
-#\r
-# Copyright (C) 2008 Luke Reeves\r
-#\r
-# This program is free software; you can redistribute it and/or modify\r
-# it under the terms of the GNU General Public License as published by\r
-# the Free Software Foundation; either version 2 of the License, or\r
-# (at your option) any later version.\r
-#\r
-# This program is distributed in the hope that it will be useful,\r
-# but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-# GNU General Public License for more details.\r
-#\r
-# You should have received a copy of the GNU General Public License\r
-# along with this program; if not, write to the Free Software\r
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307\r
-# USA\r
-#\r
-\r
-package Insipid::Main;\r
-\r
-use warnings;\r
-use strict;\r
-\r
-use vars qw(@ISA @EXPORT @EXPORT_OK);\r
-require Exporter;\r
-\r
-@ISA = qw(Exporter);\r
-\r
-@EXPORT = qw(main);\r
-\r
-use Insipid::Config;\r
-use Insipid::Database;\r
-use Insipid::Bookmarks;\r
-use Insipid::RSS;\r
-use Insipid::JSON;\r
-use Insipid::Sessions;\r
-use Insipid::Snapshots;\r
-use Insipid::Tags;\r
-use Insipid::Util;\r
-\r
-use CGI qw/:standard/;\r
-use CGI::Carp qw(fatalsToBrowser);\r
-use URI::Escape;\r
-use IO::File;\r
-use XML::Parser;\r
-use XML::Writer;\r
-use Date::Format;\r
-use Date::Parse;\r
-use DBI qw/:sql_types/;;\r
-use Digest::MD5 qw(md5 md5_hex);\r
-use MIME::Base64;\r
-use LWP::UserAgent;\r
-use HTTP::Request;\r
-use HTTP::Response;\r
-\r
-my $NL = "<br />\n";\r
-my @valid;\r
-my $icount = 0;\r
-my %options;\r
-my $tspec = "";\r
-my $query = "";\r
-my $last_page = 0;\r
-my $site_title;\r
-\r
-if(!defined($ENV{SERVER_NAME})) {\r
-       $NL = "\n";\r
-}\r
-\r
-sub main {\r
-\r
-       my $username = getconfig('username');\r
-       my $userpass = getconfig('userpass');\r
-\r
-       my $redirect = '';\r
-       my $et = '';\r
-\r
-       # Valid options:\r
-       @valid = ('feed_name', 'site_name', 'public_searches',\r
-                       'use_rewrite', 'proxy_host', 'proxy_port');\r
-\r
-       # Get the basic options\r
-       $site_title = get_option('site_name');\r
-       if($site_title eq '') {\r
-               $site_title = 'Insipid Bookmarks';\r
-       }\r
-\r
-       # Initialize variables that can be posted and in the URL.\r
-       if(defined(url_param('q'))) {\r
-               $query = url_param('q');\r
-       }\r
-\r
-       if(defined(param('q'))) {\r
-               $query = param('q');\r
-       }\r
-\r
-       # Check to see if a username and password have been posted\r
-       if(defined(param('password')) && defined(param('username'))) {\r
-         if( (param('password') eq $userpass) && (param('username') eq $username) ) {\r
-           my $rv = login();\r
-           print $rv;\r
-         } else {\r
-           push(@errors, "Invalid username or password.");\r
-         }\r
-       }\r
-\r
-       # Operations for non-HTML content\r
-\r
-       if(defined(url_param('op'))) {\r
-               if(url_param('op') eq 'export') {\r
-                       my $sn = 'n';\r
-                       if(defined(param('snapshots'))) {\r
-                               $sn = 'y';\r
-                       }\r
-\r
-                       do_export($sn);\r
-               }\r
-       \r
-               if(defined(param('op'))) {\r
-                       if(logged_in() eq 1) {\r
-                               if(param('op') eq 'logout') {\r
-                                       my $rv = logout();\r
-                                       print $rv;\r
-                               }\r
-                       }\r
-               }\r
-\r
-         # RSS\r
-         if(url_param('op') eq 'rss') {\r
-           print "Content-Type: text/xml\r\n\r\n";\r
-           send_rss();\r
-           exit;\r
-         }\r
-         \r
-         # JSON\r
-         # JSON Show tags:\r
-         if(url_param('op') eq 'json_tags') {\r
-           print "Content-Type: application/x-javascript;charset=UTF-8\r\n\r\n";\r
-               send_json_tags();\r
-           exit;\r
-         }\r
-         # JSON Show bookmarks:\r
-         if(url_param('op') eq 'json_posts') {\r
-           print "Content-Type: application/x-javascript;charset=UTF-8\r\n\r\n";\r
-           send_json_posts();\r
-           exit;\r
-         }\r
-         \r
-         # Cache\r
-         if(url_param('op') eq 'viewsnapshot') {\r
-           check_access();\r
-           if(param('md5')) {\r
-               show_snapshot(param('md5'));\r
-           }\r
-         }\r
-       }\r
-\r
-       # Allow redirections to a bookmark if the user's logged in.\r
-       # This allows private bookmarks to not send a referer.\r
-       if(logged_in() eq 1) {\r
-         if(param('go')) {\r
-           my $bid = param('go');\r
-           my $sql = "select url from $tbl_bookmarks\r
-                        where ($tbl_bookmarks.id = ?)";\r
-           my $sth = $dbh->prepare($sql);\r
-           $sth->execute($bid);\r
-           my $hr = $sth->fetchrow_hashref;\r
-           if(defined($hr->{'url'})) {\r
-             print "Cache-Control: private, must-revalidate\n";\r
-             print "Content-Type: text/html; charset=UTF-8\n\n";\r
-             print "<META HTTP-EQUIV=Refresh CONTENT=\"0; URL=$hr->{'url'}\">\n";\r
-             exit;\r
-           } else {\r
-             push(@errors, "Bookmark does not exist.");\r
-           }\r
-         }\r
-       }\r
-\r
-       # Add description to the HTML title tag.\r
-       if(url_param('tag')) {\r
-               $tspec = "/" . url_param('tag');\r
-               $tspec =~ s/ /\+/g;\r
-               my $tt = url_param('tag');\r
-               $tt =~ s/ / \+ /g;\r
-               $et = sprintf(" - %s", $tt);\r
-       }\r
-       if($query ne "") {\r
-               $et = sprintf(" - search results for \"%s\"", $query);\r
-       }\r
-\r
-       if(logged_in() eq 1) {\r
-         print "Cache-Control: private, must-revalidate\n";\r
-       }\r
-\r
-       print "Content-Type: text/html; charset=UTF-8\n\n";\r
-\r
-\r
-       print <<DOC;\r
-       <html>\r
-         <head>\r
-           <title>$site_title$et</title>\r
-           <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />\r
-           <link rel="alternate" type="application/rss+xml" title="RSS" href="$feed_url$tspec" />\r
-           <link rel="stylesheet" href="$site_url/insipid.css" type="text/css" title="Standard" />\r
+#!/usr/bin/perl
+#
+# Copyright (C) 2008 Luke Reeves
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+# USA
+#
+
+package Insipid::Main;
+
+use warnings;
+use strict;
+
+use vars qw(@ISA @EXPORT @EXPORT_OK);
+require Exporter;
+
+@ISA = qw(Exporter);
+
+@EXPORT = qw(main);
+
+use Insipid::Config;
+use Insipid::Database;
+use Insipid::Bookmarks;
+use Insipid::RSS;
+use Insipid::JSON;
+use Insipid::Sessions;
+use Insipid::Snapshots;
+use Insipid::Tags;
+use Insipid::Util;
+
+use CGI qw/:standard/;
+use CGI::Carp qw(fatalsToBrowser);
+use URI::Escape;
+use IO::File;
+use XML::Parser;
+use XML::Writer;
+use Date::Format;
+use Date::Parse;
+use DBI qw/:sql_types/;
+use Digest::MD5 qw(md5 md5_hex);
+use MIME::Base64;
+use LWP::UserAgent;
+use HTTP::Request;
+use HTTP::Response;
+
+my $NL = "<br />\n";
+my @valid;
+my $icount = 0;
+my %options;
+my $tspec     = "";
+my $query     = "";
+my $last_page = 0;
+my $site_title;
+
+if ( !defined( $ENV{SERVER_NAME} ) ) {
+    $NL = "\n";
+}
+
+sub main {
+
+    my $username = getconfig('username');
+    my $userpass = getconfig('userpass');
+
+    my $redirect = '';
+    my $et       = '';
+
+    # Valid options:
+    @valid = (
+        'feed_name',  'site_name', 'public_searches', 'use_rewrite',
+        'proxy_host', 'proxy_port'
+    );
+
+    # Get the basic options
+    $site_title = get_option('site_name');
+    if ( $site_title eq '' ) {
+        $site_title = 'Insipid Bookmarks';
+    }
+
+    # Initialize variables that can be posted and in the URL.
+    if ( defined( url_param('q') ) ) {
+        $query = url_param('q');
+    }
+
+    if ( defined( param('q') ) ) {
+        $query = param('q');
+    }
+
+    # Check to see if a username and password have been posted
+    if ( defined( param('password') ) && defined( param('username') ) ) {
+        if (   ( param('password') eq $userpass )
+            && ( param('username') eq $username ) )
+        {
+            my $rv = login();
+            print $rv;
+        }
+        else {
+            push( @errors, "Invalid username or password." );
+        }
+    }
+
+    # Operations for non-HTML content
+
+    if ( defined( url_param('op') ) ) {
+        if ( url_param('op') eq 'export' ) {
+            my $sn = 'n';
+            if ( defined( param('snapshots') ) ) {
+                $sn = 'y';
+            }
+            do_export($sn);
+        }
+
+        if ( defined( param('op') ) ) {
+            if ( logged_in() eq 1 ) {
+                if ( param('op') eq 'logout' ) {
+                    my $rv = logout();
+                    print $rv;
+                }
+            }
+        }
+
+        # RSS
+        if ( url_param('op') eq 'rss' ) {
+            print "Content-Type: text/xml\r\n\r\n";
+            send_rss();
+            exit;
+        }
+
+        # JSON
+        # JSON Show tags:
+        if ( url_param('op') eq 'json_tags' ) {
+            print
+              "Content-Type: application/x-javascript;charset=UTF-8\r\n\r\n";
+            send_json_tags();
+            exit;
+        }
+
+        # JSON Show bookmarks:
+        if ( url_param('op') eq 'json_posts' ) {
+            print
+              "Content-Type: application/x-javascript;charset=UTF-8\r\n\r\n";
+            send_json_posts();
+            exit;
+        }
+
+        # Cache
+        if ( url_param('op') eq 'viewsnapshot' ) {
+            check_access();
+            if ( param('md5') ) {
+                show_snapshot( param('md5') );
+            }
+        }
+    }
+
+    # Allow redirections to a bookmark if the user's logged in.
+    # This allows private bookmarks to not send a referer.
+    if ( logged_in() eq 1 ) {
+        if ( param('go') ) {
+            my $bid = param('go');
+            my $sql = "select url from $tbl_bookmarks
+                               where ($tbl_bookmarks.id = ?)";
+            my $sth = $dbh->prepare($sql);
+            $sth->execute($bid);
+            my $hr = $sth->fetchrow_hashref;
+            if ( defined( $hr->{'url'} ) ) {
+                print "Cache-Control: private, must-revalidate\n";
+                print "Content-Type: text/html; charset=UTF-8\n\n";
+                print
+                  "<META HTTP-EQUIV=Refresh CONTENT=\"0; URL=$hr->{'url'}\">\n";
+                exit;
+            }
+            else {
+                push( @errors, "Bookmark does not exist." );
+            }
+        }
+    }
+
+    # Add description to the HTML title tag.
+    if ( url_param('tag') ) {
+        $tspec = "/" . url_param('tag');
+        $tspec =~ s/ /\+/g;
+        my $tt = url_param('tag');
+        $tt =~ s/ / \+ /g;
+        $et = sprintf( " - %s", $tt );
+    }
+    if ( $query ne "" ) {
+        $et = sprintf( " - search results for \"%s\"", $query );
+    }
+
+    if ( logged_in() eq 1 ) {
+        print "Cache-Control: private, must-revalidate\n";
+    }
+
+    print "Content-Type: text/html; charset=UTF-8\n\n";
+
+    print <<DOC;
+<html>
+       <head>
+               <title>$site_title$et</title>
+               <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+           <link rel="alternate" type="application/rss+xml" title="RSS" href="$feed_url$tspec" />
+           <link rel="stylesheet" href="$site_url/insipid.css" type="text/css" title="Standard" />
                <script type="text/javascript" src="lib/js/jquery.js"></script>
-         </head>\r
-         <body marginheight="0" marginwidth="0">\r
-DOC\r
-\r
-\r
-       ###### Operations that don't touch the screen\r
-       if(defined(param('op')) && defined(param('id'))) {\r
-         if(param('op') eq 'delete_bookmark') {\r
-           my $id = param('id');\r
-           delete_bookmark($id);\r
-         } \r
-       }\r
-\r
-\r
-       # If the user just saved a bookmark, redirect them now.\r
-       if($redirect ne "") {\r
-         print "<script language=\"JavaScript\">document.location = \"$redirect\";</script>";\r
-         print "</body></html>";\r
-         exit;\r
-       }\r
-\r
-       show_toolbar();\r
-       show_tags();\r
-\r
-       print '<table class="bodyContent" border="0"><tr><td>';\r
-\r
-       if(defined(url_param('op'))) {\r
-         if(url_param('op') eq 'export') {\r
-               if(!defined(param('target'))) {\r
-                       print "<br /><br /><form method=\"post\" class=\"formText\">";\r
-                       print "<input type=\"checkbox\" name=\"snapshots\" />Include Snapshots<br />";\r
-                       print "<input type=\"submit\" value=\"Export\" /></form>";\r
-               }\r
-         }\r
-         \r
-         if(url_param('op') eq 'import') {\r
-           check_access();\r
-           if(param('fileupload')) {\r
-             do_import();\r
-           } else {\r
-             print <<IFORM;\r
-       <p>This allows you to import either \r
-       <a href="http://www.neuro-tech.net/insipid/">Insipid</a> or \r
-       <a href="http://del.icio.us/">del.icio.us</a> backups.  For del.icio.us, you\r
-       must first use their API to export your bookmarks to an XML file.  To do this,\r
-       access the URL "http://username:password\@del.icio.us/api/posts/all?" \r
-       (using your username and password).  You can then upload that XML file here.\r
-       </p>\r
-       <br />\r
-       <form class="formtext" enctype="multipart/form-data" action="$site_url/insipid.cgi?op=import" method="post">\r
-       Import from:<br />\r
-       <input type="file" name="fileupload" size="30"><br />\r
-       <input type="hidden" name="op" value="import" />\r
-       <input type="submit" value="Import" />\r
-       </form>\r
-IFORM\r
-           }\r
-         }\r
-       }\r
-\r
-       if(defined(param('op'))) {\r
-         if(param('op') eq 'login') {\r
-             login_form();\r
-         } \r
-\r
-         if((param('op') eq 'add_bookmark') || (param('op') eq 'edit_bookmark') ) {\r
-           #check to see if the url is bookmarked, then indicate that this is an edit.\r
-           my ($id, $url, $title, $description, $button, \r
-               $tags, $extra_params, $access_level, $access_box) =\r
-              (-1, "", "", "", "",  "", "", 0, "");\r
-\r
-           $access_level = 0;\r
-         \r
-           if(defined(param('save'))) {\r
-             ($url, $title, $description, $tags) = \r
-               (param('url'), param('title'), param('description'), param('tags'));\r
-\r
-             if(defined(param('access_level'))) {\r
-               if(param('access_level') eq 'on') {\r
-                 $access_level = 1;\r
-               } else {\r
-                 $access_level = 0;\r
-               }\r
-             }\r
-               \r
-             if(param('id')) {\r
-               update_bookmark(param('id'), $url, $title, $description, $access_level, $tags);\r
-             } else {\r
-               add_bookmark($url, $title, $description, $access_level, 0, $tags);\r
-               if(param('snapshot')) {\r
-                 if(param('snapshot') eq 'on') {\r
-                   $id = get_bookmark_id(param('url'));\r
-                   do_snapshot($id);\r
-                 }\r
-               }\r
-             }\r
-\r
-             if(param('redirect')) {\r
-               if(param('redirect') eq 'on') {\r
-                 if(@errors eq 0) {\r
-                   $redirect = $url;\r
-                 }\r
-               }\r
-             }\r
-           } else {\r
-             # Show the form, populating from the database if it's an existing entry.\r
-             my $utext = "URL:";\r
-             my $snapshot_params = "";\r
-             $id = "-1";\r
-\r
-             if(defined(param('id'))) { $id = param('id'); }\r
-             if(defined(url_param('id'))) { $id = url_param('id'); }\r
-\r
-             if($id eq "-1") { \r
-               if(defined(param('url'))) {\r
-                 $id = get_bookmark_id(param('url')); \r
-               }\r
-             }\r
-           \r
-             if($id ne -1)  {\r
-               ($url, $title, $description, $access_level) = get_bookmark($id);\r
-               $tags = get_tags($url);\r
-               $button = "Save";\r
-               $utext = "<span style=\"color:red\">URL (already bookmarked):</span>";\r
-               $extra_params = "<input type=\"hidden\" name=\"id\" value=\"$id\" />";\r
-             } else {\r
-               # There has to be a nicer way to do this.\r
-               if(param('url'))         { $url = param('url'); }\r
-               if(param('title'))       { $title = param('title'); }\r
-               if(param('description')) { $description = param('description'); }\r
-               $access_level = 1;\r
-               $button = "Add";\r
-               $snapshot_params = "<span class=\"formtext\">Snapshot:</span><input type=\"checkbox\" name=\"snapshot\" />\n";\r
-             }\r
-         \r
-             my $style = "style=\"width:500px\"";\r
-             my $redir = "off";\r
-             my $redir_box = "";\r
-             \r
-             if(param('redirect')) {\r
-                     if(param('redirect') eq 'on') { $redir = 'on'; }\r
-                     if(param('redirect') eq 'true') { $redir = 'on'; }\r
-             }\r
-\r
-             if($access_level eq 0) { $access_box = ""; } \r
-             else { $access_box = "checked=\"true\" "; }\r
-             \r
-             if($redir eq 'on') { $redir_box = "checked=\"true\""; } \r
-\r
-             print <<FORM;\r
+       </head>
+       <body marginheight="0" marginwidth="0">
+DOC
+
+    ###### Operations that don't touch the screen
+    if ( defined( param('op') ) && defined( param('id') ) ) {
+        if ( param('op') eq 'delete_bookmark' ) {
+            my $id = param('id');
+            delete_bookmark($id);
+        }
+    }
+
+    # If the user just saved a bookmark, redirect them now.
+    if ( $redirect ne "" ) {
+        print
+"<script language=\"JavaScript\">document.location = \"$redirect\";</script>";
+        print "</body></html>";
+        exit;
+    }
+
+    show_toolbar();
+    show_tags();
+
+    print '<table class="bodyContent" border="0"><tr><td>';
+
+    if ( defined( url_param('op') ) ) {
+        if ( url_param('op') eq 'export' ) {
+            if ( !defined( param('target') ) ) {
+                print "<br /><br /><form method=\"post\" class=\"formText\">";
+                print "<input type=\"checkbox\" name=\"snapshots\" />Include Snapshots<br />";
+                print "<input type=\"submit\" value=\"Export\" /></form>";
+            }
+        }
+
+        if ( url_param('op') eq 'import' ) {
+            check_access();
+            if ( param('fileupload') ) {
+                do_import();
+            }
+            else {
+                print <<IFORM;
+       <p>This allows you to import either 
+       <a href="http://www.neuro-tech.net/insipid/">Insipid</a> or 
+       <a href="http://del.icio.us/">del.icio.us</a> backups.  For del.icio.us, you
+       must first use their API to export your bookmarks to an XML file.  To do this,
+       access the URL "http://username:password\@del.icio.us/api/posts/all?" 
+       (using your username and password).  You can then upload that XML file here.
+       </p>
+       <br />
+       <form class="formtext" enctype="multipart/form-data" action="$site_url/insipid.cgi?op=import" method="post">
+       Import from:<br />
+       <input type="file" name="fileupload" size="30"><br />
+       <input type="hidden" name="op" value="import" />
+       <input type="submit" value="Import" />
+       </form>
+IFORM
+            }
+        }
+    }
+
+    if ( defined( param('op') ) ) {
+        if ( param('op') eq 'login' ) {
+            login_form();
+        }
+
+        if (   ( param('op') eq 'add_bookmark' )
+            || ( param('op') eq 'edit_bookmark' ) )
+        {
+
+            if ( logged_in() ne 1 ) {
+                push( @errors,
+                    "You have to be logged in to perform that operation." );
+
+                #return;
+            }
+
+     #check to see if the url is bookmarked, then indicate that this is an edit.
+            my (
+                $id,           $url,          $title,
+                $description,  $button,       $tags,
+                $extra_params, $access_level, $access_box
+            ) = ( -1, "", "", "", "", "", "", 0, "" );
+
+            $access_level = 0;
+
+            if ( defined( param('save') ) ) {
+                ( $url, $title, $description, $tags ) = (
+                    param('url'),         param('title'),
+                    param('description'), param('tags')
+                );
+
+                if ( defined( param('access_level') ) ) {
+                    if ( param('access_level') eq 'on' ) {
+                        $access_level = 1;
+                    }
+                    else {
+                        $access_level = 0;
+                    }
+                }
+
+                if ( param('id') ) {
+                    update_bookmark( param('id'), $url, $title, $description,
+                        $access_level, $tags );
+                }
+                else {
+                    add_bookmark( $url, $title, $description, $access_level, 0,
+                        $tags );
+                    if ( param('snapshot') ) {
+                        if ( param('snapshot') eq 'on' ) {
+                            $id = get_bookmark_id( param('url') );
+                            do_snapshot($id);
+                        }
+                    }
+                }
+
+                if ( param('redirect') ) {
+                    if ( param('redirect') eq 'on' ) {
+                        if ( @errors eq 0 ) {
+                            $redirect = $url;
+                        }
+                    }
+                }
+            }
+            else {
+
+        # Show the form, populating from the database if it's an existing entry.
+                my $utext           = "URL:";
+                my $snapshot_params = "";
+                $id = "-1";
+
+                if ( defined( param('id') ) )     { $id = param('id'); }
+                if ( defined( url_param('id') ) ) { $id = url_param('id'); }
+
+                if ( $id eq "-1" ) {
+                    if ( defined( param('url') ) ) {
+                        $id = get_bookmark_id( param('url') );
+                    }
+                }
+
+                if ( $id ne -1 ) {
+                    ( $url, $title, $description, $access_level ) =
+                      get_bookmark($id);
+                    $tags   = get_tags($url);
+                    $button = "Save";
+                    $utext = "<span style=\"color:red\">URL (already bookmarked):</span>";
+                    $extra_params = "<input type=\"hidden\" name=\"id\" value=\"$id\" />";
+                }
+                else {
+
+                    # There has to be a nicer way to do this.
+                    if ( param('url') )   { $url   = param('url'); }
+                    if ( param('title') ) { $title = param('title'); }
+                    if ( param('description') ) {
+                        $description = param('description');
+                    }
+                    $access_level = 1;
+                    $button       = "Add";
+                    $snapshot_params = "<span class=\"formtext\">Snapshot:</span><input type=\"checkbox\" name=\"snapshot\" />\n";
+                }
+
+                my $style     = "style=\"width:500px\"";
+                my $redir     = "off";
+                my $redir_box = "";
+
+                if ( param('redirect') ) {
+                    if ( param('redirect') eq 'on' )   { $redir = 'on'; }
+                    if ( param('redirect') eq 'true' ) { $redir = 'on'; }
+                }
+
+                if ( $access_level eq 0 ) { $access_box = ""; }
+                else                      { $access_box = "checked=\"true\" "; }
+
+                if ( $redir eq 'on' ) { $redir_box = "checked=\"true\""; }
+
+                print <<FORM;
                  <script type="text/javascript" src="lib/js/tagsSuggest.js"></script>
-             <br />\r
-             <form method="post">\r
-             <span class="formtext">$utext</span><br />\r
-             <input name="url" $style value="$url" /><br />\r
-             <span class="formtext">Title:</span><br />\r
-             <input name="title" $style value="$title" /><br />\r
-             <span class="formtext">Description:</span><br />\r
-             <input name="description" $style value="$description" /><br />\r
-             <span class="formtext">Tags:</span><br />\r
-             <input name="tags" $style value="$tags" /><br />\r
+             <br />
+             <form method="post">
+             <span class="formtext">$utext</span><br />
+             <input name="url" $style value="$url" /><br />
+             <span class="formtext">Title:</span><br />
+             <input name="title" $style value="$title" /><br />
+             <span class="formtext">Description:</span><br />
+             <input name="description" $style value="$description" /><br />
+             <span class="formtext">Tags:</span><br />
+             <input name="tags" $style value="$tags" /><br />
                  <div id="suggestlist"><ul></ul></div>
-             $snapshot_params\r
-             <span class="formtext">Public:</span>\r
-             <input type="checkbox" name="access_level" $access_box />\r
-             <span class="formtext">Return:</span>\r
-             <input type="checkbox" name="redirect" $redir_box />\r
-             <input type="hidden" name="save" value="true" />\r
-             <input type="hidden" name="op" value="add_bookmark" />\r
-             $extra_params\r
-             <input type="submit" value="$button" />\r
-             </form>\r
-FORM\r
-           }\r
-         }\r
-       }\r
-\r
-       # Late redirects.  TODO: Get rid of this.\r
-       if($redirect ne "") {\r
-         print "<script language=\"JavaScript\">document.location = \"$redirect\";</script>";\r
-         print "</body></html>";\r
-         exit;\r
-       }\r
-\r
-\r
-       if(defined(param('op'))) {\r
-         if(logged_in() eq 1) {\r
-           if(param('op') eq 'fetchrelated') {\r
-               if(defined(param('id'))) {\r
-                       fetch_related(param('id'));\r
-               }\r
-           }\r
-\r
-           if(param('op') eq 'snapshots') {\r
-             show_snapshots();\r
-             print "</body></html>";\r
-             exit;\r
-           }\r
-\r
-           if(param('op') eq 'snapshot') {\r
-             if(defined(param('id'))) {\r
-               do_snapshot(param('id'));\r
-               print "</body></html>";\r
-               exit;\r
-             }\r
-           }\r
-\r
-           if(param('op') eq 'bookmarklets') {\r
-             print <<DESC;\r
-<p>This bookmarklet provides a fast way to add your browser's \r
-current page to this Insipid installation.  Either drag the \r
-following link to your bookmarks toolbar or right-click on it \r
-and choose "Bookmark This Link..." to create a bookmarklet.  \r
-Then when you're on a page you'd like to save, click on your \r
-new "Add to Insipid" button and you'll be brought to a page \r
-that allows you to fill out the tags for the bookmark and save \r
-it.  Once you've clicked Save you'll be brought back to the \r
-page.</p>\r
-DESC\r
-             my $ad = <<BLET;\r
-       javascript:location.href='$site_url/insipid.cgi?op=add_bookmark&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)+'&redirect=true'\r
-BLET\r
-             print "<ul><li><a href=\"$ad\">Add to Insipid</a></li></ul>";\r
-             print "</body></html>";\r
-             exit;\r
-           }\r
-\r
-           # Configuration and management pages\r
-           if(param('op') eq 'tags') {\r
-             tag_operations();\r
-             print '</body></html>';\r
-             exit;\r
-           }\r
-\r
-           if(param('op') eq 'options') {\r
-             show_options();\r
-             print '</body></html>';\r
-             exit;\r
-           }\r
-         }\r
-       }\r
-\r
-       foreach (@errors) {\r
-         print "<div class=\"error\">$_</div>";\r
-       }\r
-\r
-       show_bookmarks();\r
-\r
-       print "</td></tr></table><br /></body></html>";\r
-} # main\r
-\r
-\r
-\r
-################################################################\r
-\r
-sub show_options {\r
-       # Save options if they were posted.\r
-       print "<br /><br />";\r
-       if(param('save')) {\r
-               my $sql = "update $tbl_options set value=? \r
-                       where (name = ?)";\r
-               my $sth = $dbh->prepare($sql);\r
-\r
-               my %save;\r
-               foreach my $p (@valid) {\r
-                       if(param($p)) {\r
-                               $save{$p} = param($p);\r
-                       }\r
-               }\r
-\r
-               foreach my $k (keys %save) {\r
-                       $sth->execute($save{$k}, $k);\r
-               }\r
-\r
-               # The proxy_host can be empty, so check for that.\r
-               if(!defined($save{'proxy_host'})) {\r
-                       $sth->execute('', 'proxy_host');\r
-               }\r
-\r
-               print "<div class=\"error\">Your options have been saved.</div>";\r
-       }\r
-\r
-       # Now show em\r
-       my $sql = "select name, description, value from $tbl_options";\r
-       my $sth = $dbh->prepare($sql);\r
-       $sth->execute();\r
-\r
-       print "<form method=\"post\">";\r
-       print "<table id=\"options\" cellpadding=5 cellspacing=5>";\r
-       while(my $hr = $sth->fetchrow_hashref) {\r
-               print "<td>$hr->{'description'}</td>";\r
-               if($hr->{'name'} eq 'version') {\r
-                       print "<td>$hr->{'value'}</td>";\r
-               } else {\r
-                       print "<td><input name=\"$hr->{'name'}\" value=\"$hr->{'value'}\" /></td>";\r
-               }\r
-               print "</tr>";\r
-       }\r
-\r
-       print "<input type=hidden name=op value=options>";\r
-       print "<input type=hidden name=save value=yes>";\r
-       print "<tr><td></td><td><input type='submit' value='Save'></td></tr>";\r
-       print "</table></form>";\r
-}\r
-\r
-sub show_footer {\r
-       my $older = 2;\r
-       if(defined(url_param('page'))) {\r
-               $older = url_param('page') + 1;\r
-       }\r
-       \r
-       if($last_page eq 0) { \r
-         if($query ne "") {\r
-           print " | <a href=\"?page=$older&q=";\r
-           print $query;\r
-           print "\">More Results</a>";\r
-         } else {\r
-           print " | <a href=\"?page=$older\">older</a>"; \r
-         }\r
-       }\r
-}\r
-\r
-sub do_import {\r
-       my $old_fh = select(OUTPUT_HANDLE);\r
-       my $cbuffer = ""; my $pcount = 0;\r
-       $| = 1;\r
-       select($old_fh);\r
-\r
-       my ($omd5, $ourl, $otype, $olength, $odate, $sql, \r
-                       $oadd, $omod, $otags);\r
-       my $ispec = '';\r
-\r
-       if($dbtype eq 'mysql') { $ispec = " ignore "; }\r
-       \r
-       $sql = "insert $ispec into pagecache_references\r
-                       (md5_parent, md5_child) values(?,?)";\r
-       my $insert_reference = $dbh->prepare($sql);\r
-\r
-       $sql = "insert $ispec into pagecache\r
-                       (md5,url,content_type,\r
-                       content_length,content,date)\r
-                       values(?,?,?,?,?,?)";\r
-       my $insert_snapshot = $dbh->prepare($sql);\r
-\r
-       $sql = 'update options set value = ? where (name = ?)';\r
-       my $update_option = $dbh->prepare($sql);\r
-\r
-       my $parser = new XML::Parser();\r
-       $parser->setHandlers(Start => sub {\r
-               my %attr;\r
-               my $expat = shift;\r
-               my $element = shift;\r
-\r
-               while(@_) {\r
-                       my $att = shift;\r
-                       my $val = shift;\r
-                       $attr{$att} = $val;\r
-               }\r
-\r
-               # netscape stuff\r
-               if ( $element eq "A" ) {\r
-                   $ourl = $attr{'HREF'};\r
-                   $oadd = $attr{'ADD_DATE'};\r
-                   $omod = $attr{'LAST_MODIFIED'};\r
-                   $otags = $attr{'TAGS'};\r
-                   $cbuffer = '';\r
-               }\r
-       \r
-               # A pagecache object\r
-               if($element eq "object") {\r
-                       $omd5 = $attr{'md5'};\r
-                       $ourl = $attr{'url'};\r
-                       $otype = $attr{'type'};\r
-                       $olength = $attr{'length'};\r
-                       $odate = $attr{'date'};\r
-               }\r
-\r
-               # A pagecache relationship\r
-               if($element eq "relationship") {\r
-                       my $parent = $attr{parent};\r
-                       my $child = $attr{child};\r
-                       $insert_reference->execute($parent, $child);\r
-               }\r
-               \r
-               # A bookmark\r
-               if($element eq "post") {\r
-                       my $url = $attr{href};\r
-                       my $title = $attr{description};\r
-                       my $tagvalue = $attr{tag};\r
-                       my $datevalue = $attr{time};\r
-                       my $access_level;\r
-                       if(defined($attr{access_level})) {\r
-                               $access_level = $attr{access_level};\r
-                       } else {\r
-                               $access_level = 1;\r
-                       }\r
-\r
-                       my $epoch = str2time($datevalue);\r
-\r
-                       if($tagvalue eq "system:unfiled") {\r
-                               $tagvalue = "";\r
-                       }\r
-\r
-                       add_bookmark($url, $title, "", $access_level,\r
-                               $epoch, $tagvalue, 1);\r
-               }\r
-\r
-               # Option\r
-               if($element eq 'option') {\r
-                       $update_option->execute(\r
-                               $attr{value}, $attr{name});\r
-               }\r
-       }, Char => sub {\r
-               my $expat = shift;\r
-               my $chars = shift;\r
-               $cbuffer = $cbuffer . $chars;\r
-               1;\r
-       }, End => sub {\r
-               my $expat = shift;\r
-               my $element = shift;\r
-    \r
-        #\r
-        # netscape stuff\r
-        #\r
-        if($element eq 'A') {\r
-            add_bookmark(\r
-                $ourl,                # $url, \r
-                $cbuffer,             # $title, \r
-                "",\r
-                '1',                  # $access_level,\r
-                $omod,                # $epoch, \r
-                $otags,               # $tagvalue\r
-            );\r
-            $cbuffer = '';\r
-        }\r
-        if ( $element eq 'TITLE' || $element eq 'H1' || $element eq 'DD') {\r
-            $cbuffer = '';\r
-        }\r
-        \r
-               if($element eq 'object') {\r
-\r
-                       $insert_snapshot->bind_param(1, $omd5);\r
-                       $insert_snapshot->bind_param(2, $ourl);\r
-                       $insert_snapshot->bind_param(3, $otype);\r
-                       $insert_snapshot->bind_param(4, $olength);\r
-                       \r
-                       if($dbtype eq "Pg") {\r
-                               $insert_snapshot->bind_param(5, \r
-                                       decode_base64($cbuffer), SQL_VARBINARY);\r
-                       } else {\r
-                               $insert_snapshot->bind_param(5, \r
-                                       decode_base64($cbuffer));\r
-                       }\r
-\r
-                       $insert_snapshot->bind_param(6, $odate);\r
-                       $insert_snapshot->execute;\r
-                       \r
-                       if(!defined($DBI::errstr)) { $pcount++; }\r
-\r
-                       $cbuffer = "";\r
-               }\r
-       });\r
-\r
-       my $xml = "";\r
-\r
-#      BEGIN {\r
-#        *CORE::GLOBAL::die = sub {\r
-#          print "Some errors were detected. ";\r
-#        };\r
-#      }\r
-\r
-       if(defined($ENV{SERVER_NAME})) {\r
-               my $fh = upload('fileupload');\r
-               while(<$fh>) {\r
-                       $xml .= $_;\r
-               }\r
-       \r
-               $parser->parse($xml);\r
-       } else {\r
-               if(!defined($ARGV[0])) {\r
-                       print "Please specify the filename to import.\n\n";\r
-                       exit;\r
-               }\r
-       \r
-               my $fn = $ARGV[0];\r
-               $parser->parsefile($fn);\r
-       }\r
-       \r
-       print "Import finished - $icount bookmarks and $pcount " .\r
-                       "snapshot objects imported.$NL";\r
-}\r
-\r
-sub do_export {\r
-       my ($snapshots, $islocal) = (@_);\r
-       my $writer;\r
-       \r
-       if(!defined($islocal)) {\r
-               print "Content-Type: text/xml;charset=UTF-8\r\n";\r
-               print "Content-Disposition: attachment; filename=bookmarks.xml\r\n\r\n";\r
-       }\r
-\r
-       $writer = new XML::Writer();\r
-\r
-       $writer->xmlDecl('UTF-8');\r
-       $writer->startTag('insipid');\r
-\r
-       export_bookmarks($writer);\r
-       export_snapshots($writer);\r
-       export_options($writer);\r
-       \r
-       $writer->endTag('insipid');\r
-       $writer->end();\r
-       exit;\r
-}\r
-\r
-sub login_form {\r
-      print <<FORM;\r
-<br />\r
-<form method="post" action="$site_url/insipid.cgi">\r
-<span class="formtext">Username:</span><br />\r
-<input style="width:250px" name="username" /><br />\r
-<span class="formtext">Password:</span><br />\r
-<input style="width:250px" name="password" type="password" /><br />\r
-<input type="submit" value="Login" />\r
-</form>\r
-<br />\r
-FORM\r
-}\r
-\r
-\r
-sub show_toolbar {\r
-       my $rdata = "";\r
-       if(defined(url_param('tag'))) {\r
-               $rdata = url_param('tag');\r
-               $rdata =~ s/ /\+/g;\r
-       }\r
-\r
-       # Toolbar\r
-       print '<center>';\r
-       print '<table border="0" width="100%" cellspacing="0" cellpadding="3"><tr>';\r
-       print '<td valign="top" bgcolor="#CCCCCC">';\r
-\r
-       # Title\r
-       print '<div class="title"><a href="';\r
-       if(get_option('use_rewrite') eq 'yes') {\r
-               print $site_url . '/bookmarks';\r
-       } else {\r
-               print 'insipid.cgi';\r
-       }\r
-       print '">' . $site_title . '</a></div>';\r
-  \r
-       if((get_option("public_searches") eq "yes") || (logged_in() eq 1)) {\r
-               print "<div class=\"search\">";\r
-               print "<form action=\"$site_url/bookmarks\" method=\"post\">";\r
-               print "<input type=\"text\" name=\"q\"> <input type=\"submit\" value=\"search\">";\r
-               print "</form>";\r
-               print "</div>";\r
-       } else {\r
-               print "&nbsp;";\r
-       }\r
-    \r
-       print "</td><td valign=\"top\" bgcolor=\"#CCCCCC\" align=\"right\">";\r
-       print "<div class=\"toolbar\">";\r
-  \r
-       if(logged_in() eq 1) {\r
-               print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=options\">options</a> | "; \r
-               print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=tags\">tags</a> | ";\r
-               print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=import\">import</a> | ";\r
-               print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=export\">export</a> | ";\r
-               print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=snapshots\">snapshots</a> | ";\r
-               print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=logout\">logout</a><br />";\r
-               print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=add_bookmark\">add</a> | ";\r
-               print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=bookmarklets\">bookmarklets</a> | ";\r
-       }\r
-\r
-       my $rf;\r
-       if(get_option('use_rewrite') eq 'yes') {\r
-               $rf = $feed_url . '/' . $rdata;\r
-       } else {\r
-               $rf = $feed_url . $rdata;\r
-       }\r
-       \r
-       print "<a class=\"tools\" href=\"$rf\">RSS feed</a>";\r
-\r
-       if(logged_in() ne 1) {\r
-               print " | <a class=\"tools\" href=\"$site_url/insipid.cgi?op=login\">login</a>";\r
-       }\r
-\r
-       print " | <a class=\"tools\" href=\"javascript:void window.open('$site_url/help.html','width=300,height=500');\">help</a> ";\r
-       print " | <a class=\"tools\" href=\"http://www.neuro-tech.net/insipid/\">source</a>";\r
-\r
-       print "</div></tr></table></center>";\r
-}\r
-\r
-sub delete_bookmark {\r
-       my($id) = (@_);\r
-       my($sql, $sth, $md5) = ("", "", "");\r
-\r
-       if(logged_in() ne 1) {\r
-         push(@errors, "You have to be logged in to perform that operation.");\r
-         return;\r
-       }\r
-\r
-       # Check for cached version to delete.\r
-       $sql = "select $tbl_pagecache.md5 from $tbl_pagecache \r
-               inner join $tbl_bookmarks on \r
-               ($tbl_pagecache.md5 = $tbl_bookmarks.md5) \r
-               where ($tbl_bookmarks.id = ?)";\r
-       $sth = $dbh->prepare($sql);\r
-       $sth->execute($id);\r
-       while(my @r = $sth->fetchrow_array) {\r
-               $md5 = $r[0];\r
-       }\r
-\r
-       # Drop the tags for the bookmark\r
-       $sql = "delete from $tbl_bookmark_tags where (bookmark_id = ?)";\r
-       $sth = $dbh->prepare($sql);\r
-       $sth->execute($id);\r
-       \r
-       # Drop the bookmark.\r
-       $sql = "delete from $tbl_bookmarks where (id = ?)";\r
-       $sth = $dbh->prepare($sql);\r
-       $sth->execute($id);\r
-\r
-       # Delete the cached page.\r
-       if($md5 ne "") { delete_snapshot($md5); }\r
-}\r
-\r
-sub show_bookmarks {\r
-       my ($subquery, $sql, $sth, @parms, @wheres, @hr);\r
-       \r
-       # this first query will be used to select from a set, like when a user \r
-       # drills in on a specific tag or to get a smaller view of the entire\r
-       # dataset (for paging purposes).\r
-\r
-       # MySQL and postgres have slightly different syntax here...\r
-       if ($dbtype eq 'mysql') {\r
-           $sql = "select $tbl_bookmarks.id from $tbl_bookmarks";\r
-       } elsif ($dbtype eq 'Pg') {\r
-           $sql = "select $tbl_bookmarks.id, $tbl_bookmarks.date \r
-               from $tbl_bookmarks";\r
-       }\r
-\r
-       # Limit to tags\r
-       if(defined(url_param('tag'))) {\r
-               # Join the tag tables only when necessary\r
-       \r
-               if(url_param('tag') =~ / /) {\r
-                       my @tags = split(/ /, url_param('tag'));\r
-                       my $icount = 1;\r
-                       \r
-                       foreach(@tags) {                \r
-                               push(@parms, $_);\r
-                               $sql = "$sql inner join $tbl_bookmark_tags \r
-                                               as bt$icount on \r
-                                         ($tbl_bookmarks.id = \r
-                                               bt$icount.bookmark_id)\r
-                                       inner join $tbl_tags as t$icount on \r
-                                          (t$icount.id = bt$icount.tag_id \r
-                                               and t$icount.name = ?) ";\r
-                               $icount++;\r
-                       }\r
-               } else {\r
-                       $sql = "$sql \r
-                               left join $tbl_bookmark_tags on \r
-                                 ($tbl_bookmarks.id = \r
-                                       $tbl_bookmark_tags.bookmark_id) \r
-                               inner join $tbl_tags on \r
-                                 ($tbl_tags.id = $tbl_bookmark_tags.tag_id) \r
-                                 where ($tbl_tags.name = ?)";\r
-                       push(@parms, url_param('tag'));\r
-               }\r
-\r
-       }\r
-\r
-       # Search\r
-       if($query ne "") {\r
-               if((get_option("public_searches") eq "yes") || (logged_in() eq 1)) {\r
-                       my $sparm = $query;\r
-                       if(length($sparm) > 2) {\r
-                               $sql = "$sql where ($tbl_bookmarks.title like ?)";\r
-                               $sparm =~ s/\%//;\r
-                               $sparm = "\%$sparm\%";\r
-                               push(@parms, $sparm);\r
-                       }\r
-               }\r
-       }\r
-\r
-       # order\r
-       $sql = "$sql order by $tbl_bookmarks.date desc";\r
-\r
-       # paging functionality\r
-       $sql = "$sql limit 50";\r
-       \r
-       if(defined(url_param('page'))) {\r
-           my $offset = ((url_param('page') - 1) * 50);\r
-           $sql = "$sql offset $offset";\r
-       }\r
-\r
-       $sth = $dbh->prepare($sql);\r
-       $sth->execute(@parms);\r
-\r
-       $subquery = "";\r
-       if($sth->rows > 0) {\r
-               if($sth->rows ne 50) { $last_page = 1; }\r
-       \r
-               $subquery = " $tbl_bookmarks.id in (";\r
-               \r
-               while(@hr = $sth->fetchrow_array) {\r
-                       $subquery = $subquery . "$hr[0],";\r
-               }\r
-               chop($subquery); # Strip off the last delimiter\r
-               \r
-               $subquery = $subquery . ")";\r
-       } else {\r
-               print "<p>No bookmarks found.</p>";\r
-               return;\r
-       }\r
-\r
-       @parms = ();\r
-       @wheres = ();\r
-       \r
-        $sql = "select \r
-                 $tbl_bookmarks.id, \r
-                 $tbl_bookmarks.title, \r
-                 $tbl_bookmarks.description, \r
-                 $tbl_bookmarks.access_level,\r
-                 $tbl_bookmarks.url,\r
-                 $tbl_tags.name,\r
-                 $tbl_bookmarks.date,\r
-                 $tbl_pagecache.date as cache_date,\r
-                 $tbl_bookmarks.md5\r
-               from $tbl_bookmarks \r
-               left join $tbl_bookmark_tags on \r
-                 ($tbl_bookmarks.id = $tbl_bookmark_tags.bookmark_id)\r
-               left join $tbl_tags on \r
-                 ($tbl_tags.id = $tbl_bookmark_tags.tag_id)\r
-               left join $tbl_pagecache on\r
-                 ($tbl_bookmarks.md5 = $tbl_pagecache.md5)";\r
-       \r
-       # Don't show private marks for non-logged in users\r
-       if(logged_in() eq 0) {\r
-         push(@wheres, "$tbl_bookmarks.access_level");\r
-         push(@parms, "1");\r
-       }\r
-\r
-       my $max = @wheres;\r
-       if($max ne 0) {\r
-         $sql = "$sql where (";\r
-         my $count = 1;\r
-         \r
-         foreach (@wheres) {\r
-           $sql = "$sql $_ = ?";\r
-           if($count < $max) {\r
-             $sql = "$sql and ";\r
-           }\r
-           $count++;\r
-         }\r
-\r
-         $sql = "$sql )";\r
-         if($subquery ne "") { $sql = "$sql and $subquery"; }\r
-       } else {\r
-         if($subquery ne "") { $sql = "$sql where $subquery "; }\r
-       }\r
-\r
-        # append sort order.\r
-       $sql = "$sql order by $tbl_bookmarks.date desc";\r
-\r
-       $sth = $dbh->prepare($sql);\r
-        $sth->execute(@parms);\r
-\r
-       my %last;\r
-       $last{id} = -1;\r
-\r
-       print '<ul><br />';\r
-\r
-       my $title = '';\r
-       if(defined(url_param('tag'))) {\r
-               my $temp = url_param('tag');\r
-               if($temp =~ / /) {\r
-                       my $count = 0;\r
-                       foreach(split(/ /, $temp)) {\r
-                               if($count++ ne 0) { $title = "$title +"; }\r
-                               $title = "$title <a class=\"bodyTitle\" href=\"$tag_url$_\">$_</a>";\r
-                       }\r
-               } else {\r
-                       $title = "<a class=\"bodyTitle\" href=\"$tag_url$temp\">$temp</a>";\r
-               }\r
-       } else {\r
-               $title = 'Most Recent Bookmarks';\r
-       }\r
-\r
-       if($query ne '') { \r
-               $title = sprintf("Search results for \"%s\"", $query); \r
-       }\r
-         \r
-       print "<span class=\"bodyTitle\">$title</span>";\r
-       show_footer();\r
-       print '<br /><br />';\r
-\r
-       print "<table class=\"bookmarklist\">";\r
-       print '<tr><td>';\r
-       print "<ul type=\"circle\">\n";\r
-       while(@hr = $sth->fetchrow_array) {\r
-               if($last{id} eq -1) {\r
-                 $last{id} = $hr[0];\r
-                 $last{title} = $hr[1];\r
-                 $last{description} = $hr[2];\r
-                 $last{access_level} = $hr[3];\r
-                 $last{url} = $hr[4];\r
-                 $last{tags} = "";\r
-                 $last{timestamp} = $hr[6];\r
-                 $last{cachetime} = $hr[7];\r
-                 $last{md5} = $hr[8];\r
-               } \r
-               \r
-               if($hr[0] ne $last{id}) {\r
-                 # the id changed, so show the last mark.\r
-                 show_bookmark($last{id}, $last{title}, $last{description}, $last{access_level}, $last{url}, $last{tags}, $last{timestamp}, $last{cachetime}, $last{md5});     \r
-\r
-                 # Swap the new one in.\r
-                 $last{id} = $hr[0];\r
-                 $last{title} = $hr[1];\r
-                 $last{description} = $hr[2];\r
-                 $last{access_level} = $hr[3];\r
-                 $last{url} = $hr[4];\r
-                 $last{tags} = $hr[5];\r
-                 $last{timestamp} = $hr[6];\r
-                 $last{cachetime} = $hr[7];\r
-                 $last{md5} = $hr[8];\r
-               } else {\r
-                 # Add tag to the current bookmark\r
-                 if(defined($hr[5])) {\r
-                   $last{tags} = "$last{tags} $hr[5]";\r
-                 }\r
-               }\r
-       }\r
-\r
-       if($last{id} ne -1) {\r
-               show_bookmark($last{id}, $last{title}, $last{description}, $last{access_level}, $last{url}, $last{tags}, $last{timestamp}, $last{cachetime}, $last{md5});\r
-       }\r
-\r
-       print "</ul></td></tr></table>";\r
-}\r
-\r
-sub show_bookmark {\r
-       my($id, $title, $description, $access_level, $url, \r
-               $tags, $timestamp, $cachetime, $md5) = (@_);\r
-\r
-       print "<div class=\"bookmarklistitem\">";\r
-       print "<li>";\r
-       if($access_level eq 0) { \r
-               print "<a href=\"$site_url/insipid.cgi?go=$id\">";\r
-               print "<i>";\r
-               print "$title";\r
-               print "</i>";\r
-       } else {\r
-               print "<a href=\"$url\">";\r
-               print $title;\r
-       }\r
-\r
-       if(logged_in() eq 1) {\r
-               if(defined($cachetime)) {\r
-                       print "</a> - <a href=\"$snapshot_url$md5\">view snapshot";\r
-               }\r
-       }\r
-       \r
-       print "</a><br /><div class=\"bookmarkOperations\">";\r
-\r
-       \r
-       my $timestr = "";\r
-       if(logged_in() eq 1) {\r
-               $timestr = time2str("%Y-%m-%d %T EST", $timestamp, "EST");\r
-       } else {\r
-               $timestr = time2str("%Y-%m-%d", $timestamp, "EST");\r
-       }\r
-\r
-       print "posted on $timestr ";\r
-           \r
-       if(defined($tags)) {\r
-         print "to ";\r
-         my $cur;\r
-           \r
-         foreach $cur (split(/\ /, $tags)) {\r
-           print '<a class="bookmarkTag" href="';\r
-           print $tag_url . $cur . '">' . $cur . '</a> ';\r
-         }\r
-       }\r
-\r
-       if(logged_in() eq 1) {\r
-               my $ex = "";\r
-\r
-               if(url_param('tag')) { $ex = "$ex&tag=" . url_param('tag'); }\r
-               if(url_param('page')) { $ex = "$ex&page=" . url_param('page'); }\r
-               if($query ne "") { $ex = "$ex&q=" . $query; }\r
-         \r
-               print "<span class=\"bodytext\">&nbsp;&mdash;&nbsp;";\r
-               print "(<a class=\"bookmarkOp\" href=\"$site_url/insipid.cgi?op=delete_bookmark&id=$id$ex\">delete</a>,&nbsp;";\r
-               print "<a class=\"bookmarkOp\" href=\"$site_url/insipid.cgi?op=edit_bookmark&id=$id$ex\">edit</a>";\r
-               if(!defined($cachetime)) {\r
-                       print ",&nbsp;<a class=\"bookmarkOp\" href=\"$site_url/insipid.cgi?op=snapshot&id=$id$ex\">snapshot</a>";\r
-               }\r
-               print ")<div class=\"bookmarkDescription\">$description</div></span></div></li>\n";\r
-       }\r
-       \r
-       print "</div>\n";\r
-}\r
-\r
-# Gets the ID for a bookmark if it already exists in the DB. Otherwise, -1.\r
-sub get_bookmark_id {\r
-       my ($url) = (@_);\r
-       \r
-       # Lookup the URL id first.\r
-       my $sql = "select $tbl_bookmarks.id from \r
-               $tbl_bookmarks where ($tbl_bookmarks.md5 = ?)";\r
-       my $sth = $dbh->prepare($sql);\r
-\r
-       $sth->execute(md5_hex($url));\r
-\r
-       if($sth->rows ne 0) {\r
-               my @r = $sth->fetchrow_array;\r
-               return $r[0];\r
-       }\r
-       \r
-       return -1;\r
-}\r
-\r
-sub get_bookmark {\r
-       my ($id) = (@_);\r
-\r
-       my $sql = "select \r
-                       $tbl_bookmarks.title, \r
-                       $tbl_bookmarks.description, \r
-                       $tbl_bookmarks.url,\r
-                       $tbl_bookmarks.access_level \r
-                       from $tbl_bookmarks \r
-                       where ($tbl_bookmarks.id = ?)";\r
-       my $sth = $dbh->prepare($sql);\r
-       $sth->execute($id);\r
-       my @r = $sth->fetchrow_array;\r
-       return ($r[2], $r[0], $r[1], $r[3]);\r
-}\r
-\r
-sub update_bookmark {\r
-       my ($id, $url, $title, $description, $access_level, $tags) = (@_);\r
-\r
-       if(logged_in() ne 1) {\r
-         push(@errors, "You have to be logged in to perform that operation.");\r
-         return;\r
-       }\r
-       \r
-       my $sql = "update $tbl_bookmarks \r
-                       set url = ?, md5 = ?, title = ?, description = ?, \r
-                       access_level = ? where (id = ?)";\r
-       my $sth = $dbh->prepare($sql);\r
-       $sth->execute($url, md5_hex("$url"), $title, $description, \r
-                       $access_level, $id);\r
-\r
-       set_tags($id, $tags);\r
-}\r
-\r
-1;\r
-__END__\r
+             $snapshot_params
+             <span class="formtext">Public:</span>
+             <input type="checkbox" name="access_level" $access_box />
+             <span class="formtext">Return:</span>
+             <input type="checkbox" name="redirect" $redir_box />
+             <input type="hidden" name="save" value="true" />
+             <input type="hidden" name="op" value="add_bookmark" />
+             $extra_params
+             <input type="submit" value="$button" />
+             </form>
+FORM
+            }
+        }
+    }
+
+    # Late redirects.  TODO: Get rid of this.
+    if ( $redirect ne "" ) {
+        print
+"<script language=\"JavaScript\">document.location = \"$redirect\";</script>";
+        print "</body></html>";
+        exit;
+    }
+
+    if ( defined( param('op') ) ) {
+        if ( logged_in() eq 1 ) {
+            if ( param('op') eq 'fetchrelated' ) {
+                if ( defined( param('id') ) ) {
+                    fetch_related( param('id') );
+                }
+            }
+
+            if ( param('op') eq 'snapshots' ) {
+                show_snapshots();
+                print "</body></html>";
+                exit;
+            }
+
+            if ( param('op') eq 'snapshot' ) {
+                if ( defined( param('id') ) ) {
+                    do_snapshot( param('id') );
+                    print "</body></html>";
+                    exit;
+                }
+            }
+
+            if ( param('op') eq 'bookmarklets' ) {
+                print <<DESC;
+<p>This bookmarklet provides a fast way to add your browser's 
+current page to this Insipid installation.  Either drag the 
+following link to your bookmarks toolbar or right-click on it 
+and choose "Bookmark This Link..." to create a bookmarklet.  
+Then when you're on a page you'd like to save, click on your 
+new "Add to Insipid" button and you'll be brought to a page 
+that allows you to fill out the tags for the bookmark and save 
+it.  Once you've clicked Save you'll be brought back to the 
+page.</p>
+DESC
+                my $ad = <<BLET;
+       javascript:location.href='$site_url/insipid.cgi?op=add_bookmark&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)+'&redirect=true'
+BLET
+                print "<ul><li><a href=\"$ad\">Add to Insipid</a></li></ul>";
+                print "</body></html>";
+                exit;
+            }
+
+            # Configuration and management pages
+            if ( param('op') eq 'tags' ) {
+                tag_operations();
+                print '</body></html>';
+                exit;
+            }
+
+            if ( param('op') eq 'options' ) {
+                show_options();
+                print '</body></html>';
+                exit;
+            }
+        }
+    }
+
+    foreach (@errors) {
+        print "<div class=\"error\">$_</div>";
+    }
+
+    show_bookmarks();
+
+    print "</td></tr></table><br /></body></html>";
+}    # main
+
+################################################################
+
+sub show_options {
+
+    # Save options if they were posted.
+    print "<br /><br />";
+    if ( param('save') ) {
+        my $sql = "update $tbl_options set value=? 
+                       where (name = ?)";
+        my $sth = $dbh->prepare($sql);
+
+        my %save;
+        foreach my $p (@valid) {
+            if ( param($p) ) {
+                $save{$p} = param($p);
+            }
+        }
+
+        foreach my $k ( keys %save ) {
+            $sth->execute( $save{$k}, $k );
+        }
+
+        # The proxy_host can be empty, so check for that.
+        if ( !defined( $save{'proxy_host'} ) ) {
+            $sth->execute( '', 'proxy_host' );
+        }
+
+        print "<div class=\"error\">Your options have been saved.</div>";
+    }
+
+    # Now show em
+    my $sql = "select name, description, value from $tbl_options";
+    my $sth = $dbh->prepare($sql);
+    $sth->execute();
+
+    print "<form method=\"post\">";
+    print "<table id=\"options\" cellpadding=5 cellspacing=5>";
+    while ( my $hr = $sth->fetchrow_hashref ) {
+        print "<td>$hr->{'description'}</td>";
+        if ( $hr->{'name'} eq 'version' ) {
+            print "<td>$hr->{'value'}</td>";
+        }
+        else {
+            print "<td><input name=\"$hr->{'name'}\" value=\"$hr->{'value'}\" /></td>";
+        }
+        print "</tr>";
+    }
+
+    print "<input type=hidden name=op value=options>";
+    print "<input type=hidden name=save value=yes>";
+    print "<tr><td></td><td><input type='submit' value='Save'></td></tr>";
+    print "</table></form>";
+}
+
+sub show_footer {
+    my $older = 2;
+    if ( defined( url_param('page') ) ) {
+        $older = url_param('page') + 1;
+    }
+
+    if ( $last_page eq 0 ) {
+        if ( $query ne "" ) {
+            print " | <a href=\"?page=$older&q=";
+            print $query;
+            print "\">More Results</a>";
+        }
+        else {
+            print " | <a href=\"?page=$older\">older</a>";
+        }
+    }
+}
+
+sub do_import {
+    my $old_fh  = select(OUTPUT_HANDLE);
+    my $cbuffer = "";
+    my $pcount  = 0;
+    $| = 1;
+    select($old_fh);
+
+    my ( $omd5, $ourl, $otype, $olength, $odate, $sql, $oadd, $omod, $otags );
+    my $ispec = '';
+
+    if ( $dbtype eq 'mysql' ) { $ispec = " ignore "; }
+
+    $sql = "insert $ispec into pagecache_references
+                       (md5_parent, md5_child) values(?,?)";
+    my $insert_reference = $dbh->prepare($sql);
+
+    $sql = "insert $ispec into pagecache
+                       (md5,url,content_type,
+                       content_length,content,date)
+                       values(?,?,?,?,?,?)";
+    my $insert_snapshot = $dbh->prepare($sql);
+
+    $sql = 'update options set value = ? where (name = ?)';
+    my $update_option = $dbh->prepare($sql);
+
+    my $parser = new XML::Parser();
+    $parser->setHandlers(
+        Start => sub {
+            my %attr;
+            my $expat   = shift;
+            my $element = shift;
+
+            while (@_) {
+                my $att = shift;
+                my $val = shift;
+                $attr{$att} = $val;
+            }
+
+            # netscape stuff
+            if ( $element eq "A" ) {
+                $ourl    = $attr{'HREF'};
+                $oadd    = $attr{'ADD_DATE'};
+                $omod    = $attr{'LAST_MODIFIED'};
+                $otags   = $attr{'TAGS'};
+                $cbuffer = '';
+            }
+
+            # A pagecache object
+            if ( $element eq "object" ) {
+                $omd5    = $attr{'md5'};
+                $ourl    = $attr{'url'};
+                $otype   = $attr{'type'};
+                $olength = $attr{'length'};
+                $odate   = $attr{'date'};
+            }
+
+            # A pagecache relationship
+            if ( $element eq "relationship" ) {
+                my $parent = $attr{parent};
+                my $child  = $attr{child};
+                $insert_reference->execute( $parent, $child );
+            }
+
+            # A bookmark
+            if ( $element eq "post" ) {
+                my $url       = $attr{href};
+                my $title     = $attr{description};
+                my $tagvalue  = $attr{tag};
+                my $datevalue = $attr{time};
+                my $access_level;
+                if ( defined( $attr{access_level} ) ) {
+                    $access_level = $attr{access_level};
+                }
+                else {
+                    $access_level = 1;
+                }
+
+                my $epoch = str2time($datevalue);
+
+                if ( $tagvalue eq "system:unfiled" ) {
+                    $tagvalue = "";
+                }
+
+                add_bookmark( $url, $title, "", $access_level, $epoch,
+                    $tagvalue, 1 );
+            }
+
+            # Option
+            if ( $element eq 'option' ) {
+                $update_option->execute( $attr{value}, $attr{name} );
+            }
+        },
+        Char => sub {
+            my $expat = shift;
+            my $chars = shift;
+            $cbuffer = $cbuffer . $chars;
+            1;
+        },
+        End => sub {
+            my $expat   = shift;
+            my $element = shift;
+
+            #
+            # netscape stuff
+            #
+            if ( $element eq 'A' ) {
+                add_bookmark(
+                    $ourl,       # $url,
+                    $cbuffer,    # $title,
+                    "",
+                    '1',         # $access_level,
+                    $omod,       # $epoch,
+                    $otags,      # $tagvalue
+                );
+                $cbuffer = '';
+            }
+            if ( $element eq 'TITLE' || $element eq 'H1' || $element eq 'DD' ) {
+                $cbuffer = '';
+            }
+
+            if ( $element eq 'object' ) {
+
+                $insert_snapshot->bind_param( 1, $omd5 );
+                $insert_snapshot->bind_param( 2, $ourl );
+                $insert_snapshot->bind_param( 3, $otype );
+                $insert_snapshot->bind_param( 4, $olength );
+
+                if ( $dbtype eq "Pg" ) {
+                    $insert_snapshot->bind_param( 5, decode_base64($cbuffer),
+                        SQL_VARBINARY );
+                }
+                else {
+                    $insert_snapshot->bind_param( 5, decode_base64($cbuffer) );
+                }
+
+                $insert_snapshot->bind_param( 6, $odate );
+                $insert_snapshot->execute;
+
+                if ( !defined($DBI::errstr) ) { $pcount++; }
+
+                $cbuffer = "";
+            }
+        }
+    );
+
+    my $xml = "";
+
+    #  BEGIN {
+    #    *CORE::GLOBAL::die = sub {
+    #      print "Some errors were detected. ";
+    #    };
+    #  }
+
+    if ( defined( $ENV{SERVER_NAME} ) ) {
+        my $fh = upload('fileupload');
+        while (<$fh>) {
+            $xml .= $_;
+        }
+
+        $parser->parse($xml);
+    }
+    else {
+        if ( !defined( $ARGV[0] ) ) {
+            print "Please specify the filename to import.\n\n";
+            exit;
+        }
+
+        my $fn = $ARGV[0];
+        $parser->parsefile($fn);
+    }
+
+    print "Import finished - $icount bookmarks and $pcount "
+      . "snapshot objects imported.$NL";
+}
+
+sub do_export {
+    my ( $snapshots, $islocal ) = (@_);
+    my $writer;
+
+    if ( !defined($islocal) ) {
+        print "Content-Type: text/xml;charset=UTF-8\r\n";
+        print "Content-Disposition: attachment; filename=bookmarks.xml\r\n\r\n";
+    }
+
+    $writer = new XML::Writer();
+
+    $writer->xmlDecl('UTF-8');
+    $writer->startTag('insipid');
+
+    export_bookmarks($writer);
+    export_snapshots($writer);
+    export_options($writer);
+
+    $writer->endTag('insipid');
+    $writer->end();
+    exit;
+}
+
+sub login_form {
+    print <<FORM;
+<br />
+<form method="post" action="$site_url/insipid.cgi">
+<span class="formtext">Username:</span><br />
+<input style="width:250px" name="username" /><br />
+<span class="formtext">Password:</span><br />
+<input style="width:250px" name="password" type="password" /><br />
+<input type="submit" value="Login" />
+</form>
+<br />
+FORM
+}
+
+sub show_toolbar {
+    my $rdata = "";
+    if ( defined( url_param('tag') ) ) {
+        $rdata = url_param('tag');
+        $rdata =~ s/ /\+/g;
+    }
+
+    # Toolbar
+    print '<center>';
+    print '<table border="0" width="100%" cellspacing="0" cellpadding="3"><tr>';
+    print '<td valign="top" bgcolor="#CCCCCC">';
+
+    # Title
+    print '<div class="title"><a href="';
+    if ( get_option('use_rewrite') eq 'yes' ) {
+        print $site_url . '/bookmarks';
+    }
+    else {
+        print 'insipid.cgi';
+    }
+    print '">' . $site_title . '</a></div>';
+
+    if ( ( get_option("public_searches") eq "yes" ) || ( logged_in() eq 1 ) ) {
+        print "<div class=\"search\">";
+        print "<form action=\"$site_url/bookmarks\" method=\"post\">";
+        print "<input type=\"text\" name=\"q\"> <input type=\"submit\" value=\"search\">";
+        print "</form>";
+        print "</div>";
+    }
+    else {
+        print "&nbsp;";
+    }
+
+    print "</td><td valign=\"top\" bgcolor=\"#CCCCCC\" align=\"right\">";
+    print "<div class=\"toolbar\">";
+
+    if ( logged_in() eq 1 ) {
+        print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=options\">options</a> | ";
+        print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=tags\">tags</a> | ";
+        print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=import\">import</a> | ";
+        print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=export\">export</a> | ";
+        print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=snapshots\">snapshots</a> | ";
+        print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=logout\">logout</a><br />";
+        print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=add_bookmark\">add</a> | ";
+        print "<a class=\"tools\" href=\"$site_url/insipid.cgi?op=bookmarklets\">bookmarklets</a> | ";
+    }
+
+    my $rf;
+    if ( get_option('use_rewrite') eq 'yes' ) {
+        $rf = $feed_url . '/' . $rdata;
+    }
+    else {
+        $rf = $feed_url . $rdata;
+    }
+
+    print "<a class=\"tools\" href=\"$rf\">RSS feed</a>";
+
+    if ( logged_in() ne 1 ) {
+        print " | <a class=\"tools\" href=\"$site_url/insipid.cgi?op=login\">login</a>";
+    }
+
+    print " | <a class=\"tools\" href=\"javascript:void window.open('$site_url/help.html','width=300,height=500');\">help</a> ";
+    print " | <a class=\"tools\" href=\"http://www.neuro-tech.net/insipid/\">source</a>";
+
+    print "</div></tr></table></center>";
+}
+
+sub delete_bookmark {
+    my ($id) = (@_);
+    my ( $sql, $sth, $md5 ) = ( "", "", "" );
+
+    if ( logged_in() ne 1 ) {
+        push( @errors, "You have to be logged in to perform that operation." );
+        return;
+    }
+
+    # Check for cached version to delete.
+    $sql = "select $tbl_pagecache.md5 from $tbl_pagecache 
+               inner join $tbl_bookmarks on 
+               ($tbl_pagecache.md5 = $tbl_bookmarks.md5) 
+               where ($tbl_bookmarks.id = ?)";
+    $sth = $dbh->prepare($sql);
+    $sth->execute($id);
+    while ( my @r = $sth->fetchrow_array ) {
+        $md5 = $r[0];
+    }
+
+    # Drop the tags for the bookmark
+    $sql = "delete from $tbl_bookmark_tags where (bookmark_id = ?)";
+    $sth = $dbh->prepare($sql);
+    $sth->execute($id);
+
+    # Drop the bookmark.
+    $sql = "delete from $tbl_bookmarks where (id = ?)";
+    $sth = $dbh->prepare($sql);
+    $sth->execute($id);
+
+    # Delete the cached page.
+    if ( $md5 ne "" ) { delete_snapshot($md5); }
+}
+
+sub show_bookmarks {
+    my ( $subquery, $sql, $sth, @parms, @wheres, @hr );
+
+    # this first query will be used to select from a set, like when a user
+    # drills in on a specific tag or to get a smaller view of the entire
+    # dataset (for paging purposes).
+
+    # MySQL and postgres have slightly different syntax here...
+    if ( $dbtype eq 'mysql' ) {
+        $sql = "select $tbl_bookmarks.id from $tbl_bookmarks";
+    }
+    elsif ( $dbtype eq 'Pg' ) {
+        $sql = "select $tbl_bookmarks.id, $tbl_bookmarks.date 
+               from $tbl_bookmarks";
+    }
+
+    # Limit to tags
+    if ( defined( url_param('tag') ) ) {
+
+        # Join the tag tables only when necessary
+
+        if ( url_param('tag') =~ / / ) {
+            my @tags = split( / /, url_param('tag') );
+            my $icount = 1;
+
+            foreach (@tags) {
+                push( @parms, $_ );
+                $sql = "$sql inner join $tbl_bookmark_tags 
+                                               as bt$icount on 
+                                         ($tbl_bookmarks.id = 
+                                               bt$icount.bookmark_id)
+                                       inner join $tbl_tags as t$icount on 
+                                          (t$icount.id = bt$icount.tag_id 
+                                               and t$icount.name = ?) ";
+                $icount++;
+            }
+        }
+        else {
+            $sql = "$sql 
+                               left join $tbl_bookmark_tags on 
+                                 ($tbl_bookmarks.id = 
+                                       $tbl_bookmark_tags.bookmark_id) 
+                               inner join $tbl_tags on 
+                                 ($tbl_tags.id = $tbl_bookmark_tags.tag_id) 
+                                 where ($tbl_tags.name = ?)";
+            push( @parms, url_param('tag') );
+        }
+
+    }
+
+    # Search
+    if ( $query ne "" ) {
+        if (   ( get_option("public_searches") eq "yes" )
+            || ( logged_in() eq 1 ) )
+        {
+            my $sparm = $query;
+            if ( length($sparm) > 2 ) {
+                $sql = "$sql where ($tbl_bookmarks.title like ?)";
+                $sparm =~ s/\%//;
+                $sparm = "\%$sparm\%";
+                push( @parms, $sparm );
+            }
+        }
+    }
+
+    # order
+    $sql = "$sql order by $tbl_bookmarks.date desc";
+
+    # paging functionality
+    $sql = "$sql limit 50";
+
+    if ( defined( url_param('page') ) ) {
+        my $offset = ( ( url_param('page') - 1 ) * 50 );
+        $sql = "$sql offset $offset";
+    }
+
+    $sth = $dbh->prepare($sql);
+    $sth->execute(@parms);
+
+    $subquery = "";
+    if ( $sth->rows > 0 ) {
+        if ( $sth->rows ne 50 ) { $last_page = 1; }
+
+        $subquery = " $tbl_bookmarks.id in (";
+
+        while ( @hr = $sth->fetchrow_array ) {
+            $subquery = $subquery . "$hr[0],";
+        }
+        chop($subquery);    # Strip off the last delimiter
+
+        $subquery = $subquery . ")";
+    }
+    else {
+        print "<p>No bookmarks found.</p>";
+        return;
+    }
+
+    @parms  = ();
+    @wheres = ();
+
+    $sql = "select 
+                 $tbl_bookmarks.id, 
+                 $tbl_bookmarks.title, 
+                 $tbl_bookmarks.description, 
+                 $tbl_bookmarks.access_level,
+                 $tbl_bookmarks.url,
+                 $tbl_tags.name,
+                 $tbl_bookmarks.date,
+                 $tbl_pagecache.date as cache_date,
+                 $tbl_bookmarks.md5
+               from $tbl_bookmarks 
+               left join $tbl_bookmark_tags on 
+                 ($tbl_bookmarks.id = $tbl_bookmark_tags.bookmark_id)
+               left join $tbl_tags on 
+                 ($tbl_tags.id = $tbl_bookmark_tags.tag_id)
+               left join $tbl_pagecache on
+                 ($tbl_bookmarks.md5 = $tbl_pagecache.md5)";
+
+    # Don't show private marks for non-logged in users
+    if ( logged_in() eq 0 ) {
+        push( @wheres, "$tbl_bookmarks.access_level" );
+        push( @parms,  "1" );
+    }
+
+    my $max = @wheres;
+    if ( $max ne 0 ) {
+        $sql = "$sql where (";
+        my $count = 1;
+
+        foreach (@wheres) {
+            $sql = "$sql $_ = ?";
+            if ( $count < $max ) {
+                $sql = "$sql and ";
+            }
+            $count++;
+        }
+
+        $sql = "$sql )";
+        if ( $subquery ne "" ) { $sql = "$sql and $subquery"; }
+    }
+    else {
+        if ( $subquery ne "" ) { $sql = "$sql where $subquery "; }
+    }
+
+    # append sort order.
+    $sql = "$sql order by $tbl_bookmarks.date desc";
+
+    $sth = $dbh->prepare($sql);
+    $sth->execute(@parms);
+
+    my %last;
+    $last{id} = -1;
+
+    print '<ul><br />';
+
+    my $title = '';
+    if ( defined( url_param('tag') ) ) {
+        my $temp = url_param('tag');
+        if ( $temp =~ / / ) {
+            my $count = 0;
+            foreach ( split( / /, $temp ) ) {
+                if ( $count++ ne 0 ) { $title = "$title +"; }
+                $title =
+                  "$title <a class=\"bodyTitle\" href=\"$tag_url$_\">$_</a>";
+            }
+        }
+        else {
+            $title = "<a class=\"bodyTitle\" href=\"$tag_url$temp\">$temp</a>";
+        }
+    }
+    else {
+        $title = 'Most Recent Bookmarks';
+    }
+
+    if ( $query ne '' ) {
+        $title = sprintf( "Search results for \"%s\"", $query );
+    }
+
+    print "<span class=\"bodyTitle\">$title</span>";
+    show_footer();
+    print '<br /><br />';
+
+    print "<table class=\"bookmarklist\">";
+    print '<tr><td>';
+    print "<ul type=\"circle\">\n";
+    while ( @hr = $sth->fetchrow_array ) {
+        if ( $last{id} eq -1 ) {
+            $last{id}           = $hr[0];
+            $last{title}        = $hr[1];
+            $last{description}  = $hr[2];
+            $last{access_level} = $hr[3];
+            $last{url}          = $hr[4];
+            $last{tags}         = "";
+            $last{timestamp}    = $hr[6];
+            $last{cachetime}    = $hr[7];
+            $last{md5}          = $hr[8];
+        }
+
+        if ( $hr[0] ne $last{id} ) {
+
+            # the id changed, so show the last mark.
+            show_bookmark(
+                $last{id},           $last{title},     $last{description},
+                $last{access_level}, $last{url},       $last{tags},
+                $last{timestamp},    $last{cachetime}, $last{md5}
+            );
+
+            # Swap the new one in.
+            $last{id}           = $hr[0];
+            $last{title}        = $hr[1];
+            $last{description}  = $hr[2];
+            $last{access_level} = $hr[3];
+            $last{url}          = $hr[4];
+            $last{tags}         = $hr[5];
+            $last{timestamp}    = $hr[6];
+            $last{cachetime}    = $hr[7];
+            $last{md5}          = $hr[8];
+        }
+        else {
+
+            # Add tag to the current bookmark
+            if ( defined( $hr[5] ) ) {
+                $last{tags} = "$last{tags} $hr[5]";
+            }
+        }
+    }
+
+    if ( $last{id} ne -1 ) {
+        show_bookmark(
+            $last{id},           $last{title},     $last{description},
+            $last{access_level}, $last{url},       $last{tags},
+            $last{timestamp},    $last{cachetime}, $last{md5}
+        );
+    }
+
+    print "</ul></td></tr></table>";
+}
+
+sub show_bookmark {
+    my (
+        $id,           $title,     $description,
+        $access_level, $url,       $tags,
+        $timestamp,    $cachetime, $md5
+    ) = (@_);
+
+    print "<div class=\"bookmarklistitem\">";
+    print "<li>";
+    if ( $access_level eq 0 ) {
+        print "<a href=\"$site_url/insipid.cgi?go=$id\">";
+        print "<i>";
+        print "$title";
+        print "</i>";
+    }
+    else {
+        print "<a href=\"$url\">";
+        print $title;
+    }
+
+    if ( logged_in() eq 1 ) {
+        if ( defined($cachetime) ) {
+            print "</a> - <a href=\"$snapshot_url$md5\">view snapshot";
+        }
+    }
+
+    print "</a><br /><div class=\"bookmarkOperations\">";
+
+    my $timestr = "";
+    if ( logged_in() eq 1 ) {
+        $timestr = time2str( "%Y-%m-%d %T EST", $timestamp, "EST" );
+    }
+    else {
+        $timestr = time2str( "%Y-%m-%d", $timestamp, "EST" );
+    }
+
+    print "posted on $timestr ";
+
+    if ( defined($tags) ) {
+        print "to ";
+        my $cur;
+
+        foreach $cur ( split( /\ /, $tags ) ) {
+            print '<a class="bookmarkTag" href="';
+            print $tag_url . $cur . '">' . $cur . '</a> ';
+        }
+    }
+
+    if ( logged_in() eq 1 ) {
+        my $ex = "";
+
+        if ( url_param('tag') )  { $ex = "$ex&tag=" . url_param('tag'); }
+        if ( url_param('page') ) { $ex = "$ex&page=" . url_param('page'); }
+        if ( $query ne "" ) { $ex = "$ex&q=" . $query; }
+
+        print "<span class=\"bodytext\">&nbsp;&mdash;&nbsp;";
+        print
+"(<a class=\"bookmarkOp\" href=\"$site_url/insipid.cgi?op=delete_bookmark&id=$id$ex\">delete</a>,&nbsp;";
+        print
+"<a class=\"bookmarkOp\" href=\"$site_url/insipid.cgi?op=edit_bookmark&id=$id$ex\">edit</a>";
+        if ( !defined($cachetime) ) {
+            print
+",&nbsp;<a class=\"bookmarkOp\" href=\"$site_url/insipid.cgi?op=snapshot&id=$id$ex\">snapshot</a>";
+        }
+        print
+")<div class=\"bookmarkDescription\">$description</div></span></div></li>\n";
+    }
+
+    print "</div>\n";
+}
+
+# Gets the ID for a bookmark if it already exists in the DB. Otherwise, -1.
+sub get_bookmark_id {
+    my ($url) = (@_);
+
+    # Lookup the URL id first.
+    my $sql = "select $tbl_bookmarks.id from 
+               $tbl_bookmarks where ($tbl_bookmarks.md5 = ?)";
+    my $sth = $dbh->prepare($sql);
+
+    $sth->execute( md5_hex($url) );
+
+    if ( $sth->rows ne 0 ) {
+        my @r = $sth->fetchrow_array;
+        return $r[0];
+    }
+
+    return -1;
+}
+
+sub get_bookmark {
+    my ($id) = (@_);
+
+    my $sql = "select 
+                       $tbl_bookmarks.title, 
+                       $tbl_bookmarks.description, 
+                       $tbl_bookmarks.url,
+                       $tbl_bookmarks.access_level 
+                       from $tbl_bookmarks 
+                       where ($tbl_bookmarks.id = ?)";
+    my $sth = $dbh->prepare($sql);
+    $sth->execute($id);
+    my @r = $sth->fetchrow_array;
+    return ( $r[2], $r[0], $r[1], $r[3] );
+}
+
+sub update_bookmark {
+    my ( $id, $url, $title, $description, $access_level, $tags ) = (@_);
+
+    if ( logged_in() ne 1 ) {
+        push( @errors, "You have to be logged in to perform that operation." );
+        return;
+    }
+
+    my $sql = "update $tbl_bookmarks 
+                       set url = ?, md5 = ?, title = ?, description = ?, 
+                       access_level = ? where (id = ?)";
+    my $sth = $dbh->prepare($sql);
+    $sth->execute( $url, md5_hex("$url"), $title, $description, $access_level,
+        $id );
+
+    set_tags( $id, $tags );
+}
+
+1;
+__END__