[PackageKit-commit] packagekit: Branch 'master' - 26 commits

Richard Hughes hughsient at kemper.freedesktop.org
Fri Aug 8 01:44:36 PDT 2008


 Makefile.am                                |    2 
 backends/apt/aptDBUSBackend.py             |    9 
 backends/yum/helpers/Makefile.am           |    1 
 client/pk-generate-pack.c                  |   11 
 configure.ac                               |   29 +
 contrib/Makefile.am                        |    4 
 contrib/packagekit-plugin/.gitignore       |   25 +
 contrib/packagekit-plugin/Makefile.am      |   28 +
 contrib/packagekit-plugin/README           |  152 ++++++
 contrib/packagekit-plugin/sdk/np_entry.cpp |  320 ++++++++++++
 contrib/packagekit-plugin/sdk/npn_gate.cpp |  215 ++++++++
 contrib/packagekit-plugin/sdk/npp_gate.cpp |  358 ++++++++++++++
 contrib/packagekit-plugin/sdk/npplat.h     |  150 ++++++
 contrib/packagekit-plugin/sdk/pluginbase.h |   96 +++
 contrib/packagekit-plugin/src/contents.cpp |  724 +++++++++++++++++++++++++++++
 contrib/packagekit-plugin/src/contents.h   |  136 +++++
 contrib/packagekit-plugin/src/plugin.cpp   |  316 ++++++++++++
 contrib/packagekit-plugin/src/plugin.h     |   81 +++
 contrib/packagekit-plugin/tests/test.html  |   34 +
 docs/html/img/author-glatzor.png           |binary
 po/POTFILES.in                             |    2 
 python/packagekit/daemonBackend.py         |   24 
 22 files changed, 2697 insertions(+), 20 deletions(-)

New commits:
commit 44d4e85a1f501265ee024ccad95901303a380232
Author: Shishir Goel <crazyontheedge at gmail.com>
Date:   Fri Aug 8 09:39:30 2008 +0100

    fix a couple of memory leaks in pkgenpack

diff --git a/client/pk-generate-pack.c b/client/pk-generate-pack.c
index d92ab40..60bc9de 100644
--- a/client/pk-generate-pack.c
+++ b/client/pk-generate-pack.c
@@ -198,7 +198,7 @@ pk_generate_pack_create (const gchar *tarfilename, GPtrArray *file_array, GError
 	TAR *t;
 	FILE *file;
 	guint i;
-	const gchar *src;
+	gchar *src;
 	gchar *dest;
 
 	file = g_fopen (tarfilename, "a+");
@@ -211,7 +211,7 @@ pk_generate_pack_create (const gchar *tarfilename, GPtrArray *file_array, GError
 
 	/* add each of the files */
 	for (i=0; i<file_array->len; i++) {
-		src = (const gchar *) g_ptr_array_index (file_array, i);
+		src = (gchar *) g_ptr_array_index (file_array, i);
 		dest =  g_path_get_basename (src);
 
 		/* add file to archive */
@@ -224,6 +224,7 @@ pk_generate_pack_create (const gchar *tarfilename, GPtrArray *file_array, GError
 
 		/* delete file */
 		g_remove (src);
+		g_free (src);
 
 		/* free the stripped filename */
 		g_free (dest);
@@ -410,7 +411,7 @@ main (int argc, char *argv[])
 	PkControl *control = NULL;
 	PkRoleEnum roles;
 	const gchar *package_list = NULL;
-	const gchar *tempdir = NULL;
+	gchar *tempdir = NULL;
 	gboolean exists;
 	gboolean overwrite;
 
@@ -474,7 +475,7 @@ main (int argc, char *argv[])
 	}
 
 	/* download packages to a temporary directory */
-	tempdir = g_strconcat (g_get_tmp_dir (), "/pack", NULL);
+	tempdir = g_build_filename (g_get_tmp_dir (), "pack", NULL);
 
 	/* check if file exists before we overwrite it */
 	exists = g_file_test (pack_filename, G_FILE_TEST_EXISTS);
@@ -509,6 +510,8 @@ main (int argc, char *argv[])
 out:
 	/* get rid of temp directory */
 	g_rmdir (tempdir);
+	
+	g_free (tempdir);
 	g_free (packname);
 	g_free (with_package_list);
 	g_free (options_help);
commit fb19c33dfb39fa93d83f048a3c1774d56f6466d8
Author: Richard Hughes <richard at hughsie.com>
Date:   Fri Aug 8 09:23:50 2008 +0100

    plugin: remove the autogen package, i don't want to support compiling the plugin out of tree

diff --git a/contrib/packagekit-plugin/autogen.sh b/contrib/packagekit-plugin/autogen.sh
deleted file mode 100755
index 45eeb39..0000000
--- a/contrib/packagekit-plugin/autogen.sh
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/bin/sh
-
-srcdir=`dirname $0`
-test -z "$srcdir" && srcdir=.
-
-ORIGDIR=`pwd`
-cd $srcdir
-PACKAGE="packagekit-plugin"
-
-have_libtool=false
-have_autoconf=false
-have_automake=false
-need_configure_in=false
-
-if libtool --version < /dev/null > /dev/null 2>&1 ; then
-	libtool_version=`libtoolize --version | sed 's/^[^0-9]*\([0-9.][0-9.]*\).*/\1/'`
-	have_libtool=true
-	case $libtool_version in
-	    1.3*)
-		need_configure_in=true
-		;;
-	esac
-fi
-
-if autoconf --version < /dev/null > /dev/null 2>&1 ; then
-	autoconf_version=`autoconf --version | sed 's/^[^0-9]*\([0-9.][0-9.]*\).*/\1/'`
-	have_autoconf=true
-	case $autoconf_version in
-	    2.13)
-		need_configure_in=true
-		;;
-	esac
-fi
-
-if $have_libtool ; then : ; else
-	echo;
-	echo "You must have libtool >= 1.3 installed to compile $PACKAGE";
-	echo;
-	exit;
-fi
-
-(automake --version) < /dev/null > /dev/null 2>&1 || {
-	echo;
-	echo "You must have automake installed to compile $PACKAGE";
-	echo;
-	exit;
-}
-
-(intltoolize --version) < /dev/null > /dev/null 2>&1 || {
-	echo;
-	echo "You must have intltool installed to compile $PACKAGE";
-	echo;
-	exit;
-}
-
-echo "Generating configuration files for $PACKAGE, please wait...."
-echo;
-
-if $need_configure_in ; then
-    if test ! -f configure.in ; then
-	echo "Creating symlink from configure.in to configure.ac..."
-	echo
-	ln -s configure.ac configure.in
-    fi
-fi
-
-aclocal $ACLOCAL_FLAGS
-libtoolize --force
-autoheader
-automake --add-missing -Woverride
-autoconf
-intltoolize
-
-cd $ORIGDIR || exit $?
-
-$srcdir/configure $@ --enable-maintainer-mode --enable-compile-warnings
-
commit badda1f4c61ddddb339ac7f7b26471ae8f4a43d6
Author: Richard Hughes <richard at hughsie.com>
Date:   Fri Aug 8 09:06:49 2008 +0100

    plugin: lots of changes to make the plugin work with the 0.3.0 API

diff --git a/contrib/packagekit-plugin/README b/contrib/packagekit-plugin/README
index 812a479..3a5b901 100644
--- a/contrib/packagekit-plugin/README
+++ b/contrib/packagekit-plugin/README
@@ -12,7 +12,7 @@ will show:
  | Version: 20061119-14.fc9           |
  +------------------------------------+
 
-Click on the plugin, and it will fire off gpk-install-package to install the package;
+Click on the plugin, and it asks the session service to install the package;
 the display changes to:
 
  +------------------------------------+
@@ -35,17 +35,17 @@ Security Considerations
 The design as a plugin is specifically meant to avoid revealing information about installed
 applications to the web page. None of the information in the plugin is revealed in the DOM
 tree. It might be possible to guess at results by subtle timing attacks, but it would at
-best be a probalistic thing.
+best be a probabilistic thing.
 
-Wweb pages probably can trick the user into clicking on the plugin
-and installing or running apps they dont' want to install or run:
+Web pages probably can trick the user into clicking on the plugin
+and installing or running applications they dont' want to install or run:
 
  - The application title is supplied by the web page
  - The web page could put elements over the plugin to make it appear
 
 (The second is why I consider the first to be acceptable.) However, importantly, the
 only applications that could be installed in that way are applications from the package
-repository already configured for the system. The only applications that can be run in 
+repository already configured for the system. The only applications that can be run in
 this way are applications already on the system. And there is no ability to pass command
 line arguments or files to the applications. So, the security risk should be minimal.
 
@@ -54,21 +54,21 @@ Accessibility
 
 The design as a plugin (for the above security considerations) causes some problems for
 accessibility. In particular, there is no access into the plugin for screenreaders
-or other assistive technologies. Making this work would require the existence of a 
+or other assistive technologies. Making this work would require the existence of a
 system for integrating accessibility support between the browser and plugins, which,
 to my knowledge does not exist at the moment.
 
-Keynav is also missing at the moment, but could be added quite easily. (Basically,
-the Return keypress just needs to be treated like clicking on the plugin.)
+Keyboard navigation is also missing at the moment, but could be added quite easily.
+(Basically, the Return keypress just needs to be treated like clicking on the plugin.)
 
 The fonts, font sizes, and colors used in the plugin come from the system theme, so they
-should not pose a problem for users with special needs. (But see discussion of 
+should not pose a problem for users with special needs. (But see discussion of
 sizing below.)
 
 Cross Browser Support
 =====================
 
-The plugins acts as a standard windowless plugin, so it shouldn't be restricted
+The plugin acts as a standard windowless plugin, so it shouldn't be restricted
 to working in Firefox or in Gecko-based browsers.
 
 The plugin does assume that the toolkit hosting the browser is GTK+ for a couple
@@ -82,7 +82,7 @@ These could be fixed if necessary. (Use XSETTINGS directly for fonts and colors,
 just skip the timestamp and let the newly launched app get placed in the
 background if not running GTK+ and no timestamp is provided.)
 
-More deeply, the plugin requires that the GLib main loop be running, so that 
+More deeply, the plugin requires that the GLib main loop be running, so that
 libpackagekit can receive asynchronous notifications from the PackageKit daemon.
 So, getting it to work in Konqueror is going to be hard unless Qt has been
 compiled to use the GLib main loop.
@@ -98,7 +98,7 @@ packagekit-plugin builds with the standard:
 
 Instead of doing 'make install', you can symlink .libs/packagekit-plugin into
 ~/.mozilla/plugins. Then you can restart your browser and load the HTML page
-under tests/. 
+under tests/.
 
 If you do this, you probably want to delete the symlink when you are
 done so that it doesn't interfere with a system install of the plugin.
@@ -106,7 +106,7 @@ done so that it doesn't interfere with a system install of the plugin.
 Using
 =====
 
-dd the following code to your web page:
+Add the following code to your web page:
 
     <object type="application/x-packagekit-plugin" width="300" height="150">
       <!-- Name that will be used in the user interface -->
@@ -119,7 +119,7 @@ dd the following code to your web page:
 
 Unfortunately browser plugins have no mechanism for size negotiation, so you
 have to specify a predetermined size in some fashion. You can do it with the pixel
-sizes as above, or you can do it with CSS using 'em' sizes or percentage 
+sizes as above, or you can do it with CSS using 'em' sizes or percentage
 lengths. For example, you could have in your CSS:
 
       .packagekit-plugin {
@@ -149,3 +149,4 @@ src/ is new code written by Red Hat, Inc.
 
 Owen Taylor <otaylor at redhat.com>
 July 24, 2008
+
diff --git a/contrib/packagekit-plugin/src/contents.cpp b/contrib/packagekit-plugin/src/contents.cpp
index 6490774..b12ba75 100644
--- a/contrib/packagekit-plugin/src/contents.cpp
+++ b/contrib/packagekit-plugin/src/contents.cpp
@@ -123,7 +123,7 @@ void PkpContents::recheck()
     mStatus = IN_PROGRESS;
     mAvailableVersion = "";
     mAvailablePackageName = "";
-    
+
     for (std::vector<std::string>::iterator i = mPackageNames.begin(); i != mPackageNames.end(); i++) {
         GError *error = NULL;
         PkClient *client = pk_client_new();
@@ -212,7 +212,7 @@ static void
 append_markup(GString *str, const char *format, ...)
 {
     va_list vap;
-    
+
     va_start(vap, format);
     char *tmp = g_markup_vprintf_escaped(format, vap);
     va_end(vap);
@@ -239,7 +239,7 @@ set_source_from_rgba(cairo_t *cr,
                           ((rgba & 0x00ff0000) >> 16) / 255.,
                           ((rgba & 0x0000ff00) >> 8) / 255.,
                           (rgba & 0x000000ff) / 255.);
-                          
+
 }
 
 /* Retrieve the system colors and fonts.
@@ -275,7 +275,7 @@ get_style(PangoFontDescription **font_desc,
     *link = rgba_from_gdk_color(&link_color);
 
     *font_desc = pango_font_description_copy(window->style->font_desc);
-   
+
     gtk_widget_destroy(window);
 }
 
@@ -285,10 +285,10 @@ PkpContents::ensureLayout(cairo_t              *cr,
                           guint32               link_color)
 {
     GString *markup = g_string_new(NULL);
-    
+
     if (mLayout)
         return;
-    
+
     mLayout = pango_cairo_create_layout(cr);
     pango_layout_set_font_description(mLayout, font_desc);
 
@@ -321,7 +321,7 @@ PkpContents::ensureLayout(cairo_t              *cr,
                               _("\n<span color='#%06x' underline='single'>Run now</span>"),
                               link_color >> 8);
         }
-        
+
         append_markup(markup, _("\n<span color='#%06x' underline='single'>Upgrade to version %s</span>"),
                       link_color >> 8,
                       mAvailableVersion.c_str());
@@ -352,7 +352,7 @@ PkpContents::refresh()
     if (mPlugin != 0)
         mPlugin->refresh();
 }
-                               
+
 void
 PkpContents::setPlugin(PkpPluginInstance *plugin)
 {
@@ -364,7 +364,7 @@ PkpContents::draw(cairo_t *cr)
 {
     guint32 foreground, background, link;
     PangoFontDescription *font_desc;
-    
+
     get_style(&font_desc, &foreground, &background, &link);
 
     set_source_from_rgba(cr, background);
@@ -385,7 +385,7 @@ PkpContents::draw(cairo_t *cr)
     pango_cairo_show_layout(cr, mLayout);
 }
 
-/* Cut and paste from pango-layout.c; determines if a layout iter is on 
+/* Cut and paste from pango-layout.c; determines if a layout iter is on
  * a line terminated by a real line break (rather than a line break from
  * wrapping). We use this to determine whether the empty run at the end
  * of a display line should be counted as a break between links or not.
@@ -429,7 +429,7 @@ int
 PkpContents::getLinkIndex(int x, int y)
 {
     /* Coordinates are relative to origin of plugin (different from drawing) */
-    
+
     if (!mLayout)
         return -1;
 
@@ -447,13 +447,13 @@ PkpContents::getLinkIndex(int x, int y)
     int seen_links = 0;
     bool in_link = false;
     int result = -1;
-    
+
     while (TRUE) {
         PangoLayoutRun *run = pango_layout_iter_get_run(iter);
         if (run) {
             PangoItem *item = run->item;
             PangoUnderline uline = PANGO_UNDERLINE_NONE;
-            
+
             for (GSList *l = item->analysis.extra_attrs; l; l = l->next) {
                 PangoAttribute *attr = (PangoAttribute *)l->data;
                 if (attr->klass->type == PANGO_ATTR_UNDERLINE) {
@@ -481,11 +481,11 @@ PkpContents::getLinkIndex(int x, int y)
             if (line_is_terminated(iter))
                 in_link = FALSE;
         }
-        
+
         if (!pango_layout_iter_next_run (iter))
             break;
     }
-    
+
     pango_layout_iter_free(iter);
 
     return result;
@@ -502,7 +502,7 @@ PkpContents::buttonRelease(int x, int y, Time time)
     int index = getLinkIndex(x, y);
     if (index < 0)
         return;
-    
+
     switch (mStatus) {
     case IN_PROGRESS:
     case INSTALLING:
@@ -554,10 +554,10 @@ static gboolean
 validate_name(const char *name)
 {
     const char *p;
-    
+
     for (p = name; *p; p++) {
         char c = *p;
-        
+
         if (!((c >= 'A' && c <= 'Z') ||
               (c >= 'a' && c <= 'z') ||
               (c >= '0' && c <= '9') ||
@@ -583,7 +583,7 @@ PkpContents::findAppInfo()
         char *id = g_strconcat(i->c_str(), ".desktop", NULL);
         GDesktopAppInfo *desktopAppInfo = g_desktop_app_info_new(id);
         g_free(id);
-        
+
         if (desktopAppInfo) {
             mAppInfo = G_APP_INFO(desktopAppInfo);
             break;
@@ -593,12 +593,12 @@ PkpContents::findAppInfo()
     if (mAppInfo != 0)
         setStatus(INSTALLED);
 }
-                  
+
 void
 PkpContents::runApplication (Time time)
 {
     GError *error = NULL;
-    
+
     if (mAppInfo == 0) {
         g_warning("Didn't find application to launch");
         return;
@@ -611,7 +611,7 @@ PkpContents::runApplication (Time time)
 #ifdef HAVE_GDK_APP_LAUNCH_CONTEXT_NEW
     context = gdk_app_launch_context_new();
     gdk_app_launch_context_set_timestamp(time);
-#endif    
+#endif
 
     if (!g_app_info_launch(mAppInfo, NULL, context, &error)) {
         g_warning("%s\n", error->message);
@@ -652,35 +652,34 @@ PkpContents::installPackage (Time time)
                                                                G_TYPE_STRING, mAvailablePackageName.c_str(),
                                                                G_TYPE_INVALID,
                                                                G_TYPE_INVALID);
-    
+
      setStatus(INSTALLING);
 }
 
 void
-PkpContents::onClientPackage(PkClient	  *client,
-                                  PkInfoEnum	   info,
-                                  const gchar	   *package_id,
-                                  const gchar	   *summary,
-                                  PkpContents *contents)
-{
-    PkPackageId *id = pk_package_id_new_from_string(package_id);
-    
-    if (info == PK_INFO_ENUM_AVAILABLE) {
+PkpContents::onClientPackage(PkClient	        *client,
+                             const PkPackageObj *obj,
+                             PkpContents        *contents)
+{
+    /* if we didn't use displayname, use the summary */
+    if (contents->mDisplayName.size() == 0)
+        contents->mDisplayName = obj->summary;
+
+    /* parse the data */
+    if (obj->info == PK_INFO_ENUM_AVAILABLE) {
         if (contents->getStatus() == IN_PROGRESS)
             contents->setStatus(AVAILABLE);
         else if (contents->getStatus() == INSTALLED)
             contents->setStatus(UPGRADABLE);
-        contents->setAvailableVersion(id->version);
-        contents->setAvailablePackageName(id->name);
-    } else if (info == PK_INFO_ENUM_INSTALLED) {
+        contents->setAvailableVersion(obj->id->version);
+        contents->setAvailablePackageName(obj->id->name);
+    } else if (obj->info == PK_INFO_ENUM_INSTALLED) {
         if (contents->getStatus() == IN_PROGRESS)
             contents->setStatus(INSTALLED);
         else if (contents->getStatus() == AVAILABLE)
             contents->setStatus(UPGRADABLE);
-        contents->setInstalledVersion(id->version);
+        contents->setInstalledVersion(obj->id->version);
     }
-    
-    pk_package_id_free(id);
 }
 
 void
@@ -698,29 +697,28 @@ PkpContents::onClientFinished(PkClient	    *client,
                               PkExitEnum     exit,
                               guint	     runtime,
                               PkpContents   *contents)
-    
+
 {
     contents->removeClient(client);
 }
-    
+
 void
 PkpContents::onInstallPackageFinished (DBusGProxy       *proxy,
                                        DBusGProxyCall   *call,
                                        void             *user_data)
 {
     PkpContents *contents = (PkpContents *)user_data;
-    
+
     GError *error = NULL;
-    if (!dbus_g_proxy_end_call(proxy, call, &error, 
+    if (!dbus_g_proxy_end_call(proxy, call, &error,
                                G_TYPE_INVALID)) {
         g_warning("Error occurred during install: %s", error->message);
         g_clear_error(&error);
-        
     }
 
     g_object_unref(contents->mInstallPackageProxy);
     contents->mInstallPackageProxy = 0;
     contents->mInstallPackageCall = 0;
-    
+
     contents->recheck();
 }
diff --git a/contrib/packagekit-plugin/src/contents.h b/contrib/packagekit-plugin/src/contents.h
index 797187d..0d465a6 100644
--- a/contrib/packagekit-plugin/src/contents.h
+++ b/contrib/packagekit-plugin/src/contents.h
@@ -68,7 +68,7 @@ public:
     virtual ~PkpContents();
 
     void setPlugin(PkpPluginInstance *plugin);
-    
+
     void draw(cairo_t *cr);
     void buttonPress(int x, int y, Time time);
     void buttonRelease(int x, int y, Time time);
@@ -83,7 +83,7 @@ private:
     void installPackage(Time time);
 
     int getLinkIndex(int x, int y);
-    
+
     void setStatus(PackageStatus status);
     PackageStatus getStatus() { return mStatus; }
     void setAvailableVersion(const char *version);
@@ -95,13 +95,11 @@ private:
                       guint32 link_color);
     void clearLayout();
     void refresh();
-    
+
     void removeClient(PkClient *client);
-    
-    static void onClientPackage(PkClient 	   *client,
-                                PkInfoEnum	    info,
-                                const gchar	   *package_id,
-                                const gchar	   *summary,
+
+    static void onClientPackage(PkClient           *client,
+                                const PkPackageObj *obj,
                                 PkpContents        *contents);
     static void onClientErrorCode(PkClient	   *client,
                                   PkErrorCodeEnum  code,
@@ -111,7 +109,7 @@ private:
                                  PkExitEnum	   exit,
                                  guint		   runtime,
                                  PkpContents      *contents);
-    
+
     static void onInstallPackageFinished(DBusGProxy     *proxy,
                                          DBusGProxyCall *call,
                                          void           *user_data);
@@ -126,7 +124,7 @@ private:
     std::string mDisplayName;
     std::vector<std::string> mPackageNames;
     std::vector<std::string> mDesktopNames;
-    
+
     PangoLayout *mLayout;
 
     std::vector<PkClient *> mClients;
diff --git a/contrib/packagekit-plugin/src/plugin.cpp b/contrib/packagekit-plugin/src/plugin.cpp
index 0ab42b2..b9c3268 100644
--- a/contrib/packagekit-plugin/src/plugin.cpp
+++ b/contrib/packagekit-plugin/src/plugin.cpp
@@ -99,14 +99,14 @@ NPError NS_PluginInitialize()
 {
     if (module_handle != 0) /* Already initialized */
         return NPERR_NO_ERROR;
-    
+
     make_module_resident();
-    
+
 #ifdef ENABLE_NLS
     bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
     bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 #endif
-    
+
     return NPERR_NO_ERROR;
 }
 
@@ -141,7 +141,7 @@ nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStru
     const char *displayName = "";
     const char *packageNames = NULL;
     const char *desktopNames = NULL;
-    
+
     if(!aCreateDataStruct)
         return NULL;
 
@@ -153,12 +153,12 @@ nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStru
         else if (strcmp(aCreateDataStruct->argn[i], "desktopnames") == 0)
             desktopNames = aCreateDataStruct->argv[i];
     }
-      
+
     PkpPluginInstance * plugin = new PkpPluginInstance(aCreateDataStruct->instance, displayName, packageNames, desktopNames);
-  
+
     NPN_SetValue(aCreateDataStruct->instance,
                  NPPVpluginWindowBool, (void *)FALSE);
-  
+
     return plugin;
 }
 
@@ -194,7 +194,7 @@ NPBool PkpPluginInstance::init(NPWindow* aWindow)
 {
     if(aWindow == NULL)
         return FALSE;
-  
+
     if (SetWindow(aWindow))
         mInitialized = TRUE;
 	
@@ -231,14 +231,14 @@ NPError PkpPluginInstance::SetWindow(NPWindow* aWindow)
     mY = aWindow->y;
     mWidth = aWindow->width;
     mHeight = aWindow->height;
-  
+
     mWindow = (Window) aWindow->window;
     NPSetWindowCallbackStruct *ws_info = (NPSetWindowCallbackStruct *)aWindow->ws_info;
     mDisplay = ws_info->display;
     mVisual = ws_info->visual;
     mDepth = ws_info->depth;
     mColormap = ws_info->colormap;
-  
+
     return NPERR_NO_ERROR;
 }
 
@@ -248,15 +248,15 @@ PkpPluginInstance::refresh()
     NPRect rect;
 
     /* Coordinates here are relative to the plugin's origin (mX,mY) */
-    
+
     rect.left = 0;
     rect.right =  mWidth;
     rect.top = 0;
     rect.bottom = mHeight;
-    
+
     NPN_InvalidateRect(mInstance, &rect);
 }
-                               
+
 uint16
 PkpPluginInstance::HandleEvent(void *event)
 {
@@ -277,7 +277,7 @@ PkpPluginInstance::HandleEvent(void *event)
 
             cairo_destroy(cr);
             cairo_surface_destroy(surface);
-            
+
             return 1;
         }
     case ButtonPress:
diff --git a/contrib/packagekit-plugin/src/plugin.h b/contrib/packagekit-plugin/src/plugin.h
index 9d3af87..9364cbc 100644
--- a/contrib/packagekit-plugin/src/plugin.h
+++ b/contrib/packagekit-plugin/src/plugin.h
@@ -62,7 +62,7 @@ public:
     int getY() { return mY; }
     int getWidth() { return mWidth; }
     int getHeight() { return mHeight; }
-    
+
 private:
     NPP mInstance;
     NPBool mInitialized;
diff --git a/contrib/packagekit-plugin/tests/test.html b/contrib/packagekit-plugin/tests/test.html
index 7afb043..a260d44 100644
--- a/contrib/packagekit-plugin/tests/test.html
+++ b/contrib/packagekit-plugin/tests/test.html
@@ -5,7 +5,7 @@
       body {
         background: #ffeedd;
       }
-      
+
       .packagekit-plugin {
          width: 30em;
          height: 5em;
@@ -14,18 +14,21 @@
   </head>
   <body>
     <object type="application/x-packagekit-plugin" width="500" height="200" class="packagekit-plugin">
-<!--      <param name="packagenames" value="firefox mozilla-firefox"/>
+      <param name="packagenames" value="firefox mozilla-firefox"/>
       <param name="desktopnames" value="firefox mozilla-firefox"/>
-      -->
-<!--      <param name="displayname" value="KStars"/>
+    </object>
+    <br/><br/>
+    <object type="application/x-packagekit-plugin" width="500" height="200" class="packagekit-plugin">
+      <param name="displayname" value="KStars"/>
       <param name="packagenames" value="kdeedu"/>
       <param name="desktopnames" value="kstars"/> -->
-<!--      <param name="displayname" value="Mugshot"/>
-      <param name="packagenames" value="mugshot"/>
-      <param name="desktopnames" value="mugshot"/>  -->
+    </object>
+    <br/><br/>
+    <object type="application/x-packagekit-plugin" width="500" height="200" class="packagekit-plugin">
       <param name="displayname" value="GNU Backgammon"/>
       <param name="packagenames" value="gnubg"/>
       <param name="desktopnames" value="fedora-gnubg"/>
     </object>
   </body>
-</hml>
\ No newline at end of file
+</html>
+
commit 1f2e095f93b7ae07b6a6e41917b5a9a096189b87
Author: Richard Hughes <richard at hughsie.com>
Date:   Fri Aug 8 08:10:36 2008 +0100

    plugin: resolve not takes an array of package_ids, so fix the compile

diff --git a/contrib/packagekit-plugin/src/contents.cpp b/contrib/packagekit-plugin/src/contents.cpp
index e3b78ab..6490774 100644
--- a/contrib/packagekit-plugin/src/contents.cpp
+++ b/contrib/packagekit-plugin/src/contents.cpp
@@ -51,6 +51,7 @@
 #include <gio/gdesktopappinfo.h>
 
 #include "pk-package-id.h"
+#include "pk-package-ids.h"
 
 #include "plugin.h"
 
@@ -126,7 +127,9 @@ void PkpContents::recheck()
     for (std::vector<std::string>::iterator i = mPackageNames.begin(); i != mPackageNames.end(); i++) {
         GError *error = NULL;
         PkClient *client = pk_client_new();
-        if (!pk_client_resolve(client, PK_FILTER_ENUM_NONE, i->c_str(), &error)) {
+        gchar **package_ids;
+        package_ids = pk_package_ids_from_id (i->c_str());
+        if (!pk_client_resolve(client, PK_FILTER_ENUM_NONE, package_ids, &error)) {
             g_warning("%s", error->message);
             g_clear_error(&error);
             g_object_unref(client);
@@ -136,6 +139,7 @@ void PkpContents::recheck()
             g_signal_connect(client, "finished", G_CALLBACK(onClientFinished), this);
             mClients.push_back(client);
         }
+        g_strfreev (package_ids);
     }
 
     findAppInfo();
commit d546746a768f165d262c2721665150c3b1a54316
Author: Richard Hughes <richard at hughsie.com>
Date:   Fri Aug 8 07:59:40 2008 +0100

    yum: ship install-signature.py -- I can't work out how this was missed

diff --git a/backends/yum/helpers/Makefile.am b/backends/yum/helpers/Makefile.am
index 4172e30..e17664d 100644
--- a/backends/yum/helpers/Makefile.am
+++ b/backends/yum/helpers/Makefile.am
@@ -23,6 +23,7 @@ dist_helper_DATA = 			\
 	resolve.py			\
 	update-packages.py		\
 	install-files.py		\
+	install-signature.py		\
 	refresh-cache.py		\
 	what-provides.py		\
 	update-system.py		\
commit a40c4541d5745bac9608e4a43a1a0c1f291aa8f8
Merge: 0312993... c47ae97...
Author: Sebastian Heinlein <devel at glatzor.de>
Date:   Thu Aug 7 22:49:32 2008 +0200

    Merge branch 'master' of git+ssh://glatzor@git.packagekit.org/srv/git/PackageKit
    
    Conflicts:
    
    	python/packagekit/daemonBackend.py

diff --cc python/packagekit/daemonBackend.py
index 0847279,a0514f8..a148312
--- a/python/packagekit/daemonBackend.py
+++ b/python/packagekit/daemonBackend.py
@@@ -430,21 -430,21 +430,21 @@@ class PackageKitBaseBackend(dbus.servic
          self.Finished(EXIT_FAILED)
  
      @dbus.service.method(PACKAGEKIT_DBUS_INTERFACE,
 -			 in_signature='ss',out_signature='')
 +                         in_signature='ss',out_signature='')
      def DownloadPackages(self,package_ids,directory):
 -	'''
 -	Implement the (backend)-download-packages functionality
 -	'''
 -	pklog.info("DownloadPackages(%s%s)" % (packages,directory))
 -	self.doDownloadPackages(packages,directory)
 +        '''
 +        Implement the (backend)-download-packages functionality
 +        '''
-         pklog.info("DownloadPackages(%s%s)" % (packages,directory))
-         self.doDownloadPackages(packages,directory)
++        pklog.info("DownloadPackages(%s,%s)" % (package_ids, directory))
++        self.doDownloadPackages(package_ids, directory)
  
      def doDownloadPackages(self,package_ids,directory):
 -	'''
 -	Should be replaced in the corresponding backend sub class
 -	'''
 -	self.ErrorCode(ERROR_NOT_SUPPORTED,
 -			"This function is not implemented in this backend")
 -	self.Finished(EXIT_FAILED)
 +        '''
 +        Should be replaced in the corresponding backend sub class
 +        '''
 +        self.ErrorCode(ERROR_NOT_SUPPORTED,
 +                       "This function is not implemented in this backend")
 +        self.Finished(EXIT_FAILED)
  
      @dbus.service.method(PACKAGEKIT_DBUS_INTERFACE,
                           in_signature='ss',out_signature='')
commit c47ae978d1a3257b42bb999215c159067816bd57
Merge: ef4a0a5... 401ce09...
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Aug 7 12:41:43 2008 -0400

    Merge branch with contrib/packagekit-plugin

diff --cc Makefile.am
index ec3a4c2,7b1ece9..3abf004
--- a/Makefile.am
+++ b/Makefile.am
@@@ -7,9 -7,9 +7,8 @@@ SUBDIRS = 						
  	policy						\
  	etc						\
  	man						\
- 	contrib						\
  	data						\
  	libselftest					\
 -	libgbus						\
  	libpackagekit					\
  	src						\
  	client						\
diff --cc po/POTFILES.in
index 7bce8d0,9578858..539b8dd
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@@ -6,9 -5,7 +6,9 @@@ client/pk-generate-pack.
  client/pk-import-desktop.c
  client/pk-import-specspo.c
  client/pk-monitor.c
 +client/pk-tools-common.c
+ contrib/packagekit-plugin/src/contents.cpp
  data/packagekit-catalog.xml.in
 +data/packagekit-pack.xml.in
  policy/org.freedesktop.packagekit.policy.in
  src/pk-main.c
- 
commit 031299324d8aa779c2d21d7e7de43d121cf57ef8
Author: Sebastian Heinlein <devel at glatzor.de>
Date:   Thu Aug 7 05:54:15 2008 +0200

    APT: fix build issues

diff --git a/backends/apt/pk-backend-apt.c b/backends/apt/pk-backend-apt.c
index cb53e0f..32ec527 100644
--- a/backends/apt/pk-backend-apt.c
+++ b/backends/apt/pk-backend-apt.c
@@ -137,7 +137,7 @@ backend_remove_packages (PkBackend *backend, gchar **package_ids, gboolean allow
 static void
 backend_get_details (PkBackend *backend, gchar **package_ids)
 {
-	pk_backend_dbus_get_details (dbus, package_id);
+	pk_backend_dbus_get_details (dbus, package_ids);
 }
 
 /**
@@ -173,7 +173,7 @@ backend_cancel (PkBackend *backend)
 static void
 backend_resolve (PkBackend *backend, PkFilterEnum filters, gchar **package_ids)
 {
-	        pk_backend_dbus_resolve (dbus, filters, package_id);
+	        pk_backend_dbus_resolve (dbus, filters, package_ids);
 }
 
 /**
commit e3803462b190e30001d6aed9fdac67bfdf372dc2
Author: Sebastian Heinlein <devel at glatzor.de>
Date:   Thu Aug 7 05:51:59 2008 +0200

    Fix broken DownloadPackages() and doDownloadPackages() in the DBus backend and replace tabs by white spaces

diff --git a/python/packagekit/daemonBackend.py b/python/packagekit/daemonBackend.py
index b0062e7..0847279 100644
--- a/python/packagekit/daemonBackend.py
+++ b/python/packagekit/daemonBackend.py
@@ -430,21 +430,21 @@ class PackageKitBaseBackend(dbus.service.Object):
         self.Finished(EXIT_FAILED)
 
     @dbus.service.method(PACKAGEKIT_DBUS_INTERFACE,
-			 in_signature='ss',out_signature='')
-    def DownloadPackages(self,package_ids,directory)
-	'''
-	Implement the (backend)-download-packages functionality
-	'''
-	pklog.info("DownloadPackages(%s%s)" % (packages,directory))
-	self.doDownloadPackages(packages,directory)
-
-    def doDownloadPackages(self,package_ids,directory)
-	'''
-	Should be replaced in the corresponding backend sub class
-	'''
-	self.ErrorCode(ERROR_NOT_SUPPORTED,
-			"This function is not implemented in this backend")
-	self.Finished(EXIT_FAILED)
+                         in_signature='ss',out_signature='')
+    def DownloadPackages(self,package_ids,directory):
+        '''
+        Implement the (backend)-download-packages functionality
+        '''
+        pklog.info("DownloadPackages(%s%s)" % (packages,directory))
+        self.doDownloadPackages(packages,directory)
+
+    def doDownloadPackages(self,package_ids,directory):
+        '''
+        Should be replaced in the corresponding backend sub class
+        '''
+        self.ErrorCode(ERROR_NOT_SUPPORTED,
+                       "This function is not implemented in this backend")
+        self.Finished(EXIT_FAILED)
 
     @dbus.service.method(PACKAGEKIT_DBUS_INTERFACE,
                          in_signature='ss',out_signature='')
commit 00d0d02c8f99458c3d906d73a1258d4e9b068a4f
Author: Sebastian Heinlein <devel at glatzor.de>
Date:   Wed Aug 6 06:10:58 2008 +0200

    Get some fame.

diff --git a/docs/html/img/author-glatzor.png b/docs/html/img/author-glatzor.png
new file mode 100644
index 0000000..8c8c3ef
Binary files /dev/null and b/docs/html/img/author-glatzor.png differ
commit 09b368c5e52a5c60207f6979db55bc7698fa6854
Author: Sebastian Heinlein <devel at glatzor.de>
Date:   Wed Aug 6 05:48:34 2008 +0200

    APT: Fix doResolve. Thanks to Martin Pitt for the patch.

diff --git a/backends/apt/aptDBUSBackend.py b/backends/apt/aptDBUSBackend.py
index da1fad2..23765a4 100755
--- a/backends/apt/aptDBUSBackend.py
+++ b/backends/apt/aptDBUSBackend.py
@@ -528,8 +528,13 @@ class PackageKitAptBackend(PackageKitBaseBackend):
         self.AllowCancel(False)
 
         #FIXME: Support candidates
-        if self._cache.has_key(name) and self.is_package_visible(pkg, filters):
-            self._emit_package(name)
+        pkg = None
+        if self._cache.has_key(name):
+            pkg = self._cache[name]
+            if not self._is_package_visible(pkg, filters):
+                pkg = None
+        if pkg:
+            self._emit_package(pkg)
             self.Finished(EXIT_SUCCESS)
         else:
             self.ErrorCode(ERROR_PACKAGE_NOT_FOUND,
commit 401ce09651cefe3567174f263606964dbbc65065
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Mon Aug 4 12:29:44 2008 -0400

    Build changes for merged contrib/packagekit-plugin
    configure.ac: Merge in pkg-config checks for browser plugin
    Makefile.am: Reorder things to build contrib/ after libraries
    contrib/Makefile.am: Conditionally add plugin to SUBDURS
    po/POTFILES.in: Add plugin source file with messages
    contrib/packagekit-plugin: Fixes for i18n

diff --git a/Makefile.am b/Makefile.am
index 7e753cb..7b1ece9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,7 +7,6 @@ SUBDIRS = 						\
 	policy						\
 	etc						\
 	man						\
-	contrib						\
 	data						\
 	libselftest					\
 	libgbus						\
@@ -15,6 +14,7 @@ SUBDIRS = 						\
 	src						\
 	client						\
 	backends					\
+	contrib						\
 	python						\
 	docs						\
 	$(NULL)
diff --git a/configure.ac b/configure.ac
index e184e53..0a67846 100644
--- a/configure.ac
+++ b/configure.ac
@@ -240,6 +240,33 @@ if test x$enable_local = xyes; then
 fi
 
 dnl ---------------------------------------------------------------------------
+dnl - Checks for contrib/packagekit-plugin
+dnl ---------------------------------------------------------------------------
+
+PKG_CHECK_MODULES(PK_BROWSER_PLUGIN, mozilla-plugin gio-unix-2.0 cairo pango gtk+-2.0 dbus-glib-1,
+                  build_browser_plugin=yes, build_browser_plugin=no)
+
+AM_CONDITIONAL(PK_BUILD_BROWSER_PLUGIN, test $build_browser_plugin = "yes")
+		 
+AC_SUBST(PK_PLUGIN_CFLAGS)
+AC_SUBST(PK_PLUGIN_LIBS)
+
+if test $build_browser_plugin = "yes"; then
+	# Check if we have GdkAppLaunchContext (new in GTK+-2.14)
+
+	packagekit_save_CPPFLAGS="$CPPFLAGS"
+	packagekit_save_LDFLAGS="$LDFLAGS"
+
+	CPPFLAGS="$CPPFLAGS $PLUGIN_CFLAGS"
+	LDFLAGS="$CPPFLAGS $PLUGIN_LIBS"
+
+	AC_CHECK_FUNCS(gdk_app_launch_context_new)
+
+	CPPFLAGS="$packagekit_save_CPPFLAGS"
+	LDFLAGS="$packagekit_save_LDFLAGS"
+fi
+
+dnl ---------------------------------------------------------------------------
 dnl - Other tests
 dnl ---------------------------------------------------------------------------
 AC_ARG_ENABLE(gcov, AS_HELP_STRING([--enable-gcov],[compile with coverage profiling instrumentation (gcc only)]),
@@ -495,6 +522,7 @@ docs/api/Makefile
 docs/api/dbus/Makefile
 docs/api/version.xml
 contrib/Makefile
+contrib/packagekit-plugin/Makefile
 contrib/udev/Makefile
 contrib/yum-packagekit/Makefile
 backends/Makefile
@@ -557,6 +585,7 @@ echo "
         Security framework:        ${with_security_framework}
         Networking stack:          ${with_networking_stack}
         GIO support:               ${with_gio}
+        Browser Plugin: 	   ${build_browser_plugin}
 
         Backends:
         ALPM backend:              ${enable_alpm}
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index acfd88c..e7d1984 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -4,6 +4,10 @@ if BACKEND_TYPE_YUM
 SUBDIRS += yum-packagekit
 endif
 
+if PK_BUILD_BROWSER_PLUGIN
+SUBDIRS += packagekit-plugin
+endif
+
 bashcompletiondir = ${SYSCONFDIR}/bash_completion.d
 dist_bashcompletion_DATA = pk-completion.bash
 
diff --git a/contrib/packagekit-plugin/.gitignore b/contrib/packagekit-plugin/.gitignore
index a05c9f7..7ec02d6 100644
--- a/contrib/packagekit-plugin/.gitignore
+++ b/contrib/packagekit-plugin/.gitignore
@@ -1,4 +1,4 @@
-COPYING
+\COPYING
 INSTALL
 LIBS
 aclocal.m4
@@ -17,6 +17,7 @@ stamp-h1
 .deps
 .libs
 *.lo
+*.o
 *.la
 po/Makefile*
 po/POTFILES
diff --git a/contrib/packagekit-plugin/AUTHORS b/contrib/packagekit-plugin/AUTHORS
deleted file mode 100644
index e69de29..0000000
diff --git a/contrib/packagekit-plugin/ChangeLog b/contrib/packagekit-plugin/ChangeLog
deleted file mode 100644
index e69de29..0000000
diff --git a/contrib/packagekit-plugin/Makefile.am b/contrib/packagekit-plugin/Makefile.am
index 03ec221..dc0e8b0 100644
--- a/contrib/packagekit-plugin/Makefile.am
+++ b/contrib/packagekit-plugin/Makefile.am
@@ -1,15 +1,16 @@
-SUBDIRS=po .
-
 plugindir=$(libdir)/mozilla/plugins
 plugin_LTLIBRARIES = packagekit-plugin.la
 
 packagekit_plugin_la_LDFLAGS = -rpath $(plugindir) -module -avoid-version -no-undefined
 packagekit_plugin_la_CPPFLAGS =			\
-	$(PLUGIN_CFLAGS)			\
-	-DLOCALEDIR=\"$(localedir)\"		\
-	-I $(top_srcdir)/sdk
+	$(PK_BROWSER_PLUGIN_CFLAGS)		\
+	-DPACKAGE_LOCALE_DIR=\"$(localedir)\"	\
+	-I $(top_srcdir)/libpackagekit		\
+	-I $(srcdir)/sdk
 
-packagekit_plugin_la_LIBADD = $(PLUGIN_LIBS)
+packagekit_plugin_la_LIBADD =				\
+	$(PK_BROWSER_PLUGIN_LIBS)			\
+	$(top_builddir)/libpackagekit/libpackagekit.la
 
 packagekit_plugin_la_SOURCES = 	\
 	sdk/np_entry.cpp	\
@@ -23,13 +24,5 @@ packagekit_plugin_la_SOURCES = 	\
 	src/plugin.cpp		\
 	src/plugin.h
 
-DISTCLEANFILES =				\
-	intltool-extract			\
-	intltool-merge				\
-	intltool-update
-
 EXTRA_DIST =					\
-	intltool-extract.in 			\
-	intltool-merge.in 			\
-	intltool-update.in			\
-	test.html
+	tests/test.html
diff --git a/contrib/packagekit-plugin/NEWS b/contrib/packagekit-plugin/NEWS
deleted file mode 100644
index e69de29..0000000
diff --git a/contrib/packagekit-plugin/configure.ac b/contrib/packagekit-plugin/configure.ac
deleted file mode 100644
index 1021498..0000000
--- a/contrib/packagekit-plugin/configure.ac
+++ /dev/null
@@ -1,52 +0,0 @@
-dnl Process this file with autoconf to produce a configure script.
-
-AC_INIT(src/plugin.cpp)
-
-AM_INIT_AUTOMAKE([packagekit-plugin], 0.1)
-AM_CONFIG_HEADER(config.h)
-
-AM_MAINTAINER_MODE
-
-AC_PROG_CC
-AC_PROG_CXX
-AC_PROG_INSTALL
-
-AC_DISABLE_STATIC
-AM_PROG_LIBTOOL
-
-# Internationalisation
-IT_PROG_INTLTOOL([0.35.0])
-GETTEXT_PACKAGE=packagekit-plugin
-AC_SUBST([GETTEXT_PACKAGE])
-AM_GLIB_GNU_GETTEXT
-AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[gettext domain])
-
-changequote(,)dnl
-if test "x$GCC" = "xyes"; then
-  case " $CFLAGS " in
-  *[\ \	]-Wall[\ \	]*) ;;
-  *) CFLAGS="$CFLAGS -Wall" ;;
-  esac
-fi
-changequote([,])dnl
-
-PKG_CHECK_MODULES(PLUGIN, mozilla-plugin gio-unix-2.0 cairo pango gtk+-2.0 packagekit >= 0.2 dbus-glib-1)
-
-packagekit_save_CPPFLAGS="$CPPFLAGS"
-packagekit_save_LDFLAGS="$LDFLAGS"
-
-CPPFLAGS="$CPPFLAGS $PLUGIN_CFLAGS"
-LDFLAGS="$CPPFLAGS $PLUGIN_LIBS"
-
-AC_CHECK_FUNCS(gdk_app_launch_context_new)
-
-CPPFLAGS="$packagekit_save_CPPFLAGS"
-LDFLAGS="$packagekit_save_LDFLAGS"
-
-AC_SUBST(PLUGIN_CFLAGS)
-AC_SUBST(PLUGIN_LIBS)
-
-AC_OUTPUT([
-Makefile
-po/Makefile.in
-])
diff --git a/contrib/packagekit-plugin/po/ChangeLog b/contrib/packagekit-plugin/po/ChangeLog
deleted file mode 100644
index e69de29..0000000
diff --git a/contrib/packagekit-plugin/po/POTFILES.in b/contrib/packagekit-plugin/po/POTFILES.in
deleted file mode 100644
index 1b23d3d..0000000
--- a/contrib/packagekit-plugin/po/POTFILES.in
+++ /dev/null
@@ -1 +0,0 @@
-src/plugin.cpp
diff --git a/contrib/packagekit-plugin/src/contents.cpp b/contrib/packagekit-plugin/src/contents.cpp
index 313faaa..e3b78ab 100644
--- a/contrib/packagekit-plugin/src/contents.cpp
+++ b/contrib/packagekit-plugin/src/contents.cpp
@@ -41,16 +41,17 @@
 
 #include <string.h>
 
-#include <glib/gi18n.h>
+#include <glib/gi18n-lib.h>
 
 #include <cairo-xlib.h>
 #include <dlfcn.h>
 #include <pango/pangocairo.h>
-#include <packagekit/pk-package-id.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 #include <gio/gdesktopappinfo.h>
 
+#include "pk-package-id.h"
+
 #include "plugin.h"
 
 #define MARGIN 5
@@ -83,12 +84,12 @@ splitString(const char *str)
 PkpContents::PkpContents(const char *displayName,
                          const char *packageNames,
                          const char *desktopNames) :
-    mStatus(IN_PROGRESS),
     mPlugin(0),
+    mStatus(IN_PROGRESS),
+    mAppInfo(0),
     mDisplayName(displayName),
     mPackageNames(splitString(packageNames)),
     mDesktopNames(splitString(desktopNames)),
-    mAppInfo(0),
     mLayout(0),
     mInstallPackageProxy(0),
     mInstallPackageCall(0)
@@ -599,7 +600,10 @@ PkpContents::runApplication (Time time)
         return;
     }
 
-    GAppLaunchContext *context;
+    if (time == 0)
+        time = get_server_timestamp();
+
+    GAppLaunchContext *context = 0;
 #ifdef HAVE_GDK_APP_LAUNCH_CONTEXT_NEW
     context = gdk_app_launch_context_new();
     gdk_app_launch_context_set_timestamp(time);
diff --git a/contrib/packagekit-plugin/src/plugin.cpp b/contrib/packagekit-plugin/src/plugin.cpp
index 345b427..0ab42b2 100644
--- a/contrib/packagekit-plugin/src/plugin.cpp
+++ b/contrib/packagekit-plugin/src/plugin.cpp
@@ -43,11 +43,10 @@
 
 #include <string.h>
 
-#include <glib/gi18n.h>
+#include <glib/gi18n-lib.h>
 
 #include <cairo-xlib.h>
 #include <dlfcn.h>
-#include <packagekit/pk-package-id.h>
 
 #include "plugin.h"
 
@@ -104,7 +103,7 @@ NPError NS_PluginInitialize()
     make_module_resident();
     
 #ifdef ENABLE_NLS
-    bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+    bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
     bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 #endif
     
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 01dcc13..9578858 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,7 +5,7 @@ client/pk-console.c
 client/pk-import-desktop.c
 client/pk-import-specspo.c
 client/pk-monitor.c
+contrib/packagekit-plugin/src/contents.cpp
 data/packagekit-catalog.xml.in
 policy/org.freedesktop.packagekit.policy.in
 src/pk-main.c
-
commit a5279f989e31a615298ede1988fad37e39d8f715
Merge: 9454d4b... 27cce0d...
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Fri Aug 1 13:06:28 2008 -0400

    Merge in packagekit-plugin as contrib/packagekit-plugin
    This is web browser plugin that allows web sites to present a
    user interface for installing packages from configured repositories
    or running installed applications.

diff --cc contrib/packagekit-plugin/.gitignore
index 0000000,0000000..a05c9f7
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/.gitignore
@@@ -1,0 -1,0 +1,24 @@@
++COPYING
++INSTALL
++LIBS
++aclocal.m4
++autom4te.cache
++config.*
++configure
++depcomp
++install-sh
++intltool-*
++libtool
++ltmain.sh
++missing
++Makefile
++Makefile.in
++stamp-h1
++.deps
++.libs
++*.lo
++*.la
++po/Makefile*
++po/POTFILES
++po/stamp-it
++
diff --cc contrib/packagekit-plugin/AUTHORS
index 0000000,0000000..e69de29
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/AUTHORS
diff --cc contrib/packagekit-plugin/ChangeLog
index 0000000,0000000..e69de29
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/ChangeLog
diff --cc contrib/packagekit-plugin/Makefile.am
index 0000000,0000000..03ec221
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/Makefile.am
@@@ -1,0 -1,0 +1,35 @@@
++SUBDIRS=po .
++
++plugindir=$(libdir)/mozilla/plugins
++plugin_LTLIBRARIES = packagekit-plugin.la
++
++packagekit_plugin_la_LDFLAGS = -rpath $(plugindir) -module -avoid-version -no-undefined
++packagekit_plugin_la_CPPFLAGS =			\
++	$(PLUGIN_CFLAGS)			\
++	-DLOCALEDIR=\"$(localedir)\"		\
++	-I $(top_srcdir)/sdk
++
++packagekit_plugin_la_LIBADD = $(PLUGIN_LIBS)
++
++packagekit_plugin_la_SOURCES = 	\
++	sdk/np_entry.cpp	\
++	sdk/npn_gate.cpp	\
++	sdk/npp_gate.cpp	\
++	sdk/npplat.h		\
++	sdk/pluginbase.h	\
++				\
++	src/contents.cpp	\
++	src/contents.h		\
++	src/plugin.cpp		\
++	src/plugin.h
++
++DISTCLEANFILES =				\
++	intltool-extract			\
++	intltool-merge				\
++	intltool-update
++
++EXTRA_DIST =					\
++	intltool-extract.in 			\
++	intltool-merge.in 			\
++	intltool-update.in			\
++	test.html
diff --cc contrib/packagekit-plugin/NEWS
index 0000000,0000000..e69de29
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/NEWS
diff --cc contrib/packagekit-plugin/README
index 0000000,0000000..812a479
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/README
@@@ -1,0 -1,0 +1,151 @@@
++package-plugin
++==============
++This is a very simple browser plugin that is meant to allow a website
++to add a box to allow to install or run a particular piece of software
++provided in their distribution's repositories.
++
++If the package is not installed but is available in the package repository, the plugin
++will show:
++
++ +------------------------------------+
++ | _Install GNU Backgammon Now_       |
++ | Version: 20061119-14.fc9           |
++ +------------------------------------+
++
++Click on the plugin, and it will fire off gpk-install-package to install the package;
++the display changes to:
++
++ +------------------------------------+
++ | GNU Backgammon                     |
++ | Installing...                      |
++ +------------------------------------+
++
++once that is done, the plugin will show:
++
++ +------------------------------------+
++ | _Run GNU Backgammon_               |
++ | Installed version: 20061119-14.fc9 |
++ +------------------------------------+
++
++Clicking on it launches the application.
++
++Security Considerations
++=======================
++
++The design as a plugin is specifically meant to avoid revealing information about installed
++applications to the web page. None of the information in the plugin is revealed in the DOM
++tree. It might be possible to guess at results by subtle timing attacks, but it would at
++best be a probalistic thing.
++
++Wweb pages probably can trick the user into clicking on the plugin
++and installing or running apps they dont' want to install or run:
++
++ - The application title is supplied by the web page
++ - The web page could put elements over the plugin to make it appear
++
++(The second is why I consider the first to be acceptable.) However, importantly, the
++only applications that could be installed in that way are applications from the package
++repository already configured for the system. The only applications that can be run in 
++this way are applications already on the system. And there is no ability to pass command
++line arguments or files to the applications. So, the security risk should be minimal.
++
++Accessibility
++=============
++
++The design as a plugin (for the above security considerations) causes some problems for
++accessibility. In particular, there is no access into the plugin for screenreaders
++or other assistive technologies. Making this work would require the existence of a 
++system for integrating accessibility support between the browser and plugins, which,
++to my knowledge does not exist at the moment.
++
++Keynav is also missing at the moment, but could be added quite easily. (Basically,
++the Return keypress just needs to be treated like clicking on the plugin.)
++
++The fonts, font sizes, and colors used in the plugin come from the system theme, so they
++should not pose a problem for users with special needs. (But see discussion of 
++sizing below.)
++
++Cross Browser Support
++=====================
++
++The plugins acts as a standard windowless plugin, so it shouldn't be restricted
++to working in Firefox or in Gecko-based browsers.
++
++The plugin does assume that the toolkit hosting the browser is GTK+ for a couple
++of things:
++
++ - To get the theme colors and fonts
++ - To get a timestamp from the X server if one isn't provided in the button press
++   event when launching an app)
++
++These could be fixed if necessary. (Use XSETTINGS directly for fonts and colors,
++just skip the timestamp and let the newly launched app get placed in the
++background if not running GTK+ and no timestamp is provided.)
++
++More deeply, the plugin requires that the GLib main loop be running, so that 
++libpackagekit can receive asynchronous notifications from the PackageKit daemon.
++So, getting it to work in Konqueror is going to be hard unless Qt has been
++compiled to use the GLib main loop.
++
++Trying it out
++=============
++
++packagekit-plugin builds with the standard:
++
++ ./autogen.sh
++ ./configure
++ make
++
++Instead of doing 'make install', you can symlink .libs/packagekit-plugin into
++~/.mozilla/plugins. Then you can restart your browser and load the HTML page
++under tests/. 
++
++If you do this, you probably want to delete the symlink when you are
++done so that it doesn't interfere with a system install of the plugin.
++
++Using
++=====
++
++dd the following code to your web page:
++
++    <object type="application/x-packagekit-plugin" width="300" height="150">
++      <!-- Name that will be used in the user interface -->
++      <param name="displayname" value="GNU Backgammon"/>
++      <!-- Whitespace separated list of package names -->
++      <param name="packagenames" value="gnubg"/>
++      <!-- List of basenames of possible desktop files (no .desktop extension) -->
++      <param name="desktopnames" value="gnubg fedora-gnubg"/>
++    </object>
++
++Unfortunately browser plugins have no mechanism for size negotiation, so you
++have to specify a predetermined size in some fashion. You can do it with the pixel
++sizes as above, or you can do it with CSS using 'em' sizes or percentage 
++lengths. For example, you could have in your CSS:
++
++      .packagekit-plugin {
++         width: 30em;
++         height: 5em;
++      }
++
++And then add class="packagekit-plugin" to the object tag above.
++
++You should make the size generously bigger than what you need on your system
++to deal with translations and with varying font sizes and device resolutions.
++Using em sizes rather than points is probably a good idea to help with the
++font size issue, but it doesn't completely resolve it since the text of the
++plugin will be sized based on the system font size, not on the web pages font.
++
++License and Copyright
++=====================
++
++You may distribute the code under the terms of the Mozilla Public License 1.1,
++GNU General Public License Version 2 or later (the "GPL"), or the
++GNU Lesser General Public License Version 2.1 or later (the "LGPL"). See the
++source files for details and for copyright information.
++
++The plugin infrastructure (The sdk/ directory and much of src/plugin.cpp
++and src/plugin.h) is derived from the Mozilla plugin SDK; other code under
++src/ is new code written by Red Hat, Inc.
++
++Owen Taylor <otaylor at redhat.com>
++July 24, 2008
diff --cc contrib/packagekit-plugin/autogen.sh
index 0000000,0000000..45eeb39
new file mode 100755
--- /dev/null
+++ b/contrib/packagekit-plugin/autogen.sh
@@@ -1,0 -1,0 +1,77 @@@
++#!/bin/sh
++
++srcdir=`dirname $0`
++test -z "$srcdir" && srcdir=.
++
++ORIGDIR=`pwd`
++cd $srcdir
++PACKAGE="packagekit-plugin"
++
++have_libtool=false
++have_autoconf=false
++have_automake=false
++need_configure_in=false
++
++if libtool --version < /dev/null > /dev/null 2>&1 ; then
++	libtool_version=`libtoolize --version | sed 's/^[^0-9]*\([0-9.][0-9.]*\).*/\1/'`
++	have_libtool=true
++	case $libtool_version in
++	    1.3*)
++		need_configure_in=true
++		;;
++	esac
++fi
++
++if autoconf --version < /dev/null > /dev/null 2>&1 ; then
++	autoconf_version=`autoconf --version | sed 's/^[^0-9]*\([0-9.][0-9.]*\).*/\1/'`
++	have_autoconf=true
++	case $autoconf_version in
++	    2.13)
++		need_configure_in=true
++		;;
++	esac
++fi
++
++if $have_libtool ; then : ; else
++	echo;
++	echo "You must have libtool >= 1.3 installed to compile $PACKAGE";
++	echo;
++	exit;
++fi
++
++(automake --version) < /dev/null > /dev/null 2>&1 || {
++	echo;
++	echo "You must have automake installed to compile $PACKAGE";
++	echo;
++	exit;
++}
++
++(intltoolize --version) < /dev/null > /dev/null 2>&1 || {
++	echo;
++	echo "You must have intltool installed to compile $PACKAGE";
++	echo;
++	exit;
++}
++
++echo "Generating configuration files for $PACKAGE, please wait...."
++echo;
++
++if $need_configure_in ; then
++    if test ! -f configure.in ; then
++	echo "Creating symlink from configure.in to configure.ac..."
++	echo
++	ln -s configure.ac configure.in
++    fi
++fi
++
++aclocal $ACLOCAL_FLAGS
++libtoolize --force
++autoheader
++automake --add-missing -Woverride
++autoconf
++intltoolize
++
++cd $ORIGDIR || exit $?
++
++$srcdir/configure $@ --enable-maintainer-mode --enable-compile-warnings
++
diff --cc contrib/packagekit-plugin/configure.ac
index 0000000,0000000..1021498
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/configure.ac
@@@ -1,0 -1,0 +1,52 @@@
++dnl Process this file with autoconf to produce a configure script.
++
++AC_INIT(src/plugin.cpp)
++
++AM_INIT_AUTOMAKE([packagekit-plugin], 0.1)
++AM_CONFIG_HEADER(config.h)
++
++AM_MAINTAINER_MODE
++
++AC_PROG_CC
++AC_PROG_CXX
++AC_PROG_INSTALL
++
++AC_DISABLE_STATIC
++AM_PROG_LIBTOOL
++
++# Internationalisation
++IT_PROG_INTLTOOL([0.35.0])
++GETTEXT_PACKAGE=packagekit-plugin
++AC_SUBST([GETTEXT_PACKAGE])
++AM_GLIB_GNU_GETTEXT
++AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[gettext domain])
++
++changequote(,)dnl
++if test "x$GCC" = "xyes"; then
++  case " $CFLAGS " in
++  *[\ \	]-Wall[\ \	]*) ;;
++  *) CFLAGS="$CFLAGS -Wall" ;;
++  esac
++fi
++changequote([,])dnl
++
++PKG_CHECK_MODULES(PLUGIN, mozilla-plugin gio-unix-2.0 cairo pango gtk+-2.0 packagekit >= 0.2 dbus-glib-1)
++
++packagekit_save_CPPFLAGS="$CPPFLAGS"
++packagekit_save_LDFLAGS="$LDFLAGS"
++
++CPPFLAGS="$CPPFLAGS $PLUGIN_CFLAGS"
++LDFLAGS="$CPPFLAGS $PLUGIN_LIBS"
++
++AC_CHECK_FUNCS(gdk_app_launch_context_new)
++
++CPPFLAGS="$packagekit_save_CPPFLAGS"
++LDFLAGS="$packagekit_save_LDFLAGS"
++
++AC_SUBST(PLUGIN_CFLAGS)
++AC_SUBST(PLUGIN_LIBS)
++
++AC_OUTPUT([
++Makefile
++po/Makefile.in
++])
diff --cc contrib/packagekit-plugin/po/ChangeLog
index 0000000,0000000..e69de29
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/po/ChangeLog
diff --cc contrib/packagekit-plugin/po/POTFILES.in
index 0000000,0000000..1b23d3d
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/po/POTFILES.in
@@@ -1,0 -1,0 +1,1 @@@
++src/plugin.cpp
diff --cc contrib/packagekit-plugin/sdk/np_entry.cpp
index 0000000,0000000..05323e9
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/sdk/np_entry.cpp
@@@ -1,0 -1,0 +1,320 @@@
++/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is mozilla.org code.
++ *
++ * The Initial Developer of the Original Code is
++ * Netscape Communications Corporation.
++ * Portions created by the Initial Developer are Copyright (C) 1998
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++//////////////////////////////////////////////////////////////
++//
++// Main plugin entry point implementation -- exports from the 
++// plugin library
++//
++#include "npplat.h"
++#include "pluginbase.h"
++
++NPNetscapeFuncs NPNFuncs;
++
++NPError OSCALL NP_Shutdown()
++{
++  NS_PluginShutdown();
++  return NPERR_NO_ERROR;
++}
++
++static NPError fillPluginFunctionTable(NPPluginFuncs* aNPPFuncs)
++{
++  if(aNPPFuncs == NULL)
++    return NPERR_INVALID_FUNCTABLE_ERROR;
++
++  // Set up the plugin function table that Netscape will use to
++  // call us. Netscape needs to know about our version and size   
++  // and have a UniversalProcPointer for every function we implement.
++
++  aNPPFuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
++#ifdef XP_MAC
++  aNPPFuncs->newp          = NewNPP_NewProc(Private_New);
++  aNPPFuncs->destroy       = NewNPP_DestroyProc(Private_Destroy);
++  aNPPFuncs->setwindow     = NewNPP_SetWindowProc(Private_SetWindow);
++  aNPPFuncs->newstream     = NewNPP_NewStreamProc(Private_NewStream);
++  aNPPFuncs->destroystream = NewNPP_DestroyStreamProc(Private_DestroyStream);
++  aNPPFuncs->asfile        = NewNPP_StreamAsFileProc(Private_StreamAsFile);
++  aNPPFuncs->writeready    = NewNPP_WriteReadyProc(Private_WriteReady);
++  aNPPFuncs->write         = NewNPP_WriteProc(Private_Write);
++  aNPPFuncs->print         = NewNPP_PrintProc(Private_Print);
++  aNPPFuncs->event         = NewNPP_HandleEventProc(Private_HandleEvent);	
++  aNPPFuncs->urlnotify     = NewNPP_URLNotifyProc(Private_URLNotify);			
++  aNPPFuncs->getvalue      = NewNPP_GetValueProc(Private_GetValue);
++  aNPPFuncs->setvalue      = NewNPP_SetValueProc(Private_SetValue);
++#else
++  aNPPFuncs->newp          = NPP_New;
++  aNPPFuncs->destroy       = NPP_Destroy;
++  aNPPFuncs->setwindow     = NPP_SetWindow;
++  aNPPFuncs->newstream     = NPP_NewStream;
++  aNPPFuncs->destroystream = NPP_DestroyStream;
++  aNPPFuncs->asfile        = NPP_StreamAsFile;
++  aNPPFuncs->writeready    = NPP_WriteReady;
++  aNPPFuncs->write         = NPP_Write;
++  aNPPFuncs->print         = NPP_Print;
++  aNPPFuncs->event         = NPP_HandleEvent;
++  aNPPFuncs->urlnotify     = NPP_URLNotify;
++  aNPPFuncs->getvalue      = NPP_GetValue;
++  aNPPFuncs->setvalue      = NPP_SetValue;
++#endif
++#ifdef OJI
++  aNPPFuncs->javaClass     = NULL;
++#endif
++
++  return NPERR_NO_ERROR;
++}
++
++static NPError fillNetscapeFunctionTable(NPNetscapeFuncs* aNPNFuncs)
++{
++  if(aNPNFuncs == NULL)
++    return NPERR_INVALID_FUNCTABLE_ERROR;
++
++  if(HIBYTE(aNPNFuncs->version) > NP_VERSION_MAJOR)
++    return NPERR_INCOMPATIBLE_VERSION_ERROR;
++
++  if(aNPNFuncs->size < sizeof(NPNetscapeFuncs))
++    return NPERR_INVALID_FUNCTABLE_ERROR;
++
++  NPNFuncs.size             = aNPNFuncs->size;
++  NPNFuncs.version          = aNPNFuncs->version;
++  NPNFuncs.geturlnotify     = aNPNFuncs->geturlnotify;
++  NPNFuncs.geturl           = aNPNFuncs->geturl;
++  NPNFuncs.posturlnotify    = aNPNFuncs->posturlnotify;
++  NPNFuncs.posturl          = aNPNFuncs->posturl;
++  NPNFuncs.requestread      = aNPNFuncs->requestread;
++  NPNFuncs.newstream        = aNPNFuncs->newstream;
++  NPNFuncs.write            = aNPNFuncs->write;
++  NPNFuncs.destroystream    = aNPNFuncs->destroystream;
++  NPNFuncs.status           = aNPNFuncs->status;
++  NPNFuncs.uagent           = aNPNFuncs->uagent;
++  NPNFuncs.memalloc         = aNPNFuncs->memalloc;
++  NPNFuncs.memfree          = aNPNFuncs->memfree;
++  NPNFuncs.memflush         = aNPNFuncs->memflush;
++  NPNFuncs.reloadplugins    = aNPNFuncs->reloadplugins;
++#ifdef OJI
++  NPNFuncs.getJavaEnv       = aNPNFuncs->getJavaEnv;
++  NPNFuncs.getJavaPeer      = aNPNFuncs->getJavaPeer;
++#endif
++  NPNFuncs.getvalue         = aNPNFuncs->getvalue;
++  NPNFuncs.setvalue         = aNPNFuncs->setvalue;
++  NPNFuncs.invalidaterect   = aNPNFuncs->invalidaterect;
++  NPNFuncs.invalidateregion = aNPNFuncs->invalidateregion;
++  NPNFuncs.forceredraw      = aNPNFuncs->forceredraw;
++
++  return NPERR_NO_ERROR;
++}
++
++//
++// Some exports are different on different platforms
++//
++
++/**************************************************/
++/*                                                */
++/*                   Windows                      */
++/*                                                */
++/**************************************************/
++#ifdef XP_WIN
++
++NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs)
++{
++  NPError rv = fillNetscapeFunctionTable(aNPNFuncs);
++  if(rv != NPERR_NO_ERROR)
++    return rv;
++
++  return NS_PluginInitialize();
++}
++
++NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* aNPPFuncs)
++{
++  return fillPluginFunctionTable(aNPPFuncs);
++}
++
++#endif //XP_WIN
++
++/**************************************************/
++/*                                                */
++/*                    Unix                        */
++/*                                                */
++/**************************************************/
++#ifdef XP_UNIX
++
++NPError NP_Initialize(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs)
++{
++  NPError rv = fillNetscapeFunctionTable(aNPNFuncs);
++  if(rv != NPERR_NO_ERROR)
++    return rv;
++
++  rv = fillPluginFunctionTable(aNPPFuncs);
++  if(rv != NPERR_NO_ERROR)
++    return rv;
++
++  return NS_PluginInitialize();
++}
++
++char * NP_GetMIMEDescription(void)
++{
++  return NPP_GetMIMEDescription();
++}
++
++NPError NP_GetValue(void *future, NPPVariable aVariable, void *aValue)
++{
++  return NS_PluginGetValue(aVariable, aValue);
++}
++
++#endif //XP_UNIX
++
++/**************************************************/
++/*                                                */
++/*                     Mac                        */
++/*                                                */
++/**************************************************/
++#ifdef XP_MAC
++
++#if !TARGET_API_MAC_CARBON
++QDGlobals* gQDPtr; // Pointer to Netscape's QuickDraw globals
++#endif
++
++short gResFile; // Refnum of the plugin's resource file
++
++NPError Private_Initialize(void)
++{
++  NPError rv = NS_PluginInitialize();
++  return rv;
++}
++
++void Private_Shutdown(void)
++{
++  NS_PluginShutdown();
++  __destroy_global_chain();
++}
++
++void SetUpQD(void);
++
++void SetUpQD(void)
++{
++  ProcessSerialNumber PSN;
++  FSSpec              myFSSpec;
++  Str63               name;
++  ProcessInfoRec      infoRec;
++  OSErr               result = noErr;
++  CFragConnectionID   connID;
++  Str255              errName;
++
++  // Memorize the plugin¹s resource file refnum for later use.
++  gResFile = CurResFile();
++
++#if !TARGET_API_MAC_CARBON
++  // Ask the system if CFM is available.
++  long response;
++  OSErr err = Gestalt(gestaltCFMAttr, &response);
++  Boolean hasCFM = BitTst(&response, 31-gestaltCFMPresent);
++
++  if (hasCFM) {
++    // GetProcessInformation takes a process serial number and 
++    // will give us back the name and FSSpec of the application.
++    // See the Process Manager in IM.
++    infoRec.processInfoLength = sizeof(ProcessInfoRec);
++    infoRec.processName = name;
++    infoRec.processAppSpec = &myFSSpec;
++
++    PSN.highLongOfPSN = 0;
++    PSN.lowLongOfPSN = kCurrentProcess;
++
++    result = GetProcessInformation(&PSN, &infoRec);
++  }
++	else
++    // If no CFM installed, assume it must be a 68K app.
++    result = -1;		
++
++  if (result == noErr) {
++    // Now that we know the app name and FSSpec, we can call GetDiskFragment
++    // to get a connID to use in a subsequent call to FindSymbol (it will also
++    // return the address of ³main² in app, which we ignore).  If GetDiskFragment 
++    // returns an error, we assume the app must be 68K.
++    Ptr mainAddr; 	
++    result =  GetDiskFragment(infoRec.processAppSpec, 0L, 0L, infoRec.processName,
++                              kReferenceCFrag, &connID, (Ptr*)&mainAddr, errName);
++  }
++
++  if (result == noErr) {
++    // The app is a PPC code fragment, so call FindSymbol
++    // to get the exported ³qd² symbol so we can access its
++    // QuickDraw globals.
++    CFragSymbolClass symClass;
++    result = FindSymbol(connID, "\pqd", (Ptr*)&gQDPtr, &symClass);
++  }
++  else {
++    // The app is 68K, so use its A5 to compute the address
++    // of its QuickDraw globals.
++    gQDPtr = (QDGlobals*)(*((long*)SetCurrentA5()) - (sizeof(QDGlobals) - sizeof(GrafPtr)));
++  }
++#endif /* !TARGET_API_MAC_CARBON */
++}
++
++NPError main(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs, NPP_ShutdownUPP* unloadUpp);
++
++#if !TARGET_API_MAC_CARBON
++#pragma export on
++#if GENERATINGCFM
++RoutineDescriptor mainRD = BUILD_ROUTINE_DESCRIPTOR(uppNPP_MainEntryProcInfo, main);
++#endif
++#pragma export off
++#endif /* !TARGET_API_MAC_CARBON */
++
++
++NPError main(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs, NPP_ShutdownUPP* aUnloadUpp)
++{
++  NPError rv = NPERR_NO_ERROR;
++
++  if (aUnloadUpp == NULL)
++    rv = NPERR_INVALID_FUNCTABLE_ERROR;
++
++  if (rv == NPERR_NO_ERROR)
++    rv = fillNetscapeFunctionTable(aNPNFuncs);
++
++  if (rv == NPERR_NO_ERROR) {
++    // defer static constructors until the global functions are initialized.
++    __InitCode__();
++    rv = fillPluginFunctionTable(aNPPFuncs);
++  }
++
++  *aUnloadUpp = NewNPP_ShutdownProc(Private_Shutdown);
++  SetUpQD();
++  rv = Private_Initialize();
++	
++  return rv;
++}
++#endif //XP_MAC
diff --cc contrib/packagekit-plugin/sdk/npn_gate.cpp
index 0000000,0000000..f5caa32
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/sdk/npn_gate.cpp
@@@ -1,0 -1,0 +1,215 @@@
++/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is mozilla.org code.
++ *
++ * The Initial Developer of the Original Code is
++ * Netscape Communications Corporation.
++ * Portions created by the Initial Developer are Copyright (C) 1998
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++
++////////////////////////////////////////////////////////////
++//
++// Implementation of Netscape entry points (NPN_*)
++//
++#include "npplat.h"
++
++extern NPNetscapeFuncs NPNFuncs;
++
++void NPN_Version(int* plugin_major, int* plugin_minor, int* netscape_major, int* netscape_minor)
++{
++  *plugin_major   = NP_VERSION_MAJOR;
++  *plugin_minor   = NP_VERSION_MINOR;
++  *netscape_major = HIBYTE(NPNFuncs.version);
++  *netscape_minor = LOBYTE(NPNFuncs.version);
++}
++
++NPError NPN_GetURLNotify(NPP instance, const char *url, const char *target, void* notifyData)
++{
++	int navMinorVers = NPNFuncs.version & 0xFF;
++  NPError rv = NPERR_NO_ERROR;
++
++  if( navMinorVers >= NPVERS_HAS_NOTIFICATION )
++		rv = CallNPN_GetURLNotifyProc(NPNFuncs.geturlnotify, instance, url, target, notifyData);
++	else
++		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
++
++  return rv;
++}
++
++NPError NPN_GetURL(NPP instance, const char *url, const char *target)
++{
++  NPError rv = CallNPN_GetURLProc(NPNFuncs.geturl, instance, url, target);
++  return rv;
++}
++
++NPError NPN_PostURLNotify(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData)
++{
++	int navMinorVers = NPNFuncs.version & 0xFF;
++  NPError rv = NPERR_NO_ERROR;
++
++	if( navMinorVers >= NPVERS_HAS_NOTIFICATION )
++		rv = CallNPN_PostURLNotifyProc(NPNFuncs.posturlnotify, instance, url, window, len, buf, file, notifyData);
++	else
++		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
++
++  return rv;
++}
++
++NPError NPN_PostURL(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file)
++{
++  NPError rv = CallNPN_PostURLProc(NPNFuncs.posturl, instance, url, window, len, buf, file);
++  return rv;
++} 
++
++NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList)
++{
++  NPError rv = CallNPN_RequestReadProc(NPNFuncs.requestread, stream, rangeList);
++  return rv;
++}
++
++NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream)
++{
++	int navMinorVersion = NPNFuncs.version & 0xFF;
++
++  NPError rv = NPERR_NO_ERROR;
++
++	if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
++		rv = CallNPN_NewStreamProc(NPNFuncs.newstream, instance, type, target, stream);
++	else
++		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
++
++  return rv;
++}
++
++int32 NPN_Write(NPP instance, NPStream *stream, int32 len, void *buffer)
++{
++	int navMinorVersion = NPNFuncs.version & 0xFF;
++  int32 rv = 0;
++
++  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
++		rv = CallNPN_WriteProc(NPNFuncs.write, instance, stream, len, buffer);
++	else
++		rv = -1;
++
++  return rv;
++}
++
++NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason)
++{
++	int navMinorVersion = NPNFuncs.version & 0xFF;
++  NPError rv = NPERR_NO_ERROR;
++
++  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
++		rv = CallNPN_DestroyStreamProc(NPNFuncs.destroystream, instance, stream, reason);
++	else
++		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
++
++  return rv;
++}
++
++void NPN_Status(NPP instance, const char *message)
++{
++  CallNPN_StatusProc(NPNFuncs.status, instance, message);
++}
++
++const char* NPN_UserAgent(NPP instance)
++{
++  const char * rv = NULL;
++  rv = CallNPN_UserAgentProc(NPNFuncs.uagent, instance);
++  return rv;
++}
++
++void* NPN_MemAlloc(uint32 size)
++{
++  void * rv = NULL;
++  rv = CallNPN_MemAllocProc(NPNFuncs.memalloc, size);
++  return rv;
++}
++
++void NPN_MemFree(void* ptr)
++{
++  CallNPN_MemFreeProc(NPNFuncs.memfree, ptr);
++}
++
++uint32 NPN_MemFlush(uint32 size)
++{
++  uint32 rv = CallNPN_MemFlushProc(NPNFuncs.memflush, size);
++  return rv;
++}
++
++void NPN_ReloadPlugins(NPBool reloadPages)
++{
++  CallNPN_ReloadPluginsProc(NPNFuncs.reloadplugins, reloadPages);
++}
++
++#ifdef OJI
++JRIEnv* NPN_GetJavaEnv(void)
++{
++  JRIEnv * rv = NULL;
++	rv = CallNPN_GetJavaEnvProc(NPNFuncs.getJavaEnv);
++  return rv;
++}
++
++jref NPN_GetJavaPeer(NPP instance)
++{
++  jref rv;
++  rv = CallNPN_GetJavaPeerProc(NPNFuncs.getJavaPeer, instance);
++  return rv;
++}
++#endif
++
++NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value)
++{
++  NPError rv = CallNPN_GetValueProc(NPNFuncs.getvalue, instance, variable, value);
++  return rv;
++}
++
++NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value)
++{
++  NPError rv = CallNPN_SetValueProc(NPNFuncs.setvalue, instance, variable, value);
++  return rv;
++}
++
++void NPN_InvalidateRect(NPP instance, NPRect *invalidRect)
++{
++  CallNPN_InvalidateRectProc(NPNFuncs.invalidaterect, instance, invalidRect);
++}
++
++void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion)
++{
++  CallNPN_InvalidateRegionProc(NPNFuncs.invalidateregion, instance, invalidRegion);
++}
++
++void NPN_ForceRedraw(NPP instance)
++{
++  CallNPN_ForceRedrawProc(NPNFuncs.forceredraw, instance);
++}
diff --cc contrib/packagekit-plugin/sdk/npp_gate.cpp
index 0000000,0000000..3e22783
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/sdk/npp_gate.cpp
@@@ -1,0 -1,0 +1,358 @@@
++/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is mozilla.org code.
++ *
++ * The Initial Developer of the Original Code is
++ * Netscape Communications Corporation.
++ * Portions created by the Initial Developer are Copyright (C) 1998
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++
++////////////////////////////////////////////////////////////
++//
++// Implementation of plugin entry points (NPP_*)
++//
++#include "pluginbase.h"
++
++// here the plugin creates a plugin instance object which 
++// will be associated with this newly created NPP instance and 
++// will do all the necessary job
++NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)
++{   
++  if(instance == NULL)
++    return NPERR_INVALID_INSTANCE_ERROR;
++
++  NPError rv = NPERR_NO_ERROR;
++
++  // create a new plugin instance object
++  // initialization will be done when the associated window is ready
++  nsPluginCreateData ds;
++  
++  ds.instance = instance;
++  ds.type     = pluginType; 
++  ds.mode     = mode; 
++  ds.argc     = argc; 
++  ds.argn     = argn; 
++  ds.argv     = argv; 
++  ds.saved    = saved;
++
++  nsPluginInstanceBase * plugin = NS_NewPluginInstance(&ds);
++  if(plugin == NULL)
++    return NPERR_OUT_OF_MEMORY_ERROR;
++
++  // associate the plugin instance object with NPP instance
++  instance->pdata = (void *)plugin;
++  return rv;
++}
++
++// here is the place to clean up and destroy the nsPluginInstance object
++NPError NPP_Destroy (NPP instance, NPSavedData** save)
++{
++  if(instance == NULL)
++    return NPERR_INVALID_INSTANCE_ERROR;
++
++  NPError rv = NPERR_NO_ERROR;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin != NULL) {
++    plugin->shut();
++    NS_DestroyPluginInstance(plugin);
++  }
++  return rv;
++}
++
++// during this call we know when the plugin window is ready or
++// is about to be destroyed so we can do some gui specific
++// initialization and shutdown
++NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow)
++{    
++  if(instance == NULL)
++    return NPERR_INVALID_INSTANCE_ERROR;
++
++  NPError rv = NPERR_NO_ERROR;
++
++  if(pNPWindow == NULL)
++    return NPERR_GENERIC_ERROR;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++
++  if(plugin == NULL) 
++    return NPERR_GENERIC_ERROR;
++
++  // window just created
++  if(!plugin->isInitialized() && (pNPWindow->window != NULL)) { 
++    if(!plugin->init(pNPWindow)) {
++      NS_DestroyPluginInstance(plugin);
++      return NPERR_MODULE_LOAD_FAILED_ERROR;
++    }
++  }
++
++  // window goes away
++  if((pNPWindow->window == NULL) && plugin->isInitialized())
++    return plugin->SetWindow(pNPWindow);
++
++  // window resized?
++  if(plugin->isInitialized() && (pNPWindow->window != NULL))
++    return plugin->SetWindow(pNPWindow);
++
++  // this should not happen, nothing to do
++  if((pNPWindow->window == NULL) && !plugin->isInitialized())
++    return plugin->SetWindow(pNPWindow);
++
++  return rv;
++}
++
++NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
++{
++  if(instance == NULL)
++    return NPERR_INVALID_INSTANCE_ERROR;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return NPERR_GENERIC_ERROR;
++
++  NPError rv = plugin->NewStream(type, stream, seekable, stype);
++  return rv;
++}
++
++int32 NPP_WriteReady (NPP instance, NPStream *stream)
++{
++  if(instance == NULL)
++    return 0x0fffffff;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return 0x0fffffff;
++
++  int32 rv = plugin->WriteReady(stream);
++  return rv;
++}
++
++int32 NPP_Write (NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
++{   
++  if(instance == NULL)
++    return len;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return len;
++
++  int32 rv = plugin->Write(stream, offset, len, buffer);
++  return rv;
++}
++
++NPError NPP_DestroyStream (NPP instance, NPStream *stream, NPError reason)
++{
++  if(instance == NULL)
++    return NPERR_INVALID_INSTANCE_ERROR;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return NPERR_GENERIC_ERROR;
++
++  NPError rv = plugin->DestroyStream(stream, reason);
++  return rv;
++}
++
++void NPP_StreamAsFile (NPP instance, NPStream* stream, const char* fname)
++{
++  if(instance == NULL)
++    return;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return;
++
++  plugin->StreamAsFile(stream, fname);
++}
++
++void NPP_Print (NPP instance, NPPrint* printInfo)
++{
++  if(instance == NULL)
++    return;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return;
++
++  plugin->Print(printInfo);
++}
++
++void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
++{
++  if(instance == NULL)
++    return;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return;
++
++  plugin->URLNotify(url, reason, notifyData);
++}
++
++NPError	NPP_GetValue(NPP instance, NPPVariable variable, void *value)
++{
++  if(instance == NULL)
++    return NPERR_INVALID_INSTANCE_ERROR;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return NPERR_GENERIC_ERROR;
++
++  NPError rv = plugin->GetValue(variable, value);
++  return rv;
++}
++
++NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
++{
++  if(instance == NULL)
++    return NPERR_INVALID_INSTANCE_ERROR;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return NPERR_GENERIC_ERROR;
++
++  NPError rv = plugin->SetValue(variable, value);
++  return rv;
++}
++
++int16	NPP_HandleEvent(NPP instance, void* event)
++{
++  if(instance == NULL)
++    return 0;
++
++  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
++  if(plugin == NULL) 
++    return 0;
++
++  uint16 rv = plugin->HandleEvent(event);
++  return rv;
++}
++
++#ifdef OJI
++jref NPP_GetJavaClass (void)
++{
++  return NULL;
++}
++#endif
++
++/**************************************************/
++/*                                                */
++/*                     Mac                        */
++/*                                                */
++/**************************************************/
++
++// Mac needs these wrappers, see npplat.h for more info
++
++#ifdef XP_MAC
++
++NPError	Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)
++{
++  NPError rv = NPP_New(pluginType, instance, mode, argc, argn, argv, saved);
++  return rv;	
++}
++
++NPError Private_Destroy(NPP instance, NPSavedData** save)
++{
++  NPError rv = NPP_Destroy(instance, save);
++  return rv;
++}
++
++NPError Private_SetWindow(NPP instance, NPWindow* window)
++{
++  NPError rv = NPP_SetWindow(instance, window);
++  return rv;
++}
++
++NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
++{
++  NPError rv = NPP_NewStream(instance, type, stream, seekable, stype);
++  return rv;
++}
++
++int32 Private_WriteReady(NPP instance, NPStream* stream)
++{
++  int32 rv = NPP_WriteReady(instance, stream);
++  return rv;
++}
++
++int32 Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
++{
++  int32 rv = NPP_Write(instance, stream, offset, len, buffer);
++  return rv;
++}
++
++void Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
++{
++  NPP_StreamAsFile(instance, stream, fname);
++}
++
++
++NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason)
++{
++  NPError rv = NPP_DestroyStream(instance, stream, reason);
++  return rv;
++}
++
++int16 Private_HandleEvent(NPP instance, void* event)
++{
++  int16 rv = NPP_HandleEvent(instance, event);
++  return rv;
++}
++
++void Private_Print(NPP instance, NPPrint* platformPrint)
++{
++  NPP_Print(instance, platformPrint);
++}
++
++void Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
++{
++  NPP_URLNotify(instance, url, reason, notifyData);
++}
++
++jref Private_GetJavaClass(void)
++{
++  return NULL;
++}
++
++NPError Private_GetValue(NPP instance, NPPVariable variable, void *result)
++{
++  NPError rv = NPP_GetValue(instance, variable, result);
++  return rv;
++}
++
++NPError Private_SetValue(NPP instance, NPNVariable variable, void *value)
++{
++  NPError rv = NPP_SetValue(instance, variable, value);
++  return rv;
++}
++
++#endif //XP_MAC
diff --cc contrib/packagekit-plugin/sdk/npplat.h
index 0000000,0000000..637ac36
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/sdk/npplat.h
@@@ -1,0 -1,0 +1,150 @@@
++/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is mozilla.org code.
++ *
++ * The Initial Developer of the Original Code is
++ * Netscape Communications Corporation.
++ * Portions created by the Initial Developer are Copyright (C) 1998
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++#ifndef _NPPLAT_H_
++#define _NPPLAT_H_
++
++#include "npapi.h"
++#include "npupp.h"
++
++/**************************************************/
++/*                                                */
++/*                   Windows                      */
++/*                                                */
++/**************************************************/
++#ifdef XP_WIN
++#include "windows.h"
++#endif //XP_WIN
++
++/**************************************************/
++/*                                                */
++/*                    Unix                        */
++/*                                                */
++/**************************************************/
++#ifdef XP_UNIX
++#include <stdio.h>
++#endif //XP_UNIX
++
++/**************************************************/
++/*                                                */
++/*                     Mac                        */
++/*                                                */
++/**************************************************/
++#ifdef XP_MAC
++
++#include <Processes.h>
++#include <Gestalt.h>
++#include <CodeFragments.h>
++#include <Timer.h>
++#include <Resources.h>
++#include <ToolUtils.h>
++
++#include "jri.h"
++
++// The Mixed Mode procInfos defined in npupp.h assume Think C-
++// style calling conventions.  These conventions are used by
++// Metrowerks with the exception of pointer return types, which
++// in Metrowerks 68K are returned in A0, instead of the standard
++// D0. Thus, since NPN_MemAlloc and NPN_UserAgent return pointers,
++// Mixed Mode will return the values to a 68K plugin in D0, but 
++// a 68K plugin compiled by Metrowerks will expect the result in
++// A0.  The following pragma forces Metrowerks to use D0 instead.
++//
++#ifdef __MWERKS__
++#ifndef powerc
++#pragma pointers_in_D0
++#endif
++#endif
++
++#ifdef __MWERKS__
++#ifndef powerc
++#pragma pointers_in_A0
++#endif
++#endif
++
++// The following fix for static initializers (which fixes a preious
++// incompatibility with some parts of PowerPlant, was submitted by 
++// Jan Ulbrich.
++#ifdef __MWERKS__
++	#ifdef __cplusplus
++	extern "C" {
++	#endif
++		#ifndef powerc
++			extern void	__InitCode__(void);
++		#else
++			extern void __sinit(void);
++			#define __InitCode__ __sinit
++		#endif
++		extern void	__destroy_global_chain(void);
++	#ifdef __cplusplus
++	}
++	#endif // __cplusplus
++#endif // __MWERKS__
++
++// Wrapper functions for all calls from Netscape to the plugin.
++// These functions let the plugin developer just create the APIs
++// as documented and defined in npapi.h, without needing to 
++// install those functions in the function table or worry about
++// setting up globals for 68K plugins.
++NPError Private_Initialize(void);
++void    Private_Shutdown(void);
++NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved);
++NPError Private_Destroy(NPP instance, NPSavedData** save);
++NPError Private_SetWindow(NPP instance, NPWindow* window);
++NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype);
++NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason);
++int32   Private_WriteReady(NPP instance, NPStream* stream);
++int32   Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer);
++void    Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
++void    Private_Print(NPP instance, NPPrint* platformPrint);
++int16   Private_HandleEvent(NPP instance, void* event);
++void    Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData);
++jref    Private_GetJavaClass(void);
++NPError Private_GetValue(NPP instance, NPPVariable variable, void *result);
++NPError Private_SetValue(NPP instance, NPNVariable variable, void *value);
++
++#endif //XP_MAC
++
++#ifndef HIBYTE
++#define HIBYTE(i) (i >> 8)
++#endif
++
++#ifndef LOBYTE
++#define LOBYTE(i) (i & 0xff)
++#endif
++
++#endif //_NPPLAT_H_
diff --cc contrib/packagekit-plugin/sdk/pluginbase.h
index 0000000,0000000..b7e3327
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/sdk/pluginbase.h
@@@ -1,0 -1,0 +1,96 @@@
++/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is mozilla.org code.
++ *
++ * The Initial Developer of the Original Code is
++ * Netscape Communications Corporation.
++ * Portions created by the Initial Developer are Copyright (C) 1998
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++#ifndef __PLUGININSTANCEBASE_H__
++#define __PLUGININSTANCEBASE_H__
++
++#include "npplat.h"
++
++struct nsPluginCreateData
++{
++  NPP instance;
++  NPMIMEType type; 
++  uint16 mode; 
++  int16 argc; 
++  char** argn; 
++  char** argv; 
++  NPSavedData* saved;
++};
++
++class nsPluginInstanceBase
++{
++public:
++  // these three methods must be implemented in the derived
++  // class platform specific way
++  virtual NPBool init(NPWindow* aWindow) = 0;
++  virtual void shut() = 0;
++  virtual NPBool isInitialized() = 0;
++
++  // implement all or part of those methods in the derived 
++  // class as needed
++  virtual NPError SetWindow(NPWindow* pNPWindow)                    { return NPERR_NO_ERROR; }
++  virtual NPError NewStream(NPMIMEType type, NPStream* stream, 
++                            NPBool seekable, uint16* stype)         { return NPERR_NO_ERROR; }
++  virtual NPError DestroyStream(NPStream *stream, NPError reason)   { return NPERR_NO_ERROR; }
++  virtual void    StreamAsFile(NPStream* stream, const char* fname) { return; }
++  virtual int32   WriteReady(NPStream *stream)                      { return 0x0fffffff; }
++  virtual int32   Write(NPStream *stream, int32 offset, 
++                        int32 len, void *buffer)                    { return len; }
++  virtual void    Print(NPPrint* printInfo)                         { return; }
++  virtual uint16  HandleEvent(void* event)                          { return 0; }
++  virtual void    URLNotify(const char* url, NPReason reason, 
++                            void* notifyData)                       { return; }
++  virtual NPError GetValue(NPPVariable variable, void *value)       { return NPERR_NO_ERROR; }
++  virtual NPError SetValue(NPNVariable variable, void *value)       { return NPERR_NO_ERROR; }
++};
++
++// functions that should be implemented for each specific plugin
++
++// creation and destruction of the object of the derived class
++nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct);
++void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin);
++
++// global plugin initialization and shutdown
++NPError NS_PluginInitialize();
++void NS_PluginShutdown();
++
++#ifdef XP_UNIX
++// global to get plugins name & description 
++NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue);
++#endif
++
++#endif // __PLUGININSTANCEBASE_H__
diff --cc contrib/packagekit-plugin/src/contents.cpp
index 0000000,0000000..313faaa
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/src/contents.cpp
@@@ -1,0 -1,0 +1,718 @@@
++/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is packagekit-plugin code.
++ *
++ * The Initial Developer of the Original Code is
++ * Red Hat, Inc.
++ * Portions created by the Initial Developer are Copyright (C) 2008
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++#define MOZ_X11
++
++#include <config.h>
++
++#include <string.h>
++
++#include <glib/gi18n.h>
++
++#include <cairo-xlib.h>
++#include <dlfcn.h>
++#include <pango/pangocairo.h>
++#include <packagekit/pk-package-id.h>
++#include <gtk/gtk.h>
++#include <gdk/gdkx.h>
++#include <gio/gdesktopappinfo.h>
++
++#include "plugin.h"
++
++#define MARGIN 5
++
++////////////////////////////////////////
++//
++// PkpContents class implementation
++//
++
++static std::vector<std::string>
++splitString(const char *str)
++{
++    std::vector<std::string> v;
++
++    if (str) {
++        char **split = g_strsplit(str, " ", -1);
++        for (char **s = split; *s; s++) {
++            char *stripped = strdup(*s);
++            g_strstrip(stripped);
++            v.push_back(stripped);
++            g_free(stripped);
++        }
++
++        g_strfreev(split);
++    }
++
++    return v;
++}
++
++PkpContents::PkpContents(const char *displayName,
++                         const char *packageNames,
++                         const char *desktopNames) :
++    mStatus(IN_PROGRESS),
++    mPlugin(0),
++    mDisplayName(displayName),
++    mPackageNames(splitString(packageNames)),
++    mDesktopNames(splitString(desktopNames)),
++    mAppInfo(0),
++    mLayout(0),
++    mInstallPackageProxy(0),
++    mInstallPackageCall(0)
++{
++    recheck();
++}
++
++PkpContents::~PkpContents()
++{
++    clearLayout();
++
++    if (mAppInfo != 0) {
++        g_object_unref(mAppInfo);
++        mAppInfo = 0;
++    }
++
++    if (mInstallPackageCall != 0) {
++        dbus_g_proxy_cancel_call(mInstallPackageProxy, mInstallPackageCall);
++        g_object_unref(mInstallPackageProxy);
++        mInstallPackageProxy = 0;
++        mInstallPackageCall = 0;
++    }
++
++    while (!mClients.empty())
++        removeClient(mClients.front());
++}
++
++void PkpContents::recheck()
++{
++    mStatus = IN_PROGRESS;
++    mAvailableVersion = "";
++    mAvailablePackageName = "";
++    
++    for (std::vector<std::string>::iterator i = mPackageNames.begin(); i != mPackageNames.end(); i++) {
++        GError *error = NULL;
++        PkClient *client = pk_client_new();
++        if (!pk_client_resolve(client, PK_FILTER_ENUM_NONE, i->c_str(), &error)) {
++            g_warning("%s", error->message);
++            g_clear_error(&error);
++            g_object_unref(client);
++        } else {
++            g_signal_connect(client, "package", G_CALLBACK(onClientPackage), this);
++            g_signal_connect(client, "error-code", G_CALLBACK(onClientErrorCode), this);
++            g_signal_connect(client, "finished", G_CALLBACK(onClientFinished), this);
++            mClients.push_back(client);
++        }
++    }
++
++    findAppInfo();
++
++    if (mClients.empty() && getStatus() == IN_PROGRESS)
++        setStatus(UNAVAILABLE);
++}
++
++void PkpContents::removeClient(PkClient *client)
++{
++    for (std::vector<PkClient *>::iterator i = mClients.begin(); i != mClients.end(); i++) {
++        if (*i == client) {
++            mClients.erase(i);
++            g_signal_handlers_disconnect_by_func(client, (void *)onClientPackage, this);
++            g_signal_handlers_disconnect_by_func(client, (void *)onClientErrorCode, this);
++            g_signal_handlers_disconnect_by_func(client, (void *)onClientFinished, this);
++            g_object_unref(client);
++            break;
++        }
++    }
++
++    if (mClients.empty()) {
++        if (getStatus() == IN_PROGRESS)
++            setStatus(UNAVAILABLE);
++    }
++}
++
++void
++PkpContents::setStatus(PackageStatus status)
++{
++    if (mStatus != status) {
++        mStatus = status;
++        clearLayout();
++        refresh();
++    }
++}
++
++void
++PkpContents::setAvailableVersion(const char *version)
++{
++    mAvailableVersion = version;
++    clearLayout();
++    refresh();
++}
++
++void
++PkpContents::setAvailablePackageName(const char *name)
++{
++    mAvailablePackageName = name;
++}
++
++void
++PkpContents::setInstalledVersion(const char *version)
++{
++    mInstalledVersion = version;
++    clearLayout();
++    refresh();
++}
++
++void
++PkpContents::clearLayout()
++{
++    if (mLayout) {
++        g_object_unref(mLayout);
++        mLayout = 0;
++    }
++}
++
++static void
++append_markup(GString *str, const char *format, ...)
++{
++    va_list vap;
++    
++    va_start(vap, format);
++    char *tmp = g_markup_vprintf_escaped(format, vap);
++    va_end(vap);
++
++    g_string_append(str, tmp);
++    g_free(tmp);
++}
++
++static guint32
++rgba_from_gdk_color(GdkColor *color)
++{
++    return (((color->red   >> 8) << 24) |
++            ((color->green >> 8) << 16) |
++            ((color->blue  >> 8) << 8) |
++            0xff);
++}
++
++static void
++set_source_from_rgba(cairo_t *cr,
++                     guint32  rgba)
++{
++    cairo_set_source_rgba(cr,
++                          ((rgba & 0xff000000) >> 24) / 255.,
++                          ((rgba & 0x00ff0000) >> 16) / 255.,
++                          ((rgba & 0x0000ff00) >> 8) / 255.,
++                          (rgba & 0x000000ff) / 255.);
++                          
++}
++
++/* Retrieve the system colors and fonts.
++ * This looks incredibly expensive .... to create a GtkWindow for
++ * every expose ... but actually it's only moderately expensive;
++ * Creating a GtkWindow is just normal GObject creation overhead --
++ * the extra expense beyond that will come when we actually create
++ * the window.
++ */
++static void
++get_style(PangoFontDescription **font_desc,
++          guint32               *foreground,
++          guint32               *background,
++          guint32               *link)
++{
++    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
++
++    gtk_widget_ensure_style(window);
++
++    *foreground = rgba_from_gdk_color(&window->style->text[GTK_STATE_NORMAL]);
++    *background = rgba_from_gdk_color(&window->style->base[GTK_STATE_NORMAL]);
++
++    GdkColor link_color = { 0, 0, 0, 0xeeee };
++    GdkColor *tmp = NULL;
++
++    gtk_widget_style_get (GTK_WIDGET (window),
++                          "link-color", &tmp, NULL);
++    if (tmp != NULL) {
++        link_color = *tmp;
++        gdk_color_free(tmp);
++    }
++
++    *link = rgba_from_gdk_color(&link_color);
++
++    *font_desc = pango_font_description_copy(window->style->font_desc);
++   
++    gtk_widget_destroy(window);
++}
++
++void
++PkpContents::ensureLayout(cairo_t              *cr,
++                          PangoFontDescription *font_desc,
++                          guint32               link_color)
++{
++    GString *markup = g_string_new(NULL);
++    
++    if (mLayout)
++        return;
++    
++    mLayout = pango_cairo_create_layout(cr);
++    pango_layout_set_font_description(mLayout, font_desc);
++
++    /* WARNING: Any changes to what links are created here will require corresponding
++     * changes to the buttonRelease() method
++     */
++    switch (mStatus) {
++    case IN_PROGRESS:
++        append_markup(markup, _("Getting package information..."));
++        break;
++    case INSTALLED:
++        if (mAppInfo != 0)
++            append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Run %s</span>"),
++                          link_color >> 8,
++                          mDisplayName.c_str());
++        else
++            append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
++        if (!mInstalledVersion.empty())
++            append_markup(markup, _("\n<small>Installed version: %s</small>"), mInstalledVersion.c_str());
++        break;
++    case UPGRADABLE:
++        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
++        if (mAppInfo != 0) {
++            if (!mInstalledVersion.empty())
++                append_markup(markup, _("\n<span color='#%06x' underline='single'>Run version %s now</span>"),
++                              link_color >> 8,
++                              mInstalledVersion.c_str());
++            else
++                append_markup(markup,
++                              _("\n<span color='#%06x' underline='single'>Run now</span>"),
++                              link_color >> 8);
++        }
++        
++        append_markup(markup, _("\n<span color='#%06x' underline='single'>Upgrade to version %s</span>"),
++                      link_color >> 8,
++                      mAvailableVersion.c_str());
++        break;
++    case AVAILABLE:
++        append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Install %s Now</span>"),
++                      link_color >> 8,
++                      mDisplayName.c_str());
++        append_markup(markup, _("\n<small>Version: %s</small>"), mAvailableVersion.c_str());
++        break;
++    case UNAVAILABLE:
++        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
++        append_markup(markup, _("\n<small>No packages found for your system</small>"));
++        break;
++    case INSTALLING:
++        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
++        append_markup(markup, _("\n<small>Installing...</small>"));
++        break;
++    }
++
++    pango_layout_set_markup(mLayout, markup->str, -1);
++    g_string_free(markup, TRUE);
++}
++
++void
++PkpContents::refresh()
++{
++    if (mPlugin != 0)
++        mPlugin->refresh();
++}
++                               
++void
++PkpContents::setPlugin(PkpPluginInstance *plugin)
++{
++    mPlugin = plugin;
++}
++
++void
++PkpContents::draw(cairo_t *cr)
++{
++    guint32 foreground, background, link;
++    PangoFontDescription *font_desc;
++    
++    get_style(&font_desc, &foreground, &background, &link);
++
++    set_source_from_rgba(cr, background);
++    cairo_rectangle(cr, mPlugin->getX(), mPlugin->getY(), mPlugin->getWidth(), mPlugin->getHeight());
++    cairo_fill(cr);
++
++    cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
++    cairo_rectangle(cr, mPlugin->getX() + 0.5, mPlugin->getY() + 0.5, mPlugin->getWidth() - 1, mPlugin->getHeight() - 1);
++    cairo_set_line_width(cr, 1);
++    cairo_stroke(cr);
++
++    ensureLayout(cr, font_desc, link);
++    int width, height;
++    pango_layout_get_pixel_size(mLayout, &width, &height);
++
++    cairo_move_to(cr, mPlugin->getX() + MARGIN, mPlugin->getY() + MARGIN);
++    set_source_from_rgba(cr, foreground);
++    pango_cairo_show_layout(cr, mLayout);
++}
++
++/* Cut and paste from pango-layout.c; determines if a layout iter is on 
++ * a line terminated by a real line break (rather than a line break from
++ * wrapping). We use this to determine whether the empty run at the end
++ * of a display line should be counted as a break between links or not.
++ *
++ * (Code in pango-layout.c is by me, Copyright Red Hat, and hereby relicensed
++ * to the license of this file)
++ */
++static gboolean
++line_is_terminated (PangoLayoutIter *iter)
++{
++    /* There is a real terminator at the end of each paragraph other
++     * than the last.
++     */
++    PangoLayoutLine *line = pango_layout_iter_get_line(iter);
++    GSList *lines = pango_layout_get_lines(pango_layout_iter_get_layout(iter));
++    GSList *link = g_slist_find(lines, line);
++    if (!link) {
++        g_warning("Can't find line in layout line list\n");
++        return FALSE;
++    }
++
++    if (link->next) {
++        PangoLayoutLine *next_line = (PangoLayoutLine *)link->next->data;
++        if (next_line->is_paragraph_start)
++            return TRUE;
++    }
++
++    return FALSE;
++}
++
++/* This function takes an X,Y position and determines whether it is over one
++ * of the underlined portions of the layout (a link). It works by iterating
++ * through the runs of the layout (a run is a segment with a consistent
++ * font and display attributes, more or less), and counting the underlined
++ * segments that we see. A segment that is underlined could be broken up
++ * into multiple runs if it is drawn with multiple fonts due to fonts
++ * substitution, so we actually count non-underlined => underlined
++ * transitions.
++ */
++int
++PkpContents::getLinkIndex(int x, int y)
++{
++    /* Coordinates are relative to origin of plugin (different from drawing) */
++    
++    if (!mLayout)
++        return -1;
++
++    x -= MARGIN;
++    y -= MARGIN;
++
++    int index;
++    int trailing;
++    if (!pango_layout_xy_to_index(mLayout,
++                                  x * PANGO_SCALE, y * PANGO_SCALE,
++                                  &index, &trailing))
++        return - 1;
++
++    PangoLayoutIter *iter = pango_layout_get_iter(mLayout);
++    int seen_links = 0;
++    bool in_link = false;
++    int result = -1;
++    
++    while (TRUE) {
++        PangoLayoutRun *run = pango_layout_iter_get_run(iter);
++        if (run) {
++            PangoItem *item = run->item;
++            PangoUnderline uline = PANGO_UNDERLINE_NONE;
++            
++            for (GSList *l = item->analysis.extra_attrs; l; l = l->next) {
++                PangoAttribute *attr = (PangoAttribute *)l->data;
++                if (attr->klass->type == PANGO_ATTR_UNDERLINE) {
++                    uline = (PangoUnderline)((PangoAttrInt *)attr)->value;
++                }
++            }
++
++            if (uline == PANGO_UNDERLINE_NONE)
++                in_link = FALSE;
++            else if (!in_link) {
++                in_link = TRUE;
++                seen_links++;
++            }
++
++            if (item->offset <= index && index < item->offset + item->length) {
++                if (in_link)
++                    result = seen_links - 1;
++
++                break;
++            }
++        } else {
++            /* We have an empty run at the end of each line. A line break doesn't
++             * terminate the link, but a real newline does.
++             */
++            if (line_is_terminated(iter))
++                in_link = FALSE;
++        }
++        
++        if (!pango_layout_iter_next_run (iter))
++            break;
++    }
++    
++    pango_layout_iter_free(iter);
++
++    return result;
++}
++
++void
++PkpContents::buttonPress(int x, int y, Time time)
++{
++}
++
++void
++PkpContents::buttonRelease(int x, int y, Time time)
++{
++    int index = getLinkIndex(x, y);
++    if (index < 0)
++        return;
++    
++    switch (mStatus) {
++    case IN_PROGRESS:
++    case INSTALLING:
++    case UNAVAILABLE:
++        break;
++    case INSTALLED:
++        if (mAppInfo != 0)
++            runApplication(time);
++        break;
++    case UPGRADABLE:
++        if (mAppInfo != 0 && index == 0)
++            runApplication(time);
++        else {
++            installPackage(time);
++        }
++        break;
++    case AVAILABLE:
++        if (!mAvailablePackageName.empty())
++            installPackage(time);
++        break;
++    }
++}
++
++void
++PkpContents::motion(int x, int y)
++{
++}
++
++void
++PkpContents::enter(int x, int y)
++{
++}
++
++void
++PkpContents::leave(int x, int y)
++{
++}
++
++static guint32
++get_server_timestamp()
++{
++    GtkWidget *invisible = gtk_invisible_new();
++    gtk_widget_realize(invisible);
++    return gdk_x11_get_server_time(invisible->window);
++    gtk_widget_destroy(invisible);
++}
++
++static gboolean
++validate_name(const char *name)
++{
++    const char *p;
++    
++    for (p = name; *p; p++) {
++        char c = *p;
++        
++        if (!((c >= 'A' && c <= 'Z') ||
++              (c >= 'a' && c <= 'z') ||
++              (c >= '0' && c <= '9') ||
++              (c == '.') ||
++              (c == '_') ||
++              (c == '-')))
++            return FALSE;
++    }
++
++    return TRUE;
++}
++
++void
++PkpContents::findAppInfo()
++{
++    for (std::vector<std::string>::iterator i = mDesktopNames.begin(); i != mDesktopNames.end(); i++) {
++        if (!validate_name(i->c_str())) {
++            g_warning("Bad desktop name: '%s'", i->c_str());
++            continue;
++        }
++
++        /* The "id" taken be g_desktop_app_info_new() is weirdly 'foo.desktop' not 'foo' */
++        char *id = g_strconcat(i->c_str(), ".desktop", NULL);
++        GDesktopAppInfo *desktopAppInfo = g_desktop_app_info_new(id);
++        g_free(id);
++        
++        if (desktopAppInfo) {
++            mAppInfo = G_APP_INFO(desktopAppInfo);
++            break;
++        }
++    }
++
++    if (mAppInfo != 0)
++        setStatus(INSTALLED);
++}
++                  
++void
++PkpContents::runApplication (Time time)
++{
++    GError *error = NULL;
++    
++    if (mAppInfo == 0) {
++        g_warning("Didn't find application to launch");
++        return;
++    }
++
++    GAppLaunchContext *context;
++#ifdef HAVE_GDK_APP_LAUNCH_CONTEXT_NEW
++    context = gdk_app_launch_context_new();
++    gdk_app_launch_context_set_timestamp(time);
++#endif    
++
++    if (!g_app_info_launch(mAppInfo, NULL, context, &error)) {
++        g_warning("%s\n", error->message);
++        g_clear_error(&error);
++        return;
++    }
++
++    if (context != 0)
++        g_object_unref(context);
++}
++
++void
++PkpContents::installPackage (Time time)
++{
++    if (mAvailablePackageName.empty()) {
++        g_warning("No available package to install");
++        return;
++    }
++
++    if (mInstallPackageCall != 0) {
++        g_warning("Already installing package");
++        return;
++    }
++
++    /* Get a proxy to the *session* PackageKit service */
++    DBusGConnection *connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
++    mInstallPackageProxy = dbus_g_proxy_new_for_name(connection,
++                                                     "org.freedesktop.PackageKit",
++                                                     "/org/freedesktop/PackageKit",
++                                                     "org.freedesktop.PackageKit");
++
++    mInstallPackageCall = dbus_g_proxy_begin_call_with_timeout(mInstallPackageProxy,
++                                                               "InstallPackageName",
++                                                               onInstallPackageFinished,
++                                                               this,
++                                                               (GDestroyNotify)0,
++                                                               24 * 60 * 1000 * 1000, /* one day */
++                                                               G_TYPE_STRING, mAvailablePackageName.c_str(),
++                                                               G_TYPE_INVALID,
++                                                               G_TYPE_INVALID);
++    
++     setStatus(INSTALLING);
++}
++
++void
++PkpContents::onClientPackage(PkClient	  *client,
++                                  PkInfoEnum	   info,
++                                  const gchar	   *package_id,
++                                  const gchar	   *summary,
++                                  PkpContents *contents)
++{
++    PkPackageId *id = pk_package_id_new_from_string(package_id);
++    
++    if (info == PK_INFO_ENUM_AVAILABLE) {
++        if (contents->getStatus() == IN_PROGRESS)
++            contents->setStatus(AVAILABLE);
++        else if (contents->getStatus() == INSTALLED)
++            contents->setStatus(UPGRADABLE);
++        contents->setAvailableVersion(id->version);
++        contents->setAvailablePackageName(id->name);
++    } else if (info == PK_INFO_ENUM_INSTALLED) {
++        if (contents->getStatus() == IN_PROGRESS)
++            contents->setStatus(INSTALLED);
++        else if (contents->getStatus() == AVAILABLE)
++            contents->setStatus(UPGRADABLE);
++        contents->setInstalledVersion(id->version);
++    }
++    
++    pk_package_id_free(id);
++}
++
++void
++PkpContents::onClientErrorCode(PkClient	       *client,
++                               PkErrorCodeEnum  code,
++                               const gchar     *details,
++                               PkpContents     *contents)
++{
++    g_warning("Error getting data from PackageKit: %s\n", details);
++    contents->removeClient(client);
++}
++
++void
++PkpContents::onClientFinished(PkClient	    *client,
++                              PkExitEnum     exit,
++                              guint	     runtime,
++                              PkpContents   *contents)
++    
++{
++    contents->removeClient(client);
++}
++    
++void
++PkpContents::onInstallPackageFinished (DBusGProxy       *proxy,
++                                       DBusGProxyCall   *call,
++                                       void             *user_data)
++{
++    PkpContents *contents = (PkpContents *)user_data;
++    
++    GError *error = NULL;
++    if (!dbus_g_proxy_end_call(proxy, call, &error, 
++                               G_TYPE_INVALID)) {
++        g_warning("Error occurred during install: %s", error->message);
++        g_clear_error(&error);
++        
++    }
++
++    g_object_unref(contents->mInstallPackageProxy);
++    contents->mInstallPackageProxy = 0;
++    contents->mInstallPackageCall = 0;
++    
++    contents->recheck();
++}
diff --cc contrib/packagekit-plugin/src/contents.h
index 0000000,0000000..797187d
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/src/contents.h
@@@ -1,0 -1,0 +1,138 @@@
++/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is packagekit-plugin code.
++ *
++ * The Initial Developer of the Original Code is
++ * Red Hat, Inc.
++ * Portions created by the Initial Developer are Copyright (C) 2008
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++
++#ifndef __CONTENTS_H__
++#define __CONTENTS_H__
++
++#include <X11/Xlib.h>
++#include <gio/gio.h>
++#include <pango/pango.h>
++#include <packagekit/pk-client.h>
++#include <cairo.h>
++#include <dbus/dbus-glib.h>
++#include <gtk/gtk.h>
++
++#include <string>
++#include <vector>
++
++class PkpPluginInstance;
++
++enum PackageStatus {
++    IN_PROGRESS, /* Looking up package information */
++    INSTALLED,   /* Package installed */
++    UPGRADABLE,  /* Package installed, newer version available */
++    AVAILABLE,   /* Package not installed, version available */
++    UNAVAILABLE, /* Package not installed or available */
++    INSTALLING   /* Currently installing a new version */
++};
++
++class PkpContents
++{
++public:
++    PkpContents(const char *displayName, const char *packageNames, const char *desktopNames);
++    virtual ~PkpContents();
++
++    void setPlugin(PkpPluginInstance *plugin);
++    
++    void draw(cairo_t *cr);
++    void buttonPress(int x, int y, Time time);
++    void buttonRelease(int x, int y, Time time);
++    void motion(int x, int y);
++    void enter(int x, int y);
++    void leave(int x, int y);
++
++private:
++    void recheck();
++    void findAppInfo();
++    void runApplication(Time time);
++    void installPackage(Time time);
++
++    int getLinkIndex(int x, int y);
++    
++    void setStatus(PackageStatus status);
++    PackageStatus getStatus() { return mStatus; }
++    void setAvailableVersion(const char *version);
++    void setAvailablePackageName(const char *name);
++    void setInstalledVersion(const char *version);
++
++    void ensureLayout(cairo_t *cr,
++                      PangoFontDescription *font_desc,
++                      guint32 link_color);
++    void clearLayout();
++    void refresh();
++    
++    void removeClient(PkClient *client);
++    
++    static void onClientPackage(PkClient 	   *client,
++                                PkInfoEnum	    info,
++                                const gchar	   *package_id,
++                                const gchar	   *summary,
++                                PkpContents        *contents);
++    static void onClientErrorCode(PkClient	   *client,
++                                  PkErrorCodeEnum  code,
++                                  const gchar	   *details,
++                                  PkpContents *contents);
++    static void onClientFinished(PkClient	  *client,
++                                 PkExitEnum	   exit,
++                                 guint		   runtime,
++                                 PkpContents      *contents);
++    
++    static void onInstallPackageFinished(DBusGProxy     *proxy,
++                                         DBusGProxyCall *call,
++                                         void           *user_data);
++
++    PkpPluginInstance *mPlugin;
++    PackageStatus mStatus;
++    std::string mAvailableVersion;
++    std::string mAvailablePackageName;
++    std::string mInstalledVersion;
++    GAppInfo *mAppInfo;
++
++    std::string mDisplayName;
++    std::vector<std::string> mPackageNames;
++    std::vector<std::string> mDesktopNames;
++    
++    PangoLayout *mLayout;
++
++    std::vector<PkClient *> mClients;
++
++    DBusGProxy *mInstallPackageProxy;
++    DBusGProxyCall *mInstallPackageCall;
++};
++
++#endif // __CONTENTS_H__
diff --cc contrib/packagekit-plugin/src/plugin.cpp
index 0000000,0000000..345b427
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/src/plugin.cpp
@@@ -1,0 -1,0 +1,317 @@@
++/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is mozilla.org code.
++ *
++ * The Initial Developer of the Original Code is
++ * Netscape Communications Corporation.
++ * Portions created by the Initial Developer are Copyright (C) 1998
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Red Hat, Inc.
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++#define MOZ_X11
++
++#include <config.h>
++
++#include <string.h>
++
++#include <glib/gi18n.h>
++
++#include <cairo-xlib.h>
++#include <dlfcn.h>
++#include <packagekit/pk-package-id.h>
++
++#include "plugin.h"
++
++#define MIME_TYPES_HANDLED  "application/x-packagekit-plugin"
++#define PLUGIN_NAME         "Plugin for Installing Applications"
++#define MIME_TYPES_DESCRIPTION  MIME_TYPES_HANDLED":bsc:"PLUGIN_NAME
++#define PLUGIN_DESCRIPTION  PLUGIN_NAME
++
++char* NPP_GetMIMEDescription(void)
++{
++    return (char *)(MIME_TYPES_DESCRIPTION);
++}
++
++static void *module_handle = 0;
++
++/////////////////////////////////////
++// general initialization and shutdown
++//
++
++/* If our dependent libraries like libpackagekit get unloaded, bad stuff
++ * happens (they may have registered GLib types and so forth) so we need
++ * to keep them around. The (GNU extension) RTLD_NODELETE seems useful
++ * but isn't so much, since it only refers to a specific library and not
++ * its dependent libraries, so we'd have to identify specifically each
++ * of our dependencies that is not safe to unload and that is most of
++ * the GTK+ stack.
++ */
++static void
++make_module_resident()
++{
++    Dl_info info;
++
++    /* Get the (absolute) filename of this module */
++    if (!dladdr((void *)NPP_GetMIMEDescription, &info)) {
++        g_warning("Can't find filename for module");
++        return;
++    }
++
++    /* Now reopen it to get our own handle */
++    module_handle = dlopen(info.dli_fname, RTLD_NOW);
++    if (!module_handle) {
++        g_warning("Can't permanently open module %s", dlerror());
++        return;
++    }
++
++    /* the module will never be closed */
++}
++
++NPError NS_PluginInitialize()
++{
++    if (module_handle != 0) /* Already initialized */
++        return NPERR_NO_ERROR;
++    
++    make_module_resident();
++    
++#ifdef ENABLE_NLS
++    bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
++    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
++#endif
++    
++    return NPERR_NO_ERROR;
++}
++
++void NS_PluginShutdown()
++{
++}
++
++// get values per plugin
++NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue)
++{
++    NPError err = NPERR_NO_ERROR;
++    switch (aVariable) {
++    case NPPVpluginNameString:
++        *((char **)aValue) = (char *)PLUGIN_NAME;
++        break;
++    case NPPVpluginDescriptionString:
++        *((char **)aValue) = (char *)PLUGIN_DESCRIPTION;
++        break;
++    default:
++        err = NPERR_INVALID_PARAM;
++        break;
++    }
++    return err;
++}
++
++/////////////////////////////////////////////////////////////
++//
++// construction and destruction of our plugin instance object
++//
++nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)
++{
++    const char *displayName = "";
++    const char *packageNames = NULL;
++    const char *desktopNames = NULL;
++    
++    if(!aCreateDataStruct)
++        return NULL;
++
++    for (int i = 0; i < aCreateDataStruct->argc; i++) {
++        if (strcmp(aCreateDataStruct->argn[i], "displayname") == 0)
++            displayName = aCreateDataStruct->argv[i];
++        else if (strcmp(aCreateDataStruct->argn[i], "packagenames") == 0)
++            packageNames = aCreateDataStruct->argv[i];
++        else if (strcmp(aCreateDataStruct->argn[i], "desktopnames") == 0)
++            desktopNames = aCreateDataStruct->argv[i];
++    }
++      
++    PkpPluginInstance * plugin = new PkpPluginInstance(aCreateDataStruct->instance, displayName, packageNames, desktopNames);
++  
++    NPN_SetValue(aCreateDataStruct->instance,
++                 NPPVpluginWindowBool, (void *)FALSE);
++  
++    return plugin;
++}
++
++void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)
++{
++    if(aPlugin)
++        delete (PkpPluginInstance *)aPlugin;
++}
++
++////////////////////////////////////////
++//
++// nsPluginInstance class implementation
++//
++
++PkpPluginInstance::PkpPluginInstance(NPP         aInstance,
++                                     const char *displayName,
++                                     const char *packageNames,
++                                     const char *desktopNames) :
++    nsPluginInstanceBase(),
++    mInstance(aInstance),
++    mInitialized(FALSE),
++    mContents(displayName, packageNames, desktopNames),
++    mWindow(0)
++{
++    mContents.setPlugin(this);
++}
++
++PkpPluginInstance::~PkpPluginInstance()
++{
++}
++
++NPBool PkpPluginInstance::init(NPWindow* aWindow)
++{
++    if(aWindow == NULL)
++        return FALSE;
++  
++    if (SetWindow(aWindow))
++        mInitialized = TRUE;
++	
++    return mInitialized;
++}
++
++void PkpPluginInstance::shut()
++{
++    mInitialized = FALSE;
++}
++
++NPError PkpPluginInstance::GetValue(NPPVariable aVariable, void *aValue)
++{
++    NPError err = NPERR_NO_ERROR;
++    switch (aVariable) {
++    case NPPVpluginNameString:
++    case NPPVpluginDescriptionString:
++        return NS_PluginGetValue(aVariable, aValue) ;
++        break;
++    default:
++        err = NPERR_INVALID_PARAM;
++        break;
++    }
++    return err;
++
++}
++
++NPError PkpPluginInstance::SetWindow(NPWindow* aWindow)
++{
++    if (aWindow == NULL)
++        return FALSE;
++
++    mX = aWindow->x;
++    mY = aWindow->y;
++    mWidth = aWindow->width;
++    mHeight = aWindow->height;
++  
++    mWindow = (Window) aWindow->window;
++    NPSetWindowCallbackStruct *ws_info = (NPSetWindowCallbackStruct *)aWindow->ws_info;
++    mDisplay = ws_info->display;
++    mVisual = ws_info->visual;
++    mDepth = ws_info->depth;
++    mColormap = ws_info->colormap;
++  
++    return NPERR_NO_ERROR;
++}
++
++void
++PkpPluginInstance::refresh()
++{
++    NPRect rect;
++
++    /* Coordinates here are relative to the plugin's origin (mX,mY) */
++    
++    rect.left = 0;
++    rect.right =  mWidth;
++    rect.top = 0;
++    rect.bottom = mHeight;
++    
++    NPN_InvalidateRect(mInstance, &rect);
++}
++                               
++uint16
++PkpPluginInstance::HandleEvent(void *event)
++{
++    XEvent *xev = (XEvent *)event;
++
++    switch (xev->xany.type) {
++    case GraphicsExpose:
++        {
++            XGraphicsExposeEvent *xge = (XGraphicsExposeEvent *)event;
++
++            cairo_surface_t *surface = cairo_xlib_surface_create (mDisplay, xge->drawable, mVisual, mX + mWidth, mY + mHeight);
++            cairo_t *cr = cairo_create(surface);
++
++            cairo_rectangle(cr, xge->x, xge->y, xge->width, xge->height);
++            cairo_clip(cr);
++
++            mContents.draw(cr);
++
++            cairo_destroy(cr);
++            cairo_surface_destroy(surface);
++            
++            return 1;
++        }
++    case ButtonPress:
++        {
++            XButtonEvent *xbe = (XButtonEvent *)event;
++            mContents.buttonPress(xbe->x, xbe->y, xbe->time);
++            return 1;
++        }
++    case ButtonRelease:
++        {
++            XButtonEvent *xbe = (XButtonEvent *)event;
++            mContents.buttonRelease(xbe->x, xbe->y, xbe->time);
++            return 1;
++        }
++    case MotionNotify:
++        {
++            XMotionEvent *xme = (XMotionEvent *)event;
++            mContents.motion(xme->x, xme->y);
++            return 1;
++        }
++    case EnterNotify:
++        {
++            XCrossingEvent *xce = (XCrossingEvent *)event;
++            mContents.enter(xce->x, xce->y);
++            return 1;
++        }
++    case LeaveNotify:
++        {
++            XCrossingEvent *xce = (XCrossingEvent *)event;
++            mContents.leave(xce->x, xce->y);
++            return 1;
++        }
++    }
++
++    return 0;
++}
diff --cc contrib/packagekit-plugin/src/plugin.h
index 0000000,0000000..9d3af87
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/src/plugin.h
@@@ -1,0 -1,0 +1,81 @@@
++/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
++/* ***** BEGIN LICENSE BLOCK *****
++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
++ *
++ * The contents of this file are subject to the Mozilla Public License Version
++ * 1.1 (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ * http://www.mozilla.org/MPL/
++ *
++ * Software distributed under the License is distributed on an "AS IS" basis,
++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
++ * for the specific language governing rights and limitations under the
++ * License.
++ *
++ * The Original Code is mozilla.org code.
++ *
++ * The Initial Developer of the Original Code is
++ * Netscape Communications Corporation.
++ * Portions created by the Initial Developer are Copyright (C) 1998
++ * the Initial Developer. All Rights Reserved.
++ *
++ * Contributor(s):
++ *
++ * Alternatively, the contents of this file may be used under the terms of
++ * either the GNU General Public License Version 2 or later (the "GPL"), or
++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
++ * in which case the provisions of the GPL or the LGPL are applicable instead
++ * of those above. If you wish to allow use of your version of this file only
++ * under the terms of either the GPL or the LGPL, and not to allow others to
++ * use your version of this file under the terms of the MPL, indicate your
++ * decision by deleting the provisions above and replace them with the notice
++ * and other provisions required by the GPL or the LGPL. If you do not delete
++ * the provisions above, a recipient may use your version of this file under
++ * the terms of any one of the MPL, the GPL or the LGPL.
++ *
++ * ***** END LICENSE BLOCK ***** */
++
++#ifndef __PLUGIN_H__
++#define __PLUGIN_H__
++
++#include <X11/Xlib.h>
++
++#include "contents.h"
++#include "pluginbase.h"
++
++class PkpPluginInstance : public nsPluginInstanceBase
++{
++public:
++    PkpPluginInstance(NPP aInstance, const char *displayName, const char *packageNames, const char *desktopNames);
++    virtual ~PkpPluginInstance();
++
++    NPBool init(NPWindow* aWindow);
++    void shut();
++    NPBool isInitialized() {return mInitialized;}
++    NPError GetValue(NPPVariable variable, void *value);
++    NPError SetWindow(NPWindow* aWindow);
++    uint16 HandleEvent(void *event);
++
++    void refresh();
++
++    int getX() { return mX; }
++    int getY() { return mY; }
++    int getWidth() { return mWidth; }
++    int getHeight() { return mHeight; }
++    
++private:
++    NPP mInstance;
++    NPBool mInitialized;
++
++    PkpContents mContents;
++
++    Window mWindow;
++    Display *mDisplay;
++    int mX, mY;
++    int mWidth, mHeight;
++    Visual* mVisual;
++    Colormap mColormap;
++    unsigned int mDepth;
++};
++
++#endif // __PLUGIN_H__
diff --cc contrib/packagekit-plugin/tests/test.html
index 0000000,0000000..7afb043
new file mode 100644
--- /dev/null
+++ b/contrib/packagekit-plugin/tests/test.html
@@@ -1,0 -1,0 +1,31 @@@
++<html>
++  <head>
++    <title>Test for PackageKit Plugin</title>
++    <style type="text/css">
++      body {
++        background: #ffeedd;
++      }
++      
++      .packagekit-plugin {
++         width: 30em;
++         height: 5em;
++      }
++    </style>
++  </head>
++  <body>
++    <object type="application/x-packagekit-plugin" width="500" height="200" class="packagekit-plugin">
++<!--      <param name="packagenames" value="firefox mozilla-firefox"/>
++      <param name="desktopnames" value="firefox mozilla-firefox"/>
++      -->
++<!--      <param name="displayname" value="KStars"/>
++      <param name="packagenames" value="kdeedu"/>
++      <param name="desktopnames" value="kstars"/> -->
++<!--      <param name="displayname" value="Mugshot"/>
++      <param name="packagenames" value="mugshot"/>
++      <param name="desktopnames" value="mugshot"/>  -->
++      <param name="displayname" value="GNU Backgammon"/>
++      <param name="packagenames" value="gnubg"/>
++      <param name="desktopnames" value="fedora-gnubg"/>
++    </object>
++  </body>
++</hml>
commit 27cce0d2f5159433a6e00a30ec537499192fb8bc
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Fri Aug 1 11:40:00 2008 -0400

    Switch launching from gnome-desktop to GAppInfo
     - Use GAppInfo rather than GnomeDesktopItem for locating desktop files
       and launching applications
     - Use GdkAppLaunchContext when available to get startup notification
     - Remove dependency on gnome-desktop and gnome-vfs

diff --git a/configure.ac b/configure.ac
index d82bf8e..1021498 100644
--- a/configure.ac
+++ b/configure.ac
@@ -30,7 +30,18 @@ if test "x$GCC" = "xyes"; then
 fi
 changequote([,])dnl
 
-PKG_CHECK_MODULES(PLUGIN, mozilla-plugin cairo pango gtk+-2.0 packagekit >= 0.2 dbus-glib-1 gnome-vfs-2.0 gnome-desktop-2.0)
+PKG_CHECK_MODULES(PLUGIN, mozilla-plugin gio-unix-2.0 cairo pango gtk+-2.0 packagekit >= 0.2 dbus-glib-1)
+
+packagekit_save_CPPFLAGS="$CPPFLAGS"
+packagekit_save_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $PLUGIN_CFLAGS"
+LDFLAGS="$CPPFLAGS $PLUGIN_LIBS"
+
+AC_CHECK_FUNCS(gdk_app_launch_context_new)
+
+CPPFLAGS="$packagekit_save_CPPFLAGS"
+LDFLAGS="$packagekit_save_LDFLAGS"
 
 AC_SUBST(PLUGIN_CFLAGS)
 AC_SUBST(PLUGIN_LIBS)
diff --git a/src/contents.cpp b/src/contents.cpp
index 043c87c..313faaa 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -49,13 +49,10 @@
 #include <packagekit/pk-package-id.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
-#include <libgnome/gnome-desktop-item.h>
-#include <libgnomevfs/gnome-vfs.h>
+#include <gio/gdesktopappinfo.h>
 
 #include "plugin.h"
 
-#define APPLICATION_DIR "/usr/share/applications"
-
 #define MARGIN 5
 
 ////////////////////////////////////////
@@ -91,6 +88,7 @@ PkpContents::PkpContents(const char *displayName,
     mDisplayName(displayName),
     mPackageNames(splitString(packageNames)),
     mDesktopNames(splitString(desktopNames)),
+    mAppInfo(0),
     mLayout(0),
     mInstallPackageProxy(0),
     mInstallPackageCall(0)
@@ -102,6 +100,11 @@ PkpContents::~PkpContents()
 {
     clearLayout();
 
+    if (mAppInfo != 0) {
+        g_object_unref(mAppInfo);
+        mAppInfo = 0;
+    }
+
     if (mInstallPackageCall != 0) {
         dbus_g_proxy_cancel_call(mInstallPackageProxy, mInstallPackageCall);
         g_object_unref(mInstallPackageProxy);
@@ -134,7 +137,7 @@ void PkpContents::recheck()
         }
     }
 
-    findDesktopFile();
+    findAppInfo();
 
     if (mClients.empty() && getStatus() == IN_PROGRESS)
         setStatus(UNAVAILABLE);
@@ -292,7 +295,7 @@ PkpContents::ensureLayout(cairo_t              *cr,
         append_markup(markup, _("Getting package information..."));
         break;
     case INSTALLED:
-        if (!mDesktopFile.empty())
+        if (mAppInfo != 0)
             append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Run %s</span>"),
                           link_color >> 8,
                           mDisplayName.c_str());
@@ -303,7 +306,7 @@ PkpContents::ensureLayout(cairo_t              *cr,
         break;
     case UPGRADABLE:
         append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
-        if (!mDesktopFile.empty()) {
+        if (mAppInfo != 0) {
             if (!mInstalledVersion.empty())
                 append_markup(markup, _("\n<span color='#%06x' underline='single'>Run version %s now</span>"),
                               link_color >> 8,
@@ -501,11 +504,11 @@ PkpContents::buttonRelease(int x, int y, Time time)
     case UNAVAILABLE:
         break;
     case INSTALLED:
-        if (!mDesktopFile.empty())
+        if (mAppInfo != 0)
             runApplication(time);
         break;
     case UPGRADABLE:
-        if (!mDesktopFile.empty() && index == 0)
+        if (mAppInfo != 0 && index == 0)
             runApplication(time);
         else {
             installPackage(time);
@@ -563,27 +566,26 @@ validate_name(const char *name)
 }
 
 void
-PkpContents::findDesktopFile()
+PkpContents::findAppInfo()
 {
     for (std::vector<std::string>::iterator i = mDesktopNames.begin(); i != mDesktopNames.end(); i++) {
         if (!validate_name(i->c_str())) {
             g_warning("Bad desktop name: '%s'", i->c_str());
             continue;
         }
-        
-        char *filename = g_strconcat(i->c_str(), ".desktop", NULL);
-        char *path = g_build_filename(APPLICATION_DIR, filename, NULL);
-        g_free(filename);
 
-        if (g_file_test(path, G_FILE_TEST_EXISTS)) {
-            mDesktopFile = path;
+        /* The "id" taken be g_desktop_app_info_new() is weirdly 'foo.desktop' not 'foo' */
+        char *id = g_strconcat(i->c_str(), ".desktop", NULL);
+        GDesktopAppInfo *desktopAppInfo = g_desktop_app_info_new(id);
+        g_free(id);
+        
+        if (desktopAppInfo) {
+            mAppInfo = G_APP_INFO(desktopAppInfo);
             break;
         }
-
-        g_free(path);
     }
 
-    if (!mDesktopFile.empty())
+    if (mAppInfo != 0)
         setStatus(INSTALLED);
 }
                   
@@ -592,34 +594,25 @@ PkpContents::runApplication (Time time)
 {
     GError *error = NULL;
     
-    /* This is idempotent and fairly cheap, so do it here to avoid initializing
-     * gnome-vfs on plugin startup
-     */
-    gnome_vfs_init();
-
-    if (mDesktopFile.c_str() == 0) {
+    if (mAppInfo == 0) {
         g_warning("Didn't find application to launch");
         return;
     }
 
-    GnomeDesktopItem *item = gnome_desktop_item_new_from_file(mDesktopFile.c_str(), GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS, &error);
-    if (!item) {
-        g_warning("%s\n", error->message);
-        g_clear_error(&error);
-        gnome_desktop_item_unref(item);
-        return;
-    }
-
-    if (time == CurrentTime)
-        time = get_server_timestamp();
+    GAppLaunchContext *context;
+#ifdef HAVE_GDK_APP_LAUNCH_CONTEXT_NEW
+    context = gdk_app_launch_context_new();
+    gdk_app_launch_context_set_timestamp(time);
+#endif    
 
-    gnome_desktop_item_set_launch_time(item, time);
-    if (!gnome_desktop_item_launch(item, NULL, (GnomeDesktopItemLaunchFlags)0, &error)) {
+    if (!g_app_info_launch(mAppInfo, NULL, context, &error)) {
         g_warning("%s\n", error->message);
         g_clear_error(&error);
-        gnome_desktop_item_unref(item);
         return;
     }
+
+    if (context != 0)
+        g_object_unref(context);
 }
 
 void
diff --git a/src/contents.h b/src/contents.h
index 78c2b87..797187d 100644
--- a/src/contents.h
+++ b/src/contents.h
@@ -40,6 +40,7 @@
 #define __CONTENTS_H__
 
 #include <X11/Xlib.h>
+#include <gio/gio.h>
 #include <pango/pango.h>
 #include <packagekit/pk-client.h>
 #include <cairo.h>
@@ -77,7 +78,7 @@ public:
 
 private:
     void recheck();
-    void findDesktopFile();
+    void findAppInfo();
     void runApplication(Time time);
     void installPackage(Time time);
 
@@ -120,7 +121,7 @@ private:
     std::string mAvailableVersion;
     std::string mAvailablePackageName;
     std::string mInstalledVersion;
-    std::string mDesktopFile;
+    GAppInfo *mAppInfo;
 
     std::string mDisplayName;
     std::vector<std::string> mPackageNames;
diff --git a/src/plugin.cpp b/src/plugin.cpp
index 6e76190..345b427 100644
--- a/src/plugin.cpp
+++ b/src/plugin.cpp
@@ -47,12 +47,7 @@
 
 #include <cairo-xlib.h>
 #include <dlfcn.h>
-#include <pango/pangocairo.h>
 #include <packagekit/pk-package-id.h>
-#include <gtk/gtk.h>
-#include <gdk/gdkx.h>
-#include <libgnome/gnome-desktop-item.h>
-#include <libgnomevfs/gnome-vfs.h>
 
 #include "plugin.h"
 
@@ -78,7 +73,7 @@ static void *module_handle = 0;
  * but isn't so much, since it only refers to a specific library and not
  * its dependent libraries, so we'd have to identify specifically each
  * of our dependencies that is not safe to unload and that is most of
- * the GTK+/GNOME stack.
+ * the GTK+ stack.
  */
 static void
 make_module_resident()
commit b6eb3458816fc360699e88589153eb5f8e9d6cd2
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Mon Jul 28 15:00:35 2008 -0400

    Call gnome_desktop_item_set_launch_time() with value from event

diff --git a/src/contents.cpp b/src/contents.cpp
index 7de6a6c..043c87c 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -502,18 +502,18 @@ PkpContents::buttonRelease(int x, int y, Time time)
         break;
     case INSTALLED:
         if (!mDesktopFile.empty())
-            runApplication();
+            runApplication(time);
         break;
     case UPGRADABLE:
         if (!mDesktopFile.empty() && index == 0)
-            runApplication();
+            runApplication(time);
         else {
-            installPackage();
+            installPackage(time);
         }
         break;
     case AVAILABLE:
         if (!mAvailablePackageName.empty())
-            installPackage();
+            installPackage(time);
         break;
     }
 }
@@ -588,7 +588,7 @@ PkpContents::findDesktopFile()
 }
                   
 void
-PkpContents::runApplication (void)
+PkpContents::runApplication (Time time)
 {
     GError *error = NULL;
     
@@ -610,10 +610,10 @@ PkpContents::runApplication (void)
         return;
     }
 
-    guint32 launch_time = gtk_get_current_event_time();
-    if (launch_time == GDK_CURRENT_TIME)
-        launch_time = get_server_timestamp();
+    if (time == CurrentTime)
+        time = get_server_timestamp();
 
+    gnome_desktop_item_set_launch_time(item, time);
     if (!gnome_desktop_item_launch(item, NULL, (GnomeDesktopItemLaunchFlags)0, &error)) {
         g_warning("%s\n", error->message);
         g_clear_error(&error);
@@ -623,7 +623,7 @@ PkpContents::runApplication (void)
 }
 
 void
-PkpContents::installPackage (void)
+PkpContents::installPackage (Time time)
 {
     if (mAvailablePackageName.empty()) {
         g_warning("No available package to install");
diff --git a/src/contents.h b/src/contents.h
index 541050b..78c2b87 100644
--- a/src/contents.h
+++ b/src/contents.h
@@ -78,8 +78,8 @@ public:
 private:
     void recheck();
     void findDesktopFile();
-    void runApplication();
-    void installPackage();
+    void runApplication(Time time);
+    void installPackage(Time time);
 
     int getLinkIndex(int x, int y);
     
commit 9aa468fc7f2fc3c34336b5ae0841b4dbb665955a
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Mon Jul 28 14:47:15 2008 -0400

    Allow for multiple links:
     - Require clicking on links, not just anywhere in the plugin
     - Add a separate UPGRADABLE state
     - Display UPGRADE with separate links to install and run

diff --git a/src/contents.cpp b/src/contents.cpp
index 13c9dcc..7de6a6c 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -273,8 +273,8 @@ get_style(PangoFontDescription **font_desc,
 
 void
 PkpContents::ensureLayout(cairo_t              *cr,
-                               PangoFontDescription *font_desc,
-                               guint32               link_color)
+                          PangoFontDescription *font_desc,
+                          guint32               link_color)
 {
     GString *markup = g_string_new(NULL);
     
@@ -284,6 +284,9 @@ PkpContents::ensureLayout(cairo_t              *cr,
     mLayout = pango_cairo_create_layout(cr);
     pango_layout_set_font_description(mLayout, font_desc);
 
+    /* WARNING: Any changes to what links are created here will require corresponding
+     * changes to the buttonRelease() method
+     */
     switch (mStatus) {
     case IN_PROGRESS:
         append_markup(markup, _("Getting package information..."));
@@ -298,6 +301,23 @@ PkpContents::ensureLayout(cairo_t              *cr,
         if (!mInstalledVersion.empty())
             append_markup(markup, _("\n<small>Installed version: %s</small>"), mInstalledVersion.c_str());
         break;
+    case UPGRADABLE:
+        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
+        if (!mDesktopFile.empty()) {
+            if (!mInstalledVersion.empty())
+                append_markup(markup, _("\n<span color='#%06x' underline='single'>Run version %s now</span>"),
+                              link_color >> 8,
+                              mInstalledVersion.c_str());
+            else
+                append_markup(markup,
+                              _("\n<span color='#%06x' underline='single'>Run now</span>"),
+                              link_color >> 8);
+        }
+        
+        append_markup(markup, _("\n<span color='#%06x' underline='single'>Upgrade to version %s</span>"),
+                      link_color >> 8,
+                      mAvailableVersion.c_str());
+        break;
     case AVAILABLE:
         append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Install %s Now</span>"),
                       link_color >> 8,
@@ -357,6 +377,112 @@ PkpContents::draw(cairo_t *cr)
     pango_cairo_show_layout(cr, mLayout);
 }
 
+/* Cut and paste from pango-layout.c; determines if a layout iter is on 
+ * a line terminated by a real line break (rather than a line break from
+ * wrapping). We use this to determine whether the empty run at the end
+ * of a display line should be counted as a break between links or not.
+ *
+ * (Code in pango-layout.c is by me, Copyright Red Hat, and hereby relicensed
+ * to the license of this file)
+ */
+static gboolean
+line_is_terminated (PangoLayoutIter *iter)
+{
+    /* There is a real terminator at the end of each paragraph other
+     * than the last.
+     */
+    PangoLayoutLine *line = pango_layout_iter_get_line(iter);
+    GSList *lines = pango_layout_get_lines(pango_layout_iter_get_layout(iter));
+    GSList *link = g_slist_find(lines, line);
+    if (!link) {
+        g_warning("Can't find line in layout line list\n");
+        return FALSE;
+    }
+
+    if (link->next) {
+        PangoLayoutLine *next_line = (PangoLayoutLine *)link->next->data;
+        if (next_line->is_paragraph_start)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+/* This function takes an X,Y position and determines whether it is over one
+ * of the underlined portions of the layout (a link). It works by iterating
+ * through the runs of the layout (a run is a segment with a consistent
+ * font and display attributes, more or less), and counting the underlined
+ * segments that we see. A segment that is underlined could be broken up
+ * into multiple runs if it is drawn with multiple fonts due to fonts
+ * substitution, so we actually count non-underlined => underlined
+ * transitions.
+ */
+int
+PkpContents::getLinkIndex(int x, int y)
+{
+    /* Coordinates are relative to origin of plugin (different from drawing) */
+    
+    if (!mLayout)
+        return -1;
+
+    x -= MARGIN;
+    y -= MARGIN;
+
+    int index;
+    int trailing;
+    if (!pango_layout_xy_to_index(mLayout,
+                                  x * PANGO_SCALE, y * PANGO_SCALE,
+                                  &index, &trailing))
+        return - 1;
+
+    PangoLayoutIter *iter = pango_layout_get_iter(mLayout);
+    int seen_links = 0;
+    bool in_link = false;
+    int result = -1;
+    
+    while (TRUE) {
+        PangoLayoutRun *run = pango_layout_iter_get_run(iter);
+        if (run) {
+            PangoItem *item = run->item;
+            PangoUnderline uline = PANGO_UNDERLINE_NONE;
+            
+            for (GSList *l = item->analysis.extra_attrs; l; l = l->next) {
+                PangoAttribute *attr = (PangoAttribute *)l->data;
+                if (attr->klass->type == PANGO_ATTR_UNDERLINE) {
+                    uline = (PangoUnderline)((PangoAttrInt *)attr)->value;
+                }
+            }
+
+            if (uline == PANGO_UNDERLINE_NONE)
+                in_link = FALSE;
+            else if (!in_link) {
+                in_link = TRUE;
+                seen_links++;
+            }
+
+            if (item->offset <= index && index < item->offset + item->length) {
+                if (in_link)
+                    result = seen_links - 1;
+
+                break;
+            }
+        } else {
+            /* We have an empty run at the end of each line. A line break doesn't
+             * terminate the link, but a real newline does.
+             */
+            if (line_is_terminated(iter))
+                in_link = FALSE;
+        }
+        
+        if (!pango_layout_iter_next_run (iter))
+            break;
+    }
+    
+    pango_layout_iter_free(iter);
+
+    return result;
+}
+
 void
 PkpContents::buttonPress(int x, int y, Time time)
 {
@@ -365,10 +491,31 @@ PkpContents::buttonPress(int x, int y, Time time)
 void
 PkpContents::buttonRelease(int x, int y, Time time)
 {
-    if (!mDesktopFile.empty())
-        runApplication();
-    else if (!mAvailablePackageName.empty())
-        installPackage();
+    int index = getLinkIndex(x, y);
+    if (index < 0)
+        return;
+    
+    switch (mStatus) {
+    case IN_PROGRESS:
+    case INSTALLING:
+    case UNAVAILABLE:
+        break;
+    case INSTALLED:
+        if (!mDesktopFile.empty())
+            runApplication();
+        break;
+    case UPGRADABLE:
+        if (!mDesktopFile.empty() && index == 0)
+            runApplication();
+        else {
+            installPackage();
+        }
+        break;
+    case AVAILABLE:
+        if (!mAvailablePackageName.empty())
+            installPackage();
+        break;
+    }
 }
 
 void
@@ -518,12 +665,17 @@ PkpContents::onClientPackage(PkClient	  *client,
     PkPackageId *id = pk_package_id_new_from_string(package_id);
     
     if (info == PK_INFO_ENUM_AVAILABLE) {
-        if (contents->getStatus() != INSTALLED)
+        if (contents->getStatus() == IN_PROGRESS)
             contents->setStatus(AVAILABLE);
+        else if (contents->getStatus() == INSTALLED)
+            contents->setStatus(UPGRADABLE);
         contents->setAvailableVersion(id->version);
         contents->setAvailablePackageName(id->name);
     } else if (info == PK_INFO_ENUM_INSTALLED) {
-        contents->setStatus(INSTALLED);
+        if (contents->getStatus() == IN_PROGRESS)
+            contents->setStatus(INSTALLED);
+        else if (contents->getStatus() == AVAILABLE)
+            contents->setStatus(UPGRADABLE);
         contents->setInstalledVersion(id->version);
     }
     
diff --git a/src/contents.h b/src/contents.h
index f9d080b..541050b 100644
--- a/src/contents.h
+++ b/src/contents.h
@@ -51,7 +51,14 @@
 
 class PkpPluginInstance;
 
-enum PackageStatus { IN_PROGRESS, INSTALLED, AVAILABLE, UNAVAILABLE, INSTALLING };
+enum PackageStatus {
+    IN_PROGRESS, /* Looking up package information */
+    INSTALLED,   /* Package installed */
+    UPGRADABLE,  /* Package installed, newer version available */
+    AVAILABLE,   /* Package not installed, version available */
+    UNAVAILABLE, /* Package not installed or available */
+    INSTALLING   /* Currently installing a new version */
+};
 
 class PkpContents
 {
@@ -73,6 +80,8 @@ private:
     void findDesktopFile();
     void runApplication();
     void installPackage();
+
+    int getLinkIndex(int x, int y);
     
     void setStatus(PackageStatus status);
     PackageStatus getStatus() { return mStatus; }
@@ -118,8 +127,6 @@ private:
     std::vector<std::string> mDesktopNames;
     
     PangoLayout *mLayout;
-    int mLinkStart;
-    int mLinkEnd;
 
     std::vector<PkClient *> mClients;
 
commit 85c62c1437e92ba9a43141313a99c50382885acb
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Mon Jul 28 13:22:41 2008 -0400

    Remove vestiges of support for PackageKit 0.1

diff --git a/configure.ac b/configure.ac
index bc14355..d82bf8e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -30,11 +30,7 @@ if test "x$GCC" = "xyes"; then
 fi
 changequote([,])dnl
 
-PKG_CHECK_MODULES(PLUGIN, mozilla-plugin cairo pango gtk+-2.0 packagekit dbus-glib-1 gnome-vfs-2.0 gnome-desktop-2.0)
-
-if $PKG_CONFIG --exists packagekit '>=' 0.2 ; then
-   AC_DEFINE(HAVE_PACKAGEKIT_0_2, 1, [define if you have PackageKit 0.2 or newer])
-fi
+PKG_CHECK_MODULES(PLUGIN, mozilla-plugin cairo pango gtk+-2.0 packagekit >= 0.2 dbus-glib-1 gnome-vfs-2.0 gnome-desktop-2.0)
 
 AC_SUBST(PLUGIN_CFLAGS)
 AC_SUBST(PLUGIN_LIBS)
diff --git a/src/contents.cpp b/src/contents.cpp
index a0de7a7..13c9dcc 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -122,11 +122,7 @@ void PkpContents::recheck()
     for (std::vector<std::string>::iterator i = mPackageNames.begin(); i != mPackageNames.end(); i++) {
         GError *error = NULL;
         PkClient *client = pk_client_new();
-#ifdef HAVE_PACKAGEKIT_0_2        
         if (!pk_client_resolve(client, PK_FILTER_ENUM_NONE, i->c_str(), &error)) {
-#else
-        if (!pk_client_resolve(client, "none", i->c_str(), &error)) {
-#endif            
             g_warning("%s", error->message);
             g_clear_error(&error);
             g_object_unref(client);
commit 58648594d9fcc1e1df81c8bb80a957e96d525456
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Mon Jul 28 13:19:40 2008 -0400

    Use org.freedesktop.PackageKit session service rather than exec'ing gpk-install-package-name

diff --git a/Makefile.am b/Makefile.am
index 7b02cf0..03ec221 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -21,9 +21,7 @@ packagekit_plugin_la_SOURCES = 	\
 	src/contents.cpp	\
 	src/contents.h		\
 	src/plugin.cpp		\
-	src/plugin.h		\
-	src/util.cpp		\
-	src/util.h
+	src/plugin.h
 
 DISTCLEANFILES =				\
 	intltool-extract			\
diff --git a/configure.ac b/configure.ac
index 1487c18..bc14355 100644
--- a/configure.ac
+++ b/configure.ac
@@ -30,7 +30,7 @@ if test "x$GCC" = "xyes"; then
 fi
 changequote([,])dnl
 
-PKG_CHECK_MODULES(PLUGIN, mozilla-plugin cairo pango gtk+-2.0 packagekit gnome-vfs-2.0 gnome-desktop-2.0)
+PKG_CHECK_MODULES(PLUGIN, mozilla-plugin cairo pango gtk+-2.0 packagekit dbus-glib-1 gnome-vfs-2.0 gnome-desktop-2.0)
 
 if $PKG_CONFIG --exists packagekit '>=' 0.2 ; then
    AC_DEFINE(HAVE_PACKAGEKIT_0_2, 1, [define if you have PackageKit 0.2 or newer])
diff --git a/src/contents.cpp b/src/contents.cpp
index 196b3a7..a0de7a7 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -92,7 +92,8 @@ PkpContents::PkpContents(const char *displayName,
     mPackageNames(splitString(packageNames)),
     mDesktopNames(splitString(desktopNames)),
     mLayout(0),
-    mInstallPackageHandle(0)
+    mInstallPackageProxy(0),
+    mInstallPackageCall(0)
 {
     recheck();
 }
@@ -101,8 +102,12 @@ PkpContents::~PkpContents()
 {
     clearLayout();
 
-    if (mInstallPackageHandle != 0)
-        pkp_execute_command_async_cancel(mInstallPackageHandle);
+    if (mInstallPackageCall != 0) {
+        dbus_g_proxy_cancel_call(mInstallPackageProxy, mInstallPackageCall);
+        g_object_unref(mInstallPackageProxy);
+        mInstallPackageProxy = 0;
+        mInstallPackageCall = 0;
+    }
 
     while (!mClients.empty())
         removeClient(mClients.front());
@@ -482,22 +487,29 @@ PkpContents::installPackage (void)
         return;
     }
 
-    if (mInstallPackageHandle != 0) {
+    if (mInstallPackageCall != 0) {
         g_warning("Already installing package");
         return;
     }
 
-    char *argv[3];
-#if HAVE_PACKAGEKIT_0_2
-    argv[0] = (char *)"gpk-install-package-name";
-#else    
-    argv[0] = (char *)"gpk-install-package";
-#endif    
-    argv[1] = (char *)mAvailablePackageName.c_str();
-    argv[2] = 0;
-
-    mInstallPackageHandle = pkp_execute_command_async(argv, onInstallFinished, this);
-    setStatus(INSTALLING);
+    /* Get a proxy to the *session* PackageKit service */
+    DBusGConnection *connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+    mInstallPackageProxy = dbus_g_proxy_new_for_name(connection,
+                                                     "org.freedesktop.PackageKit",
+                                                     "/org/freedesktop/PackageKit",
+                                                     "org.freedesktop.PackageKit");
+
+    mInstallPackageCall = dbus_g_proxy_begin_call_with_timeout(mInstallPackageProxy,
+                                                               "InstallPackageName",
+                                                               onInstallPackageFinished,
+                                                               this,
+                                                               (GDestroyNotify)0,
+                                                               24 * 60 * 1000 * 1000, /* one day */
+                                                               G_TYPE_STRING, mAvailablePackageName.c_str(),
+                                                               G_TYPE_INVALID,
+                                                               G_TYPE_INVALID);
+    
+     setStatus(INSTALLING);
 }
 
 void
@@ -543,22 +555,23 @@ PkpContents::onClientFinished(PkClient	    *client,
 }
     
 void
-PkpContents::onInstallFinished(GError        *error,
-                               int            status,
-                               const char    *output,
-                               void          *callback_data)
+PkpContents::onInstallPackageFinished (DBusGProxy       *proxy,
+                                       DBusGProxyCall   *call,
+                                       void             *user_data)
 {
-    PkpContents *contents = (PkpContents *)callback_data;
-
-    contents->mInstallPackageHandle = 0;
+    PkpContents *contents = (PkpContents *)user_data;
     
-    if (error) {
+    GError *error = NULL;
+    if (!dbus_g_proxy_end_call(proxy, call, &error, 
+                               G_TYPE_INVALID)) {
         g_warning("Error occurred during install: %s", error->message);
+        g_clear_error(&error);
+        
     }
 
-    if (status != 0) {
-        g_warning("gpk-install-command exited with non-zero status %d", status);
-    }
-
+    g_object_unref(contents->mInstallPackageProxy);
+    contents->mInstallPackageProxy = 0;
+    contents->mInstallPackageCall = 0;
+    
     contents->recheck();
 }
diff --git a/src/contents.h b/src/contents.h
index 388b246..f9d080b 100644
--- a/src/contents.h
+++ b/src/contents.h
@@ -43,13 +43,12 @@
 #include <pango/pango.h>
 #include <packagekit/pk-client.h>
 #include <cairo.h>
+#include <dbus/dbus-glib.h>
 #include <gtk/gtk.h>
 
 #include <string>
 #include <vector>
 
-#include "util.h"
-
 class PkpPluginInstance;
 
 enum PackageStatus { IN_PROGRESS, INSTALLED, AVAILABLE, UNAVAILABLE, INSTALLING };
@@ -102,10 +101,10 @@ private:
                                  PkExitEnum	   exit,
                                  guint		   runtime,
                                  PkpContents      *contents);
-    static void onInstallFinished(GError        *error,
-                                  int            status,
-                                  const char    *output,
-                                  void          *callback_data);
+    
+    static void onInstallPackageFinished(DBusGProxy     *proxy,
+                                         DBusGProxyCall *call,
+                                         void           *user_data);
 
     PkpPluginInstance *mPlugin;
     PackageStatus mStatus;
@@ -124,7 +123,8 @@ private:
 
     std::vector<PkClient *> mClients;
 
-    PkpExecuteCommandAsyncHandle *mInstallPackageHandle;
+    DBusGProxy *mInstallPackageProxy;
+    DBusGProxyCall *mInstallPackageCall;
 };
 
 #endif // __CONTENTS_H__
diff --git a/src/plugin.h b/src/plugin.h
index 39d600b..9d3af87 100644
--- a/src/plugin.h
+++ b/src/plugin.h
@@ -42,7 +42,6 @@
 
 #include "contents.h"
 #include "pluginbase.h"
-#include "util.h"
 
 class PkpPluginInstance : public nsPluginInstanceBase
 {
diff --git a/src/util.cpp b/src/util.cpp
deleted file mode 100644
index 01a24c8..0000000
--- a/src/util.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is packagekit-plugin code.
- *
- * The Initial Developer of the Original Code is
- * Red Hat, Inc.
- * Portions created by the Initial Developer are Copyright (C) 2008
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <config.h>
-#include "util.h"
-
-struct PkpExecuteCommandAsyncHandle {
-    PkpExecuteCommandCallback callback;
-    void *callback_data;
-
-    GError *error;
-    int status;
-    GString *output;
-
-    guint io_watch;
-    guint child_watch;
-    
-    gboolean exited;
-    gboolean closed;
-};
-    
-static void
-pkp_execute_command_async_free(PkpExecuteCommandAsyncHandle *handle)
-{
-    if (handle->io_watch)
-        g_source_remove(handle->io_watch);
-    if (handle->child_watch)
-        g_source_remove(handle->child_watch);
-    
-    if (handle->error)
-        g_error_free(handle->error);
-    
-    g_string_free(handle->output, TRUE);
-    g_free(handle);
-}
-
-static void
-pkp_execute_command_async_finish(PkpExecuteCommandAsyncHandle *handle)
-{
-    handle->callback(handle->error, handle->status, handle->output->str, handle->callback_data);
-
-    pkp_execute_command_async_free(handle);
-}
-     
-static void
-pkp_execute_async_child_watch(GPid     pid,
-			      int      status,
-			      gpointer data)
-{
-    PkpExecuteCommandAsyncHandle *handle = (PkpExecuteCommandAsyncHandle *)data;
-
-    handle->exited = TRUE;
-    handle->child_watch = 0;
-    handle->status = status;
-
-    if (handle->exited && handle->closed)
-        pkp_execute_command_async_finish(handle);
-}
-    
-static gboolean
-pkp_execute_async_io_watch(GIOChannel   *source,
-			   GIOCondition  condition,
-			   gpointer      data)
-{
-    PkpExecuteCommandAsyncHandle *handle = (PkpExecuteCommandAsyncHandle *)data;
-    GIOStatus status;
-    char buf[1024];
-    gsize bytes_read;
-
-    handle->io_watch = 0;
-    
-    status = g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, &handle->error);
-    switch (status) {
-    case G_IO_STATUS_ERROR:
-        g_io_channel_close(source);
-        handle->closed = TRUE;
-        if (handle->exited && handle->closed)
-            pkp_execute_command_async_finish(handle);
-        return FALSE;
-    case G_IO_STATUS_NORMAL:
-        g_string_append_len(handle->output, buf, bytes_read);
-        break;
-    case G_IO_STATUS_EOF:
-        g_io_channel_close(source);
-        handle->closed = TRUE;
-        if (handle->exited && handle->closed)
-            pkp_execute_command_async_finish(handle);
-        
-        return FALSE;
-    case G_IO_STATUS_AGAIN:
-        /* Should not be reached */
-        break;
-    }
-
-    return TRUE;
-}
-
-PkpExecuteCommandAsyncHandle *
-pkp_execute_command_async(char                     **argv,
-			  PkpExecuteCommandCallback  callback,
-			  void                      *callback_data)
-{
-    PkpExecuteCommandAsyncHandle *handle;
-    GPid child_pid;
-    int out_fd;
-    GIOChannel *channel;
-    const char *locale_encoding;
-
-    handle = g_new(PkpExecuteCommandAsyncHandle, 1);
-    
-    handle->callback = callback;
-    handle->callback_data = callback_data;
-    
-    handle->error = NULL;
-    handle->status = -1;
-    handle->output = g_string_new(NULL);
-
-    handle->closed = FALSE;
-    handle->exited = FALSE;
-    
-    if (!g_spawn_async_with_pipes(NULL, argv, NULL,
-                                  (GSpawnFlags)(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
-                                  NULL, NULL,
-                                  &child_pid,
-                                  NULL, &out_fd, NULL,
-                                  &handle->error)) {
-        pkp_execute_command_async_finish(handle);
-        return NULL;
-    }
-
-    channel = g_io_channel_unix_new(out_fd);
-    
-    g_get_charset(&locale_encoding);
-    g_io_channel_set_encoding(channel, locale_encoding, NULL);
-
-    handle->io_watch = g_io_add_watch(channel,
-                                      (GIOCondition)(G_IO_IN | G_IO_HUP),
-                                      pkp_execute_async_io_watch,
-                                      handle);
-    g_io_channel_unref(channel);
-
-    handle->child_watch = g_child_watch_add(child_pid,
-                                            pkp_execute_async_child_watch,
-                                            handle);
-
-    return handle;
-}
-
-void
-pkp_execute_command_async_cancel (PkpExecuteCommandAsyncHandle *handle)
-{
-    pkp_execute_command_async_free(handle);
-}
diff --git a/src/util.h b/src/util.h
deleted file mode 100644
index d36b84b..0000000
--- a/src/util.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is packagekit-plugin code.
- *
- * The Initial Developer of the Original Code is
- * Red Hat, Inc.
- * Portions created by the Initial Developer are Copyright (C) 2008
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include <glib.h>
-
-struct PkpExecuteCommandAsyncHandle;
-
-typedef void (*PkpExecuteCommandCallback) (GError                    *error,
-					   int                        status,
-					   const char                *output,
-					   void                      *callback_data);
-
-PkpExecuteCommandAsyncHandle *pkp_execute_command_async (char                         **argv,
-							 PkpExecuteCommandCallback      callback,
-							 void                          *callback_data);
-
-/* This doesn't actually try to kill the command, it just cancels the callback */
-void pkp_execute_command_async_cancel (PkpExecuteCommandAsyncHandle *handle);
commit 33b12006227c929f6db4011eae1d695780ea3ff2
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Jul 24 16:18:18 2008 -0400

    Clean up stray debug printfS

diff --git a/src/contents.cpp b/src/contents.cpp
index 4b691fe..196b3a7 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -507,8 +507,6 @@ PkpContents::onClientPackage(PkClient	  *client,
                                   const gchar	   *summary,
                                   PkpContents *contents)
 {
-    fprintf(stderr, "package: %d %s %s\n", info, package_id, summary);
-
     PkPackageId *id = pk_package_id_new_from_string(package_id);
     
     if (info == PK_INFO_ENUM_AVAILABLE) {
@@ -525,12 +523,12 @@ PkpContents::onClientPackage(PkClient	  *client,
 }
 
 void
-PkpContents::onClientErrorCode(PkClient	     *client,
-                                    PkErrorCodeEnum   code,
-                                    const gchar	     *details,
-                                    PkpContents *contents)
+PkpContents::onClientErrorCode(PkClient	       *client,
+                               PkErrorCodeEnum  code,
+                               const gchar     *details,
+                               PkpContents     *contents)
 {
-    fprintf(stderr, "error code: %d %s\n", code, details);
+    g_warning("Error getting data from PackageKit: %s\n", details);
     contents->removeClient(client);
 }
 
@@ -541,7 +539,6 @@ PkpContents::onClientFinished(PkClient	    *client,
                               PkpContents   *contents)
     
 {
-    fprintf(stderr, "finished: %d\n", exit);
     contents->removeClient(client);
 }
     
commit 76ffe4169ea573c9889d8cd518a7a80ca77dfac3
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Jul 24 16:16:46 2008 -0400

    gpk-install-package => gpk-install-package-name for PackageKit 0.2

diff --git a/src/contents.cpp b/src/contents.cpp
index 835144e..4b691fe 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -488,7 +488,11 @@ PkpContents::installPackage (void)
     }
 
     char *argv[3];
+#if HAVE_PACKAGEKIT_0_2
+    argv[0] = (char *)"gpk-install-package-name";
+#else    
     argv[0] = (char *)"gpk-install-package";
+#endif    
     argv[1] = (char *)mAvailablePackageName.c_str();
     argv[2] = 0;
 
commit 43582461a21567a367b1311c9e0e9d0e99bab71d
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Jul 24 16:10:28 2008 -0400

    Fix crash on plugin initialization

diff --git a/src/contents.cpp b/src/contents.cpp
index 4fc9c74..835144e 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -87,6 +87,7 @@ PkpContents::PkpContents(const char *displayName,
                          const char *packageNames,
                          const char *desktopNames) :
     mStatus(IN_PROGRESS),
+    mPlugin(0),
     mDisplayName(displayName),
     mPackageNames(splitString(packageNames)),
     mDesktopNames(splitString(desktopNames)),
@@ -319,7 +320,8 @@ PkpContents::ensureLayout(cairo_t              *cr,
 void
 PkpContents::refresh()
 {
-    mPlugin->refresh();
+    if (mPlugin != 0)
+        mPlugin->refresh();
 }
                                
 void
commit 24d607eb209903b29a4c8694966540e86354a0d2
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Jul 24 16:07:39 2008 -0400

    Fix compilation with PackageKit 0.2

diff --git a/configure.ac b/configure.ac
index fab93d2..1487c18 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,6 +32,10 @@ changequote([,])dnl
 
 PKG_CHECK_MODULES(PLUGIN, mozilla-plugin cairo pango gtk+-2.0 packagekit gnome-vfs-2.0 gnome-desktop-2.0)
 
+if $PKG_CONFIG --exists packagekit '>=' 0.2 ; then
+   AC_DEFINE(HAVE_PACKAGEKIT_0_2, 1, [define if you have PackageKit 0.2 or newer])
+fi
+
 AC_SUBST(PLUGIN_CFLAGS)
 AC_SUBST(PLUGIN_LIBS)
 
diff --git a/src/contents.cpp b/src/contents.cpp
index 4b71e25..4fc9c74 100644
--- a/src/contents.cpp
+++ b/src/contents.cpp
@@ -116,7 +116,11 @@ void PkpContents::recheck()
     for (std::vector<std::string>::iterator i = mPackageNames.begin(); i != mPackageNames.end(); i++) {
         GError *error = NULL;
         PkClient *client = pk_client_new();
+#ifdef HAVE_PACKAGEKIT_0_2        
+        if (!pk_client_resolve(client, PK_FILTER_ENUM_NONE, i->c_str(), &error)) {
+#else
         if (!pk_client_resolve(client, "none", i->c_str(), &error)) {
+#endif            
             g_warning("%s", error->message);
             g_clear_error(&error);
             g_object_unref(client);
commit 061f7d4cffe1982ce08cb5e1201d6e9291e0b974
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Jul 24 14:29:02 2008 -0400

    Add a README section on testing

diff --git a/README b/README
index 2045b8d..812a479 100644
--- a/README
+++ b/README
@@ -87,6 +87,22 @@ libpackagekit can receive asynchronous notifications from the PackageKit daemon.
 So, getting it to work in Konqueror is going to be hard unless Qt has been
 compiled to use the GLib main loop.
 
+Trying it out
+=============
+
+packagekit-plugin builds with the standard:
+
+ ./autogen.sh
+ ./configure
+ make
+
+Instead of doing 'make install', you can symlink .libs/packagekit-plugin into
+~/.mozilla/plugins. Then you can restart your browser and load the HTML page
+under tests/. 
+
+If you do this, you probably want to delete the symlink when you are
+done so that it doesn't interfere with a system install of the plugin.
+
 Using
 =====
 
commit b36a120f4d36fa8a8939e2b526d066a07e93ab9f
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Jul 24 14:16:42 2008 -0400

    Fill in the README file

diff --git a/README b/README
index e69de29..2045b8d 100644
--- a/README
+++ b/README
@@ -0,0 +1,135 @@
+package-plugin
+==============
+This is a very simple browser plugin that is meant to allow a website
+to add a box to allow to install or run a particular piece of software
+provided in their distribution's repositories.
+
+If the package is not installed but is available in the package repository, the plugin
+will show:
+
+ +------------------------------------+
+ | _Install GNU Backgammon Now_       |
+ | Version: 20061119-14.fc9           |
+ +------------------------------------+
+
+Click on the plugin, and it will fire off gpk-install-package to install the package;
+the display changes to:
+
+ +------------------------------------+
+ | GNU Backgammon                     |
+ | Installing...                      |
+ +------------------------------------+
+
+once that is done, the plugin will show:
+
+ +------------------------------------+
+ | _Run GNU Backgammon_               |
+ | Installed version: 20061119-14.fc9 |
+ +------------------------------------+
+
+Clicking on it launches the application.
+
+Security Considerations
+=======================
+
+The design as a plugin is specifically meant to avoid revealing information about installed
+applications to the web page. None of the information in the plugin is revealed in the DOM
+tree. It might be possible to guess at results by subtle timing attacks, but it would at
+best be a probalistic thing.
+
+Wweb pages probably can trick the user into clicking on the plugin
+and installing or running apps they dont' want to install or run:
+
+ - The application title is supplied by the web page
+ - The web page could put elements over the plugin to make it appear
+
+(The second is why I consider the first to be acceptable.) However, importantly, the
+only applications that could be installed in that way are applications from the package
+repository already configured for the system. The only applications that can be run in 
+this way are applications already on the system. And there is no ability to pass command
+line arguments or files to the applications. So, the security risk should be minimal.
+
+Accessibility
+=============
+
+The design as a plugin (for the above security considerations) causes some problems for
+accessibility. In particular, there is no access into the plugin for screenreaders
+or other assistive technologies. Making this work would require the existence of a 
+system for integrating accessibility support between the browser and plugins, which,
+to my knowledge does not exist at the moment.
+
+Keynav is also missing at the moment, but could be added quite easily. (Basically,
+the Return keypress just needs to be treated like clicking on the plugin.)
+
+The fonts, font sizes, and colors used in the plugin come from the system theme, so they
+should not pose a problem for users with special needs. (But see discussion of 
+sizing below.)
+
+Cross Browser Support
+=====================
+
+The plugins acts as a standard windowless plugin, so it shouldn't be restricted
+to working in Firefox or in Gecko-based browsers.
+
+The plugin does assume that the toolkit hosting the browser is GTK+ for a couple
+of things:
+
+ - To get the theme colors and fonts
+ - To get a timestamp from the X server if one isn't provided in the button press
+   event when launching an app)
+
+These could be fixed if necessary. (Use XSETTINGS directly for fonts and colors,
+just skip the timestamp and let the newly launched app get placed in the
+background if not running GTK+ and no timestamp is provided.)
+
+More deeply, the plugin requires that the GLib main loop be running, so that 
+libpackagekit can receive asynchronous notifications from the PackageKit daemon.
+So, getting it to work in Konqueror is going to be hard unless Qt has been
+compiled to use the GLib main loop.
+
+Using
+=====
+
+dd the following code to your web page:
+
+    <object type="application/x-packagekit-plugin" width="300" height="150">
+      <!-- Name that will be used in the user interface -->
+      <param name="displayname" value="GNU Backgammon"/>
+      <!-- Whitespace separated list of package names -->
+      <param name="packagenames" value="gnubg"/>
+      <!-- List of basenames of possible desktop files (no .desktop extension) -->
+      <param name="desktopnames" value="gnubg fedora-gnubg"/>
+    </object>
+
+Unfortunately browser plugins have no mechanism for size negotiation, so you
+have to specify a predetermined size in some fashion. You can do it with the pixel
+sizes as above, or you can do it with CSS using 'em' sizes or percentage 
+lengths. For example, you could have in your CSS:
+
+      .packagekit-plugin {
+         width: 30em;
+         height: 5em;
+      }
+
+And then add class="packagekit-plugin" to the object tag above.
+
+You should make the size generously bigger than what you need on your system
+to deal with translations and with varying font sizes and device resolutions.
+Using em sizes rather than points is probably a good idea to help with the
+font size issue, but it doesn't completely resolve it since the text of the
+plugin will be sized based on the system font size, not on the web pages font.
+
+License and Copyright
+=====================
+
+You may distribute the code under the terms of the Mozilla Public License 1.1,
+GNU General Public License Version 2 or later (the "GPL"), or the
+GNU Lesser General Public License Version 2.1 or later (the "LGPL"). See the
+source files for details and for copyright information.
+
+The plugin infrastructure (The sdk/ directory and much of src/plugin.cpp
+and src/plugin.h) is derived from the Mozilla plugin SDK; other code under
+src/ is new code written by Red Hat, Inc.
+
+Owen Taylor <otaylor at redhat.com>
+July 24, 2008
commit 99e02f44a2dcda1abad76cbb6e4125fae2f81441
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Jul 24 13:23:34 2008 -0400

    Split plugin.cpp
     plugin.cpp - plugin boilerplate
     contents.cpp - code that draws the contents

diff --git a/Makefile.am b/Makefile.am
index 4fd3ca1..7b02cf0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,7 @@ packagekit_plugin_la_CPPFLAGS =			\
 	-DLOCALEDIR=\"$(localedir)\"		\
 	-I $(top_srcdir)/sdk
 
-packagekit_plugin_la_LIBS = $(PLUGIN_LIBS)
+packagekit_plugin_la_LIBADD = $(PLUGIN_LIBS)
 
 packagekit_plugin_la_SOURCES = 	\
 	sdk/np_entry.cpp	\
@@ -18,6 +18,8 @@ packagekit_plugin_la_SOURCES = 	\
 	sdk/npplat.h		\
 	sdk/pluginbase.h	\
 				\
+	src/contents.cpp	\
+	src/contents.h		\
 	src/plugin.cpp		\
 	src/plugin.h		\
 	src/util.cpp		\
diff --git a/src/contents.cpp b/src/contents.cpp
new file mode 100644
index 0000000..4b71e25
--- /dev/null
+++ b/src/contents.cpp
@@ -0,0 +1,557 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is packagekit-plugin code.
+ *
+ * The Initial Developer of the Original Code is
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#define MOZ_X11
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include <cairo-xlib.h>
+#include <dlfcn.h>
+#include <pango/pangocairo.h>
+#include <packagekit/pk-package-id.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <libgnome/gnome-desktop-item.h>
+#include <libgnomevfs/gnome-vfs.h>
+
+#include "plugin.h"
+
+#define APPLICATION_DIR "/usr/share/applications"
+
+#define MARGIN 5
+
+////////////////////////////////////////
+//
+// PkpContents class implementation
+//
+
+static std::vector<std::string>
+splitString(const char *str)
+{
+    std::vector<std::string> v;
+
+    if (str) {
+        char **split = g_strsplit(str, " ", -1);
+        for (char **s = split; *s; s++) {
+            char *stripped = strdup(*s);
+            g_strstrip(stripped);
+            v.push_back(stripped);
+            g_free(stripped);
+        }
+
+        g_strfreev(split);
+    }
+
+    return v;
+}
+
+PkpContents::PkpContents(const char *displayName,
+                         const char *packageNames,
+                         const char *desktopNames) :
+    mStatus(IN_PROGRESS),
+    mDisplayName(displayName),
+    mPackageNames(splitString(packageNames)),
+    mDesktopNames(splitString(desktopNames)),
+    mLayout(0),
+    mInstallPackageHandle(0)
+{
+    recheck();
+}
+
+PkpContents::~PkpContents()
+{
+    clearLayout();
+
+    if (mInstallPackageHandle != 0)
+        pkp_execute_command_async_cancel(mInstallPackageHandle);
+
+    while (!mClients.empty())
+        removeClient(mClients.front());
+}
+
+void PkpContents::recheck()
+{
+    mStatus = IN_PROGRESS;
+    mAvailableVersion = "";
+    mAvailablePackageName = "";
+    
+    for (std::vector<std::string>::iterator i = mPackageNames.begin(); i != mPackageNames.end(); i++) {
+        GError *error = NULL;
+        PkClient *client = pk_client_new();
+        if (!pk_client_resolve(client, "none", i->c_str(), &error)) {
+            g_warning("%s", error->message);
+            g_clear_error(&error);
+            g_object_unref(client);
+        } else {
+            g_signal_connect(client, "package", G_CALLBACK(onClientPackage), this);
+            g_signal_connect(client, "error-code", G_CALLBACK(onClientErrorCode), this);
+            g_signal_connect(client, "finished", G_CALLBACK(onClientFinished), this);
+            mClients.push_back(client);
+        }
+    }
+
+    findDesktopFile();
+
+    if (mClients.empty() && getStatus() == IN_PROGRESS)
+        setStatus(UNAVAILABLE);
+}
+
+void PkpContents::removeClient(PkClient *client)
+{
+    for (std::vector<PkClient *>::iterator i = mClients.begin(); i != mClients.end(); i++) {
+        if (*i == client) {
+            mClients.erase(i);
+            g_signal_handlers_disconnect_by_func(client, (void *)onClientPackage, this);
+            g_signal_handlers_disconnect_by_func(client, (void *)onClientErrorCode, this);
+            g_signal_handlers_disconnect_by_func(client, (void *)onClientFinished, this);
+            g_object_unref(client);
+            break;
+        }
+    }
+
+    if (mClients.empty()) {
+        if (getStatus() == IN_PROGRESS)
+            setStatus(UNAVAILABLE);
+    }
+}
+
+void
+PkpContents::setStatus(PackageStatus status)
+{
+    if (mStatus != status) {
+        mStatus = status;
+        clearLayout();
+        refresh();
+    }
+}
+
+void
+PkpContents::setAvailableVersion(const char *version)
+{
+    mAvailableVersion = version;
+    clearLayout();
+    refresh();
+}
+
+void
+PkpContents::setAvailablePackageName(const char *name)
+{
+    mAvailablePackageName = name;
+}
+
+void
+PkpContents::setInstalledVersion(const char *version)
+{
+    mInstalledVersion = version;
+    clearLayout();
+    refresh();
+}
+
+void
+PkpContents::clearLayout()
+{
+    if (mLayout) {
+        g_object_unref(mLayout);
+        mLayout = 0;
+    }
+}
+
+static void
+append_markup(GString *str, const char *format, ...)
+{
+    va_list vap;
+    
+    va_start(vap, format);
+    char *tmp = g_markup_vprintf_escaped(format, vap);
+    va_end(vap);
+
+    g_string_append(str, tmp);
+    g_free(tmp);
+}
+
+static guint32
+rgba_from_gdk_color(GdkColor *color)
+{
+    return (((color->red   >> 8) << 24) |
+            ((color->green >> 8) << 16) |
+            ((color->blue  >> 8) << 8) |
+            0xff);
+}
+
+static void
+set_source_from_rgba(cairo_t *cr,
+                     guint32  rgba)
+{
+    cairo_set_source_rgba(cr,
+                          ((rgba & 0xff000000) >> 24) / 255.,
+                          ((rgba & 0x00ff0000) >> 16) / 255.,
+                          ((rgba & 0x0000ff00) >> 8) / 255.,
+                          (rgba & 0x000000ff) / 255.);
+                          
+}
+
+/* Retrieve the system colors and fonts.
+ * This looks incredibly expensive .... to create a GtkWindow for
+ * every expose ... but actually it's only moderately expensive;
+ * Creating a GtkWindow is just normal GObject creation overhead --
+ * the extra expense beyond that will come when we actually create
+ * the window.
+ */
+static void
+get_style(PangoFontDescription **font_desc,
+          guint32               *foreground,
+          guint32               *background,
+          guint32               *link)
+{
+    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+    gtk_widget_ensure_style(window);
+
+    *foreground = rgba_from_gdk_color(&window->style->text[GTK_STATE_NORMAL]);
+    *background = rgba_from_gdk_color(&window->style->base[GTK_STATE_NORMAL]);
+
+    GdkColor link_color = { 0, 0, 0, 0xeeee };
+    GdkColor *tmp = NULL;
+
+    gtk_widget_style_get (GTK_WIDGET (window),
+                          "link-color", &tmp, NULL);
+    if (tmp != NULL) {
+        link_color = *tmp;
+        gdk_color_free(tmp);
+    }
+
+    *link = rgba_from_gdk_color(&link_color);
+
+    *font_desc = pango_font_description_copy(window->style->font_desc);
+   
+    gtk_widget_destroy(window);
+}
+
+void
+PkpContents::ensureLayout(cairo_t              *cr,
+                               PangoFontDescription *font_desc,
+                               guint32               link_color)
+{
+    GString *markup = g_string_new(NULL);
+    
+    if (mLayout)
+        return;
+    
+    mLayout = pango_cairo_create_layout(cr);
+    pango_layout_set_font_description(mLayout, font_desc);
+
+    switch (mStatus) {
+    case IN_PROGRESS:
+        append_markup(markup, _("Getting package information..."));
+        break;
+    case INSTALLED:
+        if (!mDesktopFile.empty())
+            append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Run %s</span>"),
+                          link_color >> 8,
+                          mDisplayName.c_str());
+        else
+            append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
+        if (!mInstalledVersion.empty())
+            append_markup(markup, _("\n<small>Installed version: %s</small>"), mInstalledVersion.c_str());
+        break;
+    case AVAILABLE:
+        append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Install %s Now</span>"),
+                      link_color >> 8,
+                      mDisplayName.c_str());
+        append_markup(markup, _("\n<small>Version: %s</small>"), mAvailableVersion.c_str());
+        break;
+    case UNAVAILABLE:
+        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
+        append_markup(markup, _("\n<small>No packages found for your system</small>"));
+        break;
+    case INSTALLING:
+        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
+        append_markup(markup, _("\n<small>Installing...</small>"));
+        break;
+    }
+
+    pango_layout_set_markup(mLayout, markup->str, -1);
+    g_string_free(markup, TRUE);
+}
+
+void
+PkpContents::refresh()
+{
+    mPlugin->refresh();
+}
+                               
+void
+PkpContents::setPlugin(PkpPluginInstance *plugin)
+{
+    mPlugin = plugin;
+}
+
+void
+PkpContents::draw(cairo_t *cr)
+{
+    guint32 foreground, background, link;
+    PangoFontDescription *font_desc;
+    
+    get_style(&font_desc, &foreground, &background, &link);
+
+    set_source_from_rgba(cr, background);
+    cairo_rectangle(cr, mPlugin->getX(), mPlugin->getY(), mPlugin->getWidth(), mPlugin->getHeight());
+    cairo_fill(cr);
+
+    cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
+    cairo_rectangle(cr, mPlugin->getX() + 0.5, mPlugin->getY() + 0.5, mPlugin->getWidth() - 1, mPlugin->getHeight() - 1);
+    cairo_set_line_width(cr, 1);
+    cairo_stroke(cr);
+
+    ensureLayout(cr, font_desc, link);
+    int width, height;
+    pango_layout_get_pixel_size(mLayout, &width, &height);
+
+    cairo_move_to(cr, mPlugin->getX() + MARGIN, mPlugin->getY() + MARGIN);
+    set_source_from_rgba(cr, foreground);
+    pango_cairo_show_layout(cr, mLayout);
+}
+
+void
+PkpContents::buttonPress(int x, int y, Time time)
+{
+}
+
+void
+PkpContents::buttonRelease(int x, int y, Time time)
+{
+    if (!mDesktopFile.empty())
+        runApplication();
+    else if (!mAvailablePackageName.empty())
+        installPackage();
+}
+
+void
+PkpContents::motion(int x, int y)
+{
+}
+
+void
+PkpContents::enter(int x, int y)
+{
+}
+
+void
+PkpContents::leave(int x, int y)
+{
+}
+
+static guint32
+get_server_timestamp()
+{
+    GtkWidget *invisible = gtk_invisible_new();
+    gtk_widget_realize(invisible);
+    return gdk_x11_get_server_time(invisible->window);
+    gtk_widget_destroy(invisible);
+}
+
+static gboolean
+validate_name(const char *name)
+{
+    const char *p;
+    
+    for (p = name; *p; p++) {
+        char c = *p;
+        
+        if (!((c >= 'A' && c <= 'Z') ||
+              (c >= 'a' && c <= 'z') ||
+              (c >= '0' && c <= '9') ||
+              (c == '.') ||
+              (c == '_') ||
+              (c == '-')))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+void
+PkpContents::findDesktopFile()
+{
+    for (std::vector<std::string>::iterator i = mDesktopNames.begin(); i != mDesktopNames.end(); i++) {
+        if (!validate_name(i->c_str())) {
+            g_warning("Bad desktop name: '%s'", i->c_str());
+            continue;
+        }
+        
+        char *filename = g_strconcat(i->c_str(), ".desktop", NULL);
+        char *path = g_build_filename(APPLICATION_DIR, filename, NULL);
+        g_free(filename);
+
+        if (g_file_test(path, G_FILE_TEST_EXISTS)) {
+            mDesktopFile = path;
+            break;
+        }
+
+        g_free(path);
+    }
+
+    if (!mDesktopFile.empty())
+        setStatus(INSTALLED);
+}
+                  
+void
+PkpContents::runApplication (void)
+{
+    GError *error = NULL;
+    
+    /* This is idempotent and fairly cheap, so do it here to avoid initializing
+     * gnome-vfs on plugin startup
+     */
+    gnome_vfs_init();
+
+    if (mDesktopFile.c_str() == 0) {
+        g_warning("Didn't find application to launch");
+        return;
+    }
+
+    GnomeDesktopItem *item = gnome_desktop_item_new_from_file(mDesktopFile.c_str(), GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS, &error);
+    if (!item) {
+        g_warning("%s\n", error->message);
+        g_clear_error(&error);
+        gnome_desktop_item_unref(item);
+        return;
+    }
+
+    guint32 launch_time = gtk_get_current_event_time();
+    if (launch_time == GDK_CURRENT_TIME)
+        launch_time = get_server_timestamp();
+
+    if (!gnome_desktop_item_launch(item, NULL, (GnomeDesktopItemLaunchFlags)0, &error)) {
+        g_warning("%s\n", error->message);
+        g_clear_error(&error);
+        gnome_desktop_item_unref(item);
+        return;
+    }
+}
+
+void
+PkpContents::installPackage (void)
+{
+    if (mAvailablePackageName.empty()) {
+        g_warning("No available package to install");
+        return;
+    }
+
+    if (mInstallPackageHandle != 0) {
+        g_warning("Already installing package");
+        return;
+    }
+
+    char *argv[3];
+    argv[0] = (char *)"gpk-install-package";
+    argv[1] = (char *)mAvailablePackageName.c_str();
+    argv[2] = 0;
+
+    mInstallPackageHandle = pkp_execute_command_async(argv, onInstallFinished, this);
+    setStatus(INSTALLING);
+}
+
+void
+PkpContents::onClientPackage(PkClient	  *client,
+                                  PkInfoEnum	   info,
+                                  const gchar	   *package_id,
+                                  const gchar	   *summary,
+                                  PkpContents *contents)
+{
+    fprintf(stderr, "package: %d %s %s\n", info, package_id, summary);
+
+    PkPackageId *id = pk_package_id_new_from_string(package_id);
+    
+    if (info == PK_INFO_ENUM_AVAILABLE) {
+        if (contents->getStatus() != INSTALLED)
+            contents->setStatus(AVAILABLE);
+        contents->setAvailableVersion(id->version);
+        contents->setAvailablePackageName(id->name);
+    } else if (info == PK_INFO_ENUM_INSTALLED) {
+        contents->setStatus(INSTALLED);
+        contents->setInstalledVersion(id->version);
+    }
+    
+    pk_package_id_free(id);
+}
+
+void
+PkpContents::onClientErrorCode(PkClient	     *client,
+                                    PkErrorCodeEnum   code,
+                                    const gchar	     *details,
+                                    PkpContents *contents)
+{
+    fprintf(stderr, "error code: %d %s\n", code, details);
+    contents->removeClient(client);
+}
+
+void
+PkpContents::onClientFinished(PkClient	    *client,
+                              PkExitEnum     exit,
+                              guint	     runtime,
+                              PkpContents   *contents)
+    
+{
+    fprintf(stderr, "finished: %d\n", exit);
+    contents->removeClient(client);
+}
+    
+void
+PkpContents::onInstallFinished(GError        *error,
+                               int            status,
+                               const char    *output,
+                               void          *callback_data)
+{
+    PkpContents *contents = (PkpContents *)callback_data;
+
+    contents->mInstallPackageHandle = 0;
+    
+    if (error) {
+        g_warning("Error occurred during install: %s", error->message);
+    }
+
+    if (status != 0) {
+        g_warning("gpk-install-command exited with non-zero status %d", status);
+    }
+
+    contents->recheck();
+}
diff --git a/src/contents.h b/src/contents.h
new file mode 100644
index 0000000..388b246
--- /dev/null
+++ b/src/contents.h
@@ -0,0 +1,130 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is packagekit-plugin code.
+ *
+ * The Initial Developer of the Original Code is
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+#ifndef __CONTENTS_H__
+#define __CONTENTS_H__
+
+#include <X11/Xlib.h>
+#include <pango/pango.h>
+#include <packagekit/pk-client.h>
+#include <cairo.h>
+#include <gtk/gtk.h>
+
+#include <string>
+#include <vector>
+
+#include "util.h"
+
+class PkpPluginInstance;
+
+enum PackageStatus { IN_PROGRESS, INSTALLED, AVAILABLE, UNAVAILABLE, INSTALLING };
+
+class PkpContents
+{
+public:
+    PkpContents(const char *displayName, const char *packageNames, const char *desktopNames);
+    virtual ~PkpContents();
+
+    void setPlugin(PkpPluginInstance *plugin);
+    
+    void draw(cairo_t *cr);
+    void buttonPress(int x, int y, Time time);
+    void buttonRelease(int x, int y, Time time);
+    void motion(int x, int y);
+    void enter(int x, int y);
+    void leave(int x, int y);
+
+private:
+    void recheck();
+    void findDesktopFile();
+    void runApplication();
+    void installPackage();
+    
+    void setStatus(PackageStatus status);
+    PackageStatus getStatus() { return mStatus; }
+    void setAvailableVersion(const char *version);
+    void setAvailablePackageName(const char *name);
+    void setInstalledVersion(const char *version);
+
+    void ensureLayout(cairo_t *cr,
+                      PangoFontDescription *font_desc,
+                      guint32 link_color);
+    void clearLayout();
+    void refresh();
+    
+    void removeClient(PkClient *client);
+    
+    static void onClientPackage(PkClient 	   *client,
+                                PkInfoEnum	    info,
+                                const gchar	   *package_id,
+                                const gchar	   *summary,
+                                PkpContents        *contents);
+    static void onClientErrorCode(PkClient	   *client,
+                                  PkErrorCodeEnum  code,
+                                  const gchar	   *details,
+                                  PkpContents *contents);
+    static void onClientFinished(PkClient	  *client,
+                                 PkExitEnum	   exit,
+                                 guint		   runtime,
+                                 PkpContents      *contents);
+    static void onInstallFinished(GError        *error,
+                                  int            status,
+                                  const char    *output,
+                                  void          *callback_data);
+
+    PkpPluginInstance *mPlugin;
+    PackageStatus mStatus;
+    std::string mAvailableVersion;
+    std::string mAvailablePackageName;
+    std::string mInstalledVersion;
+    std::string mDesktopFile;
+
+    std::string mDisplayName;
+    std::vector<std::string> mPackageNames;
+    std::vector<std::string> mDesktopNames;
+    
+    PangoLayout *mLayout;
+    int mLinkStart;
+    int mLinkEnd;
+
+    std::vector<PkClient *> mClients;
+
+    PkpExecuteCommandAsyncHandle *mInstallPackageHandle;
+};
+
+#endif // __CONTENTS_H__
diff --git a/src/plugin.cpp b/src/plugin.cpp
index b1e389d..6e76190 100644
--- a/src/plugin.cpp
+++ b/src/plugin.cpp
@@ -21,6 +21,8 @@
  *
  * Contributor(s):
  *
+ * Red Hat, Inc.
+ *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -54,15 +56,11 @@
 
 #include "plugin.h"
 
-#define APPLICATION_DIR "/usr/share/applications"
-
 #define MIME_TYPES_HANDLED  "application/x-packagekit-plugin"
 #define PLUGIN_NAME         "Plugin for Installing Applications"
 #define MIME_TYPES_DESCRIPTION  MIME_TYPES_HANDLED":bsc:"PLUGIN_NAME
 #define PLUGIN_DESCRIPTION  PLUGIN_NAME
 
-#define MARGIN 5
-
 char* NPP_GetMIMEDescription(void)
 {
     return (char *)(MIME_TYPES_DESCRIPTION);
@@ -70,6 +68,10 @@ char* NPP_GetMIMEDescription(void)
 
 static void *module_handle = 0;
 
+/////////////////////////////////////
+// general initialization and shutdown
+//
+
 /* If our dependent libraries like libpackagekit get unloaded, bad stuff
  * happens (they may have registered GLib types and so forth) so we need
  * to keep them around. The (GNU extension) RTLD_NODELETE seems useful
@@ -99,9 +101,6 @@ make_module_resident()
     /* the module will never be closed */
 }
 
-/////////////////////////////////////
-// general initialization and shutdown
-//
 NPError NS_PluginInitialize()
 {
     if (module_handle != 0) /* Already initialized */
@@ -161,7 +160,7 @@ nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStru
             desktopNames = aCreateDataStruct->argv[i];
     }
       
-    nsPluginInstance * plugin = new nsPluginInstance(aCreateDataStruct->instance, displayName, packageNames, desktopNames);
+    PkpPluginInstance * plugin = new PkpPluginInstance(aCreateDataStruct->instance, displayName, packageNames, desktopNames);
   
     NPN_SetValue(aCreateDataStruct->instance,
                  NPPVpluginWindowBool, (void *)FALSE);
@@ -172,7 +171,7 @@ nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStru
 void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)
 {
     if(aPlugin)
-        delete (nsPluginInstance *)aPlugin;
+        delete (PkpPluginInstance *)aPlugin;
 }
 
 ////////////////////////////////////////
@@ -180,49 +179,24 @@ void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)
 // nsPluginInstance class implementation
 //
 
-static std::vector<std::string>
-splitString(const char *str)
-{
-    std::vector<std::string> v;
-
-    if (str) {
-        char **split = g_strsplit(str, " ", -1);
-        for (char **s = split; *s; s++) {
-            char *stripped = strdup(*s);
-            g_strstrip(stripped);
-            v.push_back(stripped);
-            g_free(stripped);
-        }
-
-        g_strfreev(split);
-    }
-
-    return v;
-}
-
-nsPluginInstance::nsPluginInstance(NPP         aInstance,
-                                   const char *displayName,
-                                   const char *packageNames,
-                                   const char *desktopNames) :
+PkpPluginInstance::PkpPluginInstance(NPP         aInstance,
+                                     const char *displayName,
+                                     const char *packageNames,
+                                     const char *desktopNames) :
     nsPluginInstanceBase(),
     mInstance(aInstance),
     mInitialized(FALSE),
-    mStatus(IN_PROGRESS),
-    mDisplayName(displayName),
-    mPackageNames(splitString(packageNames)),
-    mDesktopNames(splitString(desktopNames)),
-    mWindow(0),
-    mLayout(0),
-    mInstallPackageHandle(0)
+    mContents(displayName, packageNames, desktopNames),
+    mWindow(0)
 {
-    recheck();
+    mContents.setPlugin(this);
 }
 
-nsPluginInstance::~nsPluginInstance()
+PkpPluginInstance::~PkpPluginInstance()
 {
 }
 
-NPBool nsPluginInstance::init(NPWindow* aWindow)
+NPBool PkpPluginInstance::init(NPWindow* aWindow)
 {
     if(aWindow == NULL)
         return FALSE;
@@ -233,66 +207,12 @@ NPBool nsPluginInstance::init(NPWindow* aWindow)
     return mInitialized;
 }
 
-void nsPluginInstance::recheck()
+void PkpPluginInstance::shut()
 {
-    mStatus = IN_PROGRESS;
-    mAvailableVersion = "";
-    mAvailablePackageName = "";
-    
-    for (std::vector<std::string>::iterator i = mPackageNames.begin(); i != mPackageNames.end(); i++) {
-        GError *error = NULL;
-        PkClient *client = pk_client_new();
-        if (!pk_client_resolve(client, "none", i->c_str(), &error)) {
-            g_warning("%s", error->message);
-            g_clear_error(&error);
-            g_object_unref(client);
-        } else {
-            g_signal_connect(client, "package", G_CALLBACK(onClientPackage), this);
-            g_signal_connect(client, "error-code", G_CALLBACK(onClientErrorCode), this);
-            g_signal_connect(client, "finished", G_CALLBACK(onClientFinished), this);
-            mClients.push_back(client);
-        }
-    }
-
-    findDesktopFile();
-
-    if (mClients.empty() && getStatus() == IN_PROGRESS)
-        setStatus(UNAVAILABLE);
-}
-
-void nsPluginInstance::removeClient(PkClient *client)
-{
-    for (std::vector<PkClient *>::iterator i = mClients.begin(); i != mClients.end(); i++) {
-        if (*i == client) {
-            mClients.erase(i);
-            g_signal_handlers_disconnect_by_func(client, (void *)onClientPackage, this);
-            g_signal_handlers_disconnect_by_func(client, (void *)onClientErrorCode, this);
-            g_signal_handlers_disconnect_by_func(client, (void *)onClientFinished, this);
-            g_object_unref(client);
-            break;
-        }
-    }
-
-    if (mClients.empty()) {
-        if (getStatus() == IN_PROGRESS)
-            setStatus(UNAVAILABLE);
-    }
-}
-
-void nsPluginInstance::shut()
-{
-    clearLayout();
-
-    if (mInstallPackageHandle != 0)
-        pkp_execute_command_async_cancel(mInstallPackageHandle);
-
-    while (!mClients.empty())
-        removeClient(mClients.front());
-    
     mInitialized = FALSE;
 }
 
-NPError nsPluginInstance::GetValue(NPPVariable aVariable, void *aValue)
+NPError PkpPluginInstance::GetValue(NPPVariable aVariable, void *aValue)
 {
     NPError err = NPERR_NO_ERROR;
     switch (aVariable) {
@@ -308,11 +228,8 @@ NPError nsPluginInstance::GetValue(NPPVariable aVariable, void *aValue)
 
 }
 
-NPError nsPluginInstance::SetWindow(NPWindow* aWindow)
+NPError PkpPluginInstance::SetWindow(NPWindow* aWindow)
 {
-    if (aWindow == NULL || (Window)aWindow->window != mWindow)
-        clearLayout();
-    
     if (aWindow == NULL)
         return FALSE;
 
@@ -332,166 +249,7 @@ NPError nsPluginInstance::SetWindow(NPWindow* aWindow)
 }
 
 void
-nsPluginInstance::setStatus(PackageStatus status)
-{
-    if (mStatus != status) {
-        mStatus = status;
-        clearLayout();
-        refresh();
-    }
-}
-
-void
-nsPluginInstance::setAvailableVersion(const char *version)
-{
-    mAvailableVersion = version;
-    clearLayout();
-    refresh();
-}
-
-void
-nsPluginInstance::setAvailablePackageName(const char *name)
-{
-    mAvailablePackageName = name;
-}
-
-void
-nsPluginInstance::setInstalledVersion(const char *version)
-{
-    mInstalledVersion = version;
-    clearLayout();
-    refresh();
-}
-
-void
-nsPluginInstance::clearLayout()
-{
-    if (mLayout) {
-        g_object_unref(mLayout);
-        mLayout = 0;
-    }
-}
-
-static void
-append_markup(GString *str, const char *format, ...)
-{
-    va_list vap;
-    
-    va_start(vap, format);
-    char *tmp = g_markup_vprintf_escaped(format, vap);
-    va_end(vap);
-
-    g_string_append(str, tmp);
-    g_free(tmp);
-}
-
-static guint32
-rgba_from_gdk_color(GdkColor *color)
-{
-    return (((color->red   >> 8) << 24) |
-            ((color->green >> 8) << 16) |
-            ((color->blue  >> 8) << 8) |
-            0xff);
-}
-
-static void
-set_source_from_rgba(cairo_t *cr,
-                     guint32  rgba)
-{
-    cairo_set_source_rgba(cr,
-                          ((rgba & 0xff000000) >> 24) / 255.,
-                          ((rgba & 0x00ff0000) >> 16) / 255.,
-                          ((rgba & 0x0000ff00) >> 8) / 255.,
-                          (rgba & 0x000000ff) / 255.);
-                          
-}
-
-/* Retrieve the system colors and fonts.
- * This looks incredibly expensive .... to create a GtkWindow for
- * every expose ... but actually it's only moderately expensive;
- * Creating a GtkWindow is just normal GObject creation overhead --
- * the extra expense beyond that will come when we actually create
- * the window.
- */
-static void
-get_style(PangoFontDescription **font_desc,
-          guint32               *foreground,
-          guint32               *background,
-          guint32               *link)
-{
-    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-
-    gtk_widget_ensure_style(window);
-
-    *foreground = rgba_from_gdk_color(&window->style->text[GTK_STATE_NORMAL]);
-    *background = rgba_from_gdk_color(&window->style->base[GTK_STATE_NORMAL]);
-
-    GdkColor link_color = { 0, 0, 0, 0xeeee };
-    GdkColor *tmp = NULL;
-
-    gtk_widget_style_get (GTK_WIDGET (window),
-                          "link-color", &tmp, NULL);
-    if (tmp != NULL) {
-        link_color = *tmp;
-        gdk_color_free(tmp);
-    }
-
-    *link = rgba_from_gdk_color(&link_color);
-
-    *font_desc = pango_font_description_copy(window->style->font_desc);
-   
-    gtk_widget_destroy(window);
-}
-
-void
-nsPluginInstance::ensureLayout(cairo_t              *cr,
-                               PangoFontDescription *font_desc,
-                               guint32               link_color)
-{
-    GString *markup = g_string_new(NULL);
-    
-    if (mLayout)
-        return;
-    
-    mLayout = pango_cairo_create_layout(cr);
-    pango_layout_set_font_description(mLayout, font_desc);
-
-    switch (mStatus) {
-    case IN_PROGRESS:
-        append_markup(markup, _("Getting package information..."));
-        break;
-    case INSTALLED:
-        if (!mDesktopFile.empty())
-            append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Run %s</span>"),
-                          link_color >> 8,
-                          mDisplayName.c_str());
-        else
-            append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
-        if (!mInstalledVersion.empty())
-            append_markup(markup, _("\n<small>Installed version: %s</small>"), mInstalledVersion.c_str());
-        break;
-    case AVAILABLE:
-        append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Install %s Now</span>"),
-                      link_color >> 8,
-                      mDisplayName.c_str());
-        append_markup(markup, _("\n<small>Version: %s</small>"), mAvailableVersion.c_str());
-        break;
-    case UNAVAILABLE:
-        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
-        append_markup(markup, _("\n<small>No packages found for your system</small>"));
-        break;
-    case INSTALLING:
-        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
-        append_markup(markup, _("\n<small>Installing...</small>"));
-        break;
-    }
-
-    pango_layout_set_markup(mLayout, markup->str, -1);
-    g_string_free(markup, TRUE);
-}
-
-void
-nsPluginInstance::refresh()
+PkpPluginInstance::refresh()
 {
     NPRect rect;
 
@@ -505,271 +263,60 @@ nsPluginInstance::refresh()
     NPN_InvalidateRect(mInstance, &rect);
 }
                                
-void
-nsPluginInstance::handleGraphicsExpose(XGraphicsExposeEvent *xev)
-{
-    cairo_surface_t *surface = cairo_xlib_surface_create (mDisplay, xev->drawable, mVisual, mX + mWidth, mY + mHeight);
-    cairo_t *cr = cairo_create(surface);
-    guint32 foreground, background, link;
-    PangoFontDescription *font_desc;
-
-    get_style(&font_desc, &foreground, &background, &link);
-
-    cairo_rectangle(cr,xev->x, xev->y, xev->width, xev->height);
-    cairo_clip(cr);
-
-    set_source_from_rgba(cr, background);
-    cairo_rectangle(cr, mX, mY, mWidth, mHeight);
-    cairo_fill(cr);
-
-    cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
-    cairo_rectangle(cr, mX + 0.5, mY + 0.5, mWidth - 1, mHeight - 1);
-    cairo_set_line_width(cr, 1);
-    cairo_stroke(cr);
-
-    ensureLayout(cr, font_desc, link);
-    int width, height;
-    pango_layout_get_pixel_size(mLayout, &width, &height);
-
-    if (width < mWidth - MARGIN * 2 && height < mHeight - MARGIN * 2) {
-        cairo_move_to(cr, mX + MARGIN, mY + MARGIN);
-        set_source_from_rgba(cr, foreground);
-        pango_cairo_show_layout(cr, mLayout);
-    }
-    
-    cairo_surface_destroy(surface);
-}
-
-void
-nsPluginInstance::handleButtonPress(XButtonEvent *xev)
-{
-}
-
-void
-nsPluginInstance::handleButtonRelease(XButtonEvent *xev)
-{
-    if (!mDesktopFile.empty())
-        runApplication();
-    else if (!mAvailablePackageName.empty())
-        installPackage();
-}
-
-void
-nsPluginInstance::handleMotionNotify(XMotionEvent *xev)
-{
-}
-
-void
-nsPluginInstance::handleEnterNotify(XCrossingEvent *xev)
-{
-}
-
-void
-nsPluginInstance::handleLeaveNotify(XCrossingEvent *xev)
-{
-}
-
 uint16
-nsPluginInstance::HandleEvent(void *event)
+PkpPluginInstance::HandleEvent(void *event)
 {
     XEvent *xev = (XEvent *)event;
 
     switch (xev->xany.type) {
     case GraphicsExpose:
-        handleGraphicsExpose((XGraphicsExposeEvent *)event);
-        return 1;
-    case ButtonPress:
-        handleButtonPress((XButtonEvent *)event);
-        return 1;
-    case ButtonRelease:
-        handleButtonRelease((XButtonEvent *)event);
-        return 1;
-    case MotionNotify:
-        handleMotionNotify((XMotionEvent *)event);
-        return 1;
-    case EnterNotify:
-        handleEnterNotify((XCrossingEvent *)event);
-        return 1;
-    case LeaveNotify:
-        handleLeaveNotify((XCrossingEvent *)event);
-        return 1;
-    }
+        {
+            XGraphicsExposeEvent *xge = (XGraphicsExposeEvent *)event;
 
-    return 0;
-}
+            cairo_surface_t *surface = cairo_xlib_surface_create (mDisplay, xge->drawable, mVisual, mX + mWidth, mY + mHeight);
+            cairo_t *cr = cairo_create(surface);
 
-static guint32
-get_server_timestamp()
-{
-    GtkWidget *invisible = gtk_invisible_new();
-    gtk_widget_realize(invisible);
-    return gdk_x11_get_server_time(invisible->window);
-    gtk_widget_destroy(invisible);
-}
-
-static gboolean
-validate_name(const char *name)
-{
-    const char *p;
-    
-    for (p = name; *p; p++) {
-        char c = *p;
-        
-        if (!((c >= 'A' && c <= 'Z') ||
-              (c >= 'a' && c <= 'z') ||
-              (c >= '0' && c <= '9') ||
-              (c == '.') ||
-              (c == '_') ||
-              (c == '-')))
-            return FALSE;
-    }
+            cairo_rectangle(cr, xge->x, xge->y, xge->width, xge->height);
+            cairo_clip(cr);
 
-    return TRUE;
-}
+            mContents.draw(cr);
 
-void
-nsPluginInstance::findDesktopFile()
-{
-    for (std::vector<std::string>::iterator i = mDesktopNames.begin(); i != mDesktopNames.end(); i++) {
-        if (!validate_name(i->c_str())) {
-            g_warning("Bad desktop name: '%s'", i->c_str());
-            continue;
+            cairo_destroy(cr);
+            cairo_surface_destroy(surface);
+            
+            return 1;
         }
-        
-        char *filename = g_strconcat(i->c_str(), ".desktop", NULL);
-        char *path = g_build_filename(APPLICATION_DIR, filename, NULL);
-        g_free(filename);
-
-        if (g_file_test(path, G_FILE_TEST_EXISTS)) {
-            mDesktopFile = path;
-            break;
+    case ButtonPress:
+        {
+            XButtonEvent *xbe = (XButtonEvent *)event;
+            mContents.buttonPress(xbe->x, xbe->y, xbe->time);
+            return 1;
+        }
+    case ButtonRelease:
+        {
+            XButtonEvent *xbe = (XButtonEvent *)event;
+            mContents.buttonRelease(xbe->x, xbe->y, xbe->time);
+            return 1;
+        }
+    case MotionNotify:
+        {
+            XMotionEvent *xme = (XMotionEvent *)event;
+            mContents.motion(xme->x, xme->y);
+            return 1;
+        }
+    case EnterNotify:
+        {
+            XCrossingEvent *xce = (XCrossingEvent *)event;
+            mContents.enter(xce->x, xce->y);
+            return 1;
+        }
+    case LeaveNotify:
+        {
+            XCrossingEvent *xce = (XCrossingEvent *)event;
+            mContents.leave(xce->x, xce->y);
+            return 1;
         }
-
-        g_free(path);
-    }
-
-    if (!mDesktopFile.empty())
-        setStatus(INSTALLED);
-}
-                  
-void
-nsPluginInstance::runApplication (void)
-{
-    GError *error = NULL;
-    
-    /* This is idempotent and fairly cheap, so do it here to avoid initializing
-     * gnome-vfs on plugin startup
-     */
-    gnome_vfs_init();
-
-    if (mDesktopFile.c_str() == 0) {
-        g_warning("Didn't find application to launch");
-        return;
-    }
-
-    GnomeDesktopItem *item = gnome_desktop_item_new_from_file(mDesktopFile.c_str(), GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS, &error);
-    if (!item) {
-        g_warning("%s\n", error->message);
-        g_clear_error(&error);
-        gnome_desktop_item_unref(item);
-        return;
-    }
-
-    guint32 launch_time = gtk_get_current_event_time();
-    if (launch_time == GDK_CURRENT_TIME)
-        launch_time = get_server_timestamp();
-
-    if (!gnome_desktop_item_launch(item, NULL, (GnomeDesktopItemLaunchFlags)0, &error)) {
-        g_warning("%s\n", error->message);
-        g_clear_error(&error);
-        gnome_desktop_item_unref(item);
-        return;
-    }
-}
-
-void
-nsPluginInstance::installPackage (void)
-{
-    if (mAvailablePackageName.empty()) {
-        g_warning("No available package to install");
-        return;
-    }
-
-    if (mInstallPackageHandle != 0) {
-        g_warning("Already installing package");
-        return;
-    }
-
-    char *argv[3];
-    argv[0] = (char *)"gpk-install-package";
-    argv[1] = (char *)mAvailablePackageName.c_str();
-    argv[2] = 0;
-
-    mInstallPackageHandle = pkp_execute_command_async(argv, onInstallFinished, this);
-    setStatus(INSTALLING);
-}
-
-void
-nsPluginInstance::onClientPackage(PkClient	  *client,
-                                  PkInfoEnum	   info,
-                                  const gchar	   *package_id,
-                                  const gchar	   *summary,
-                                  nsPluginInstance *instance)
-{
-    fprintf(stderr, "package: %d %s %s\n", info, package_id, summary);
-
-    PkPackageId *id = pk_package_id_new_from_string(package_id);
-    
-    if (info == PK_INFO_ENUM_AVAILABLE) {
-        if (instance->getStatus() != INSTALLED)
-            instance->setStatus(AVAILABLE);
-        instance->setAvailableVersion(id->version);
-        instance->setAvailablePackageName(id->name);
-    } else if (info == PK_INFO_ENUM_INSTALLED) {
-        instance->setStatus(INSTALLED);
-        instance->setInstalledVersion(id->version);
-    }
-    
-    pk_package_id_free(id);
-}
-
-void
-nsPluginInstance::onClientErrorCode(PkClient	     *client,
-                                    PkErrorCodeEnum   code,
-                                    const gchar	     *details,
-                                    nsPluginInstance *instance)
-{
-    fprintf(stderr, "error code: %d %s\n", code, details);
-    instance->removeClient(client);
-}
-
-void
-nsPluginInstance::onClientFinished(PkClient	    *client,
-                                   PkExitEnum	     exit,
-                                   guint	     runtime,
-                                   nsPluginInstance *instance)
-{
-    fprintf(stderr, "finished: %d\n", exit);
-    instance->removeClient(client);
-}
-    
-void
-nsPluginInstance::onInstallFinished(GError        *error,
-                                    int            status,
-                                    const char    *output,
-                                    void          *callback_data)
-{
-    nsPluginInstance *instance = (nsPluginInstance *)callback_data;
-
-    instance->mInstallPackageHandle = 0;
-    
-    if (error) {
-        g_warning("Error occurred during install: %s", error->message);
-    }
-
-    if (status != 0) {
-        g_warning("gpk-install-command exited with non-zero status %d", status);
     }
 
-    instance->recheck();
+    return 0;
 }
diff --git a/src/plugin.h b/src/plugin.h
index 59c1255..39d600b 100644
--- a/src/plugin.h
+++ b/src/plugin.h
@@ -39,24 +39,16 @@
 #define __PLUGIN_H__
 
 #include <X11/Xlib.h>
-#include <pango/pango.h>
-#include <packagekit/pk-client.h>
-#include <cairo.h>
-#include <gtk/gtk.h>
-
-#include <string>
-#include <vector>
 
+#include "contents.h"
 #include "pluginbase.h"
 #include "util.h"
 
-enum PackageStatus { IN_PROGRESS, INSTALLED, AVAILABLE, UNAVAILABLE, INSTALLING };
-
-class nsPluginInstance : public nsPluginInstanceBase
+class PkpPluginInstance : public nsPluginInstanceBase
 {
 public:
-    nsPluginInstance(NPP aInstance, const char *displayName, const char *packageNames, const char *desktopNames);
-    virtual ~nsPluginInstance();
+    PkpPluginInstance(NPP aInstance, const char *displayName, const char *packageNames, const char *desktopNames);
+    virtual ~PkpPluginInstance();
 
     NPBool init(NPWindow* aWindow);
     void shut();
@@ -65,63 +57,19 @@ public:
     NPError SetWindow(NPWindow* aWindow);
     uint16 HandleEvent(void *event);
 
-    void setStatus(PackageStatus status);
-    PackageStatus getStatus() { return mStatus; }
-    void setAvailableVersion(const char *version);
-    void setAvailablePackageName(const char *name);
-    void setInstalledVersion(const char *version);
-
-private:
-    void recheck();
-    void findDesktopFile();
-    void runApplication();
-    void installPackage();
-    
-    void ensureLayout(cairo_t *cr,
-                      PangoFontDescription *font_desc,
-                      guint32 link_color);
-    void clearLayout();
     void refresh();
-    
-    void handleGraphicsExpose(XGraphicsExposeEvent *xev);
-    void handleButtonPress(XButtonEvent *xev);
-    void handleButtonRelease(XButtonEvent *xev);
-    void handleMotionNotify(XMotionEvent *xev);
-    void handleEnterNotify(XCrossingEvent *xev);
-    void handleLeaveNotify(XCrossingEvent *xev);
 
-    void removeClient(PkClient *client);
-    
-    static void onClientPackage(PkClient 	   *client,
-                                PkInfoEnum	    info,
-                                const gchar	   *package_id,
-                                const gchar	   *summary,
-                                nsPluginInstance   *instance);
-    static void onClientErrorCode(PkClient	   *client,
-                                  PkErrorCodeEnum  code,
-                                  const gchar	   *details,
-                                  nsPluginInstance *instance);
-    static void onClientFinished(PkClient	  *client,
-                                 PkExitEnum	   exit,
-                                 guint		   runtime,
-                                 nsPluginInstance *instance);
-    static void onInstallFinished(GError        *error,
-                                  int            status,
-                                  const char    *output,
-                                  void          *callback_data);
+    int getX() { return mX; }
+    int getY() { return mY; }
+    int getWidth() { return mWidth; }
+    int getHeight() { return mHeight; }
     
+private:
     NPP mInstance;
     NPBool mInitialized;
-    PackageStatus mStatus;
-    std::string mAvailableVersion;
-    std::string mAvailablePackageName;
-    std::string mInstalledVersion;
-    std::string mDesktopFile;
 
-    std::string mDisplayName;
-    std::vector<std::string> mPackageNames;
-    std::vector<std::string> mDesktopNames;
-    
+    PkpContents mContents;
+
     Window mWindow;
     Display *mDisplay;
     int mX, mY;
@@ -129,14 +77,6 @@ private:
     Visual* mVisual;
     Colormap mColormap;
     unsigned int mDepth;
-  
-    PangoLayout *mLayout;
-    int mLinkStart;
-    int mLinkEnd;
-
-    std::vector<PkClient *> mClients;
-
-    PkpExecuteCommandAsyncHandle *mInstallPackageHandle;
 };
 
 #endif // __PLUGIN_H__
diff --git a/src/util.cpp b/src/util.cpp
index de8629e..01a24c8 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -12,7 +12,7 @@
  * for the specific language governing rights and limitations under the
  * License.
  *
- * The Original Code is PackageKit plugin code.
+ * The Original Code is packagekit-plugin code.
  *
  * The Initial Developer of the Original Code is
  * Red Hat, Inc.
diff --git a/src/util.h b/src/util.h
index daff9a0..d36b84b 100644
--- a/src/util.h
+++ b/src/util.h
@@ -12,7 +12,7 @@
  * for the specific language governing rights and limitations under the
  * License.
  *
- * The Original Code is PackageKit plugin code.
+ * The Original Code is packagekit-plugin code.
  *
  * The Initial Developer of the Original Code is
  * Red Hat, Inc.
commit 08e2ce26abfd519c6aec98c4e4ad475f54dfd656
Author: Owen W. Taylor <otaylor at fishsoup.net>
Date:   Thu Jul 24 11:56:37 2008 -0400

    Initial import; basically works

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a05c9f7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+COPYING
+INSTALL
+LIBS
+aclocal.m4
+autom4te.cache
+config.*
+configure
+depcomp
+install-sh
+intltool-*
+libtool
+ltmain.sh
+missing
+Makefile
+Makefile.in
+stamp-h1
+.deps
+.libs
+*.lo
+*.la
+po/Makefile*
+po/POTFILES
+po/stamp-it
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..e69de29
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..4fd3ca1
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,35 @@
+SUBDIRS=po .
+
+plugindir=$(libdir)/mozilla/plugins
+plugin_LTLIBRARIES = packagekit-plugin.la
+
+packagekit_plugin_la_LDFLAGS = -rpath $(plugindir) -module -avoid-version -no-undefined
+packagekit_plugin_la_CPPFLAGS =			\
+	$(PLUGIN_CFLAGS)			\
+	-DLOCALEDIR=\"$(localedir)\"		\
+	-I $(top_srcdir)/sdk
+
+packagekit_plugin_la_LIBS = $(PLUGIN_LIBS)
+
+packagekit_plugin_la_SOURCES = 	\
+	sdk/np_entry.cpp	\
+	sdk/npn_gate.cpp	\
+	sdk/npp_gate.cpp	\
+	sdk/npplat.h		\
+	sdk/pluginbase.h	\
+				\
+	src/plugin.cpp		\
+	src/plugin.h		\
+	src/util.cpp		\
+	src/util.h
+
+DISTCLEANFILES =				\
+	intltool-extract			\
+	intltool-merge				\
+	intltool-update
+
+EXTRA_DIST =					\
+	intltool-extract.in 			\
+	intltool-merge.in 			\
+	intltool-update.in			\
+	test.html
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..45eeb39
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+PACKAGE="packagekit-plugin"
+
+have_libtool=false
+have_autoconf=false
+have_automake=false
+need_configure_in=false
+
+if libtool --version < /dev/null > /dev/null 2>&1 ; then
+	libtool_version=`libtoolize --version | sed 's/^[^0-9]*\([0-9.][0-9.]*\).*/\1/'`
+	have_libtool=true
+	case $libtool_version in
+	    1.3*)
+		need_configure_in=true
+		;;
+	esac
+fi
+
+if autoconf --version < /dev/null > /dev/null 2>&1 ; then
+	autoconf_version=`autoconf --version | sed 's/^[^0-9]*\([0-9.][0-9.]*\).*/\1/'`
+	have_autoconf=true
+	case $autoconf_version in
+	    2.13)
+		need_configure_in=true
+		;;
+	esac
+fi
+
+if $have_libtool ; then : ; else
+	echo;
+	echo "You must have libtool >= 1.3 installed to compile $PACKAGE";
+	echo;
+	exit;
+fi
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+	echo;
+	echo "You must have automake installed to compile $PACKAGE";
+	echo;
+	exit;
+}
+
+(intltoolize --version) < /dev/null > /dev/null 2>&1 || {
+	echo;
+	echo "You must have intltool installed to compile $PACKAGE";
+	echo;
+	exit;
+}
+
+echo "Generating configuration files for $PACKAGE, please wait...."
+echo;
+
+if $need_configure_in ; then
+    if test ! -f configure.in ; then
+	echo "Creating symlink from configure.in to configure.ac..."
+	echo
+	ln -s configure.ac configure.in
+    fi
+fi
+
+aclocal $ACLOCAL_FLAGS
+libtoolize --force
+autoheader
+automake --add-missing -Woverride
+autoconf
+intltoolize
+
+cd $ORIGDIR || exit $?
+
+$srcdir/configure $@ --enable-maintainer-mode --enable-compile-warnings
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..fab93d2
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,41 @@
+dnl Process this file with autoconf to produce a configure script.
+
+AC_INIT(src/plugin.cpp)
+
+AM_INIT_AUTOMAKE([packagekit-plugin], 0.1)
+AM_CONFIG_HEADER(config.h)
+
+AM_MAINTAINER_MODE
+
+AC_PROG_CC
+AC_PROG_CXX
+AC_PROG_INSTALL
+
+AC_DISABLE_STATIC
+AM_PROG_LIBTOOL
+
+# Internationalisation
+IT_PROG_INTLTOOL([0.35.0])
+GETTEXT_PACKAGE=packagekit-plugin
+AC_SUBST([GETTEXT_PACKAGE])
+AM_GLIB_GNU_GETTEXT
+AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE],["$GETTEXT_PACKAGE"],[gettext domain])
+
+changequote(,)dnl
+if test "x$GCC" = "xyes"; then
+  case " $CFLAGS " in
+  *[\ \	]-Wall[\ \	]*) ;;
+  *) CFLAGS="$CFLAGS -Wall" ;;
+  esac
+fi
+changequote([,])dnl
+
+PKG_CHECK_MODULES(PLUGIN, mozilla-plugin cairo pango gtk+-2.0 packagekit gnome-vfs-2.0 gnome-desktop-2.0)
+
+AC_SUBST(PLUGIN_CFLAGS)
+AC_SUBST(PLUGIN_LIBS)
+
+AC_OUTPUT([
+Makefile
+po/Makefile.in
+])
diff --git a/po/ChangeLog b/po/ChangeLog
new file mode 100644
index 0000000..e69de29
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..1b23d3d
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1 @@
+src/plugin.cpp
diff --git a/sdk/np_entry.cpp b/sdk/np_entry.cpp
new file mode 100644
index 0000000..05323e9
--- /dev/null
+++ b/sdk/np_entry.cpp
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+//////////////////////////////////////////////////////////////
+//
+// Main plugin entry point implementation -- exports from the 
+// plugin library
+//
+#include "npplat.h"
+#include "pluginbase.h"
+
+NPNetscapeFuncs NPNFuncs;
+
+NPError OSCALL NP_Shutdown()
+{
+  NS_PluginShutdown();
+  return NPERR_NO_ERROR;
+}
+
+static NPError fillPluginFunctionTable(NPPluginFuncs* aNPPFuncs)
+{
+  if(aNPPFuncs == NULL)
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+
+  // Set up the plugin function table that Netscape will use to
+  // call us. Netscape needs to know about our version and size   
+  // and have a UniversalProcPointer for every function we implement.
+
+  aNPPFuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+#ifdef XP_MAC
+  aNPPFuncs->newp          = NewNPP_NewProc(Private_New);
+  aNPPFuncs->destroy       = NewNPP_DestroyProc(Private_Destroy);
+  aNPPFuncs->setwindow     = NewNPP_SetWindowProc(Private_SetWindow);
+  aNPPFuncs->newstream     = NewNPP_NewStreamProc(Private_NewStream);
+  aNPPFuncs->destroystream = NewNPP_DestroyStreamProc(Private_DestroyStream);
+  aNPPFuncs->asfile        = NewNPP_StreamAsFileProc(Private_StreamAsFile);
+  aNPPFuncs->writeready    = NewNPP_WriteReadyProc(Private_WriteReady);
+  aNPPFuncs->write         = NewNPP_WriteProc(Private_Write);
+  aNPPFuncs->print         = NewNPP_PrintProc(Private_Print);
+  aNPPFuncs->event         = NewNPP_HandleEventProc(Private_HandleEvent);	
+  aNPPFuncs->urlnotify     = NewNPP_URLNotifyProc(Private_URLNotify);			
+  aNPPFuncs->getvalue      = NewNPP_GetValueProc(Private_GetValue);
+  aNPPFuncs->setvalue      = NewNPP_SetValueProc(Private_SetValue);
+#else
+  aNPPFuncs->newp          = NPP_New;
+  aNPPFuncs->destroy       = NPP_Destroy;
+  aNPPFuncs->setwindow     = NPP_SetWindow;
+  aNPPFuncs->newstream     = NPP_NewStream;
+  aNPPFuncs->destroystream = NPP_DestroyStream;
+  aNPPFuncs->asfile        = NPP_StreamAsFile;
+  aNPPFuncs->writeready    = NPP_WriteReady;
+  aNPPFuncs->write         = NPP_Write;
+  aNPPFuncs->print         = NPP_Print;
+  aNPPFuncs->event         = NPP_HandleEvent;
+  aNPPFuncs->urlnotify     = NPP_URLNotify;
+  aNPPFuncs->getvalue      = NPP_GetValue;
+  aNPPFuncs->setvalue      = NPP_SetValue;
+#endif
+#ifdef OJI
+  aNPPFuncs->javaClass     = NULL;
+#endif
+
+  return NPERR_NO_ERROR;
+}
+
+static NPError fillNetscapeFunctionTable(NPNetscapeFuncs* aNPNFuncs)
+{
+  if(aNPNFuncs == NULL)
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+
+  if(HIBYTE(aNPNFuncs->version) > NP_VERSION_MAJOR)
+    return NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  if(aNPNFuncs->size < sizeof(NPNetscapeFuncs))
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+
+  NPNFuncs.size             = aNPNFuncs->size;
+  NPNFuncs.version          = aNPNFuncs->version;
+  NPNFuncs.geturlnotify     = aNPNFuncs->geturlnotify;
+  NPNFuncs.geturl           = aNPNFuncs->geturl;
+  NPNFuncs.posturlnotify    = aNPNFuncs->posturlnotify;
+  NPNFuncs.posturl          = aNPNFuncs->posturl;
+  NPNFuncs.requestread      = aNPNFuncs->requestread;
+  NPNFuncs.newstream        = aNPNFuncs->newstream;
+  NPNFuncs.write            = aNPNFuncs->write;
+  NPNFuncs.destroystream    = aNPNFuncs->destroystream;
+  NPNFuncs.status           = aNPNFuncs->status;
+  NPNFuncs.uagent           = aNPNFuncs->uagent;
+  NPNFuncs.memalloc         = aNPNFuncs->memalloc;
+  NPNFuncs.memfree          = aNPNFuncs->memfree;
+  NPNFuncs.memflush         = aNPNFuncs->memflush;
+  NPNFuncs.reloadplugins    = aNPNFuncs->reloadplugins;
+#ifdef OJI
+  NPNFuncs.getJavaEnv       = aNPNFuncs->getJavaEnv;
+  NPNFuncs.getJavaPeer      = aNPNFuncs->getJavaPeer;
+#endif
+  NPNFuncs.getvalue         = aNPNFuncs->getvalue;
+  NPNFuncs.setvalue         = aNPNFuncs->setvalue;
+  NPNFuncs.invalidaterect   = aNPNFuncs->invalidaterect;
+  NPNFuncs.invalidateregion = aNPNFuncs->invalidateregion;
+  NPNFuncs.forceredraw      = aNPNFuncs->forceredraw;
+
+  return NPERR_NO_ERROR;
+}
+
+//
+// Some exports are different on different platforms
+//
+
+/**************************************************/
+/*                                                */
+/*                   Windows                      */
+/*                                                */
+/**************************************************/
+#ifdef XP_WIN
+
+NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs)
+{
+  NPError rv = fillNetscapeFunctionTable(aNPNFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  return NS_PluginInitialize();
+}
+
+NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* aNPPFuncs)
+{
+  return fillPluginFunctionTable(aNPPFuncs);
+}
+
+#endif //XP_WIN
+
+/**************************************************/
+/*                                                */
+/*                    Unix                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_UNIX
+
+NPError NP_Initialize(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs)
+{
+  NPError rv = fillNetscapeFunctionTable(aNPNFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  rv = fillPluginFunctionTable(aNPPFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  return NS_PluginInitialize();
+}
+
+char * NP_GetMIMEDescription(void)
+{
+  return NPP_GetMIMEDescription();
+}
+
+NPError NP_GetValue(void *future, NPPVariable aVariable, void *aValue)
+{
+  return NS_PluginGetValue(aVariable, aValue);
+}
+
+#endif //XP_UNIX
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_MAC
+
+#if !TARGET_API_MAC_CARBON
+QDGlobals* gQDPtr; // Pointer to Netscape's QuickDraw globals
+#endif
+
+short gResFile; // Refnum of the plugin's resource file
+
+NPError Private_Initialize(void)
+{
+  NPError rv = NS_PluginInitialize();
+  return rv;
+}
+
+void Private_Shutdown(void)
+{
+  NS_PluginShutdown();
+  __destroy_global_chain();
+}
+
+void SetUpQD(void);
+
+void SetUpQD(void)
+{
+  ProcessSerialNumber PSN;
+  FSSpec              myFSSpec;
+  Str63               name;
+  ProcessInfoRec      infoRec;
+  OSErr               result = noErr;
+  CFragConnectionID   connID;
+  Str255              errName;
+
+  // Memorize the plugin¹s resource file refnum for later use.
+  gResFile = CurResFile();
+
+#if !TARGET_API_MAC_CARBON
+  // Ask the system if CFM is available.
+  long response;
+  OSErr err = Gestalt(gestaltCFMAttr, &response);
+  Boolean hasCFM = BitTst(&response, 31-gestaltCFMPresent);
+
+  if (hasCFM) {
+    // GetProcessInformation takes a process serial number and 
+    // will give us back the name and FSSpec of the application.
+    // See the Process Manager in IM.
+    infoRec.processInfoLength = sizeof(ProcessInfoRec);
+    infoRec.processName = name;
+    infoRec.processAppSpec = &myFSSpec;
+
+    PSN.highLongOfPSN = 0;
+    PSN.lowLongOfPSN = kCurrentProcess;
+
+    result = GetProcessInformation(&PSN, &infoRec);
+  }
+	else
+    // If no CFM installed, assume it must be a 68K app.
+    result = -1;		
+
+  if (result == noErr) {
+    // Now that we know the app name and FSSpec, we can call GetDiskFragment
+    // to get a connID to use in a subsequent call to FindSymbol (it will also
+    // return the address of ³main² in app, which we ignore).  If GetDiskFragment 
+    // returns an error, we assume the app must be 68K.
+    Ptr mainAddr; 	
+    result =  GetDiskFragment(infoRec.processAppSpec, 0L, 0L, infoRec.processName,
+                              kReferenceCFrag, &connID, (Ptr*)&mainAddr, errName);
+  }
+
+  if (result == noErr) {
+    // The app is a PPC code fragment, so call FindSymbol
+    // to get the exported ³qd² symbol so we can access its
+    // QuickDraw globals.
+    CFragSymbolClass symClass;
+    result = FindSymbol(connID, "\pqd", (Ptr*)&gQDPtr, &symClass);
+  }
+  else {
+    // The app is 68K, so use its A5 to compute the address
+    // of its QuickDraw globals.
+    gQDPtr = (QDGlobals*)(*((long*)SetCurrentA5()) - (sizeof(QDGlobals) - sizeof(GrafPtr)));
+  }
+#endif /* !TARGET_API_MAC_CARBON */
+}
+
+NPError main(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs, NPP_ShutdownUPP* unloadUpp);
+
+#if !TARGET_API_MAC_CARBON
+#pragma export on
+#if GENERATINGCFM
+RoutineDescriptor mainRD = BUILD_ROUTINE_DESCRIPTOR(uppNPP_MainEntryProcInfo, main);
+#endif
+#pragma export off
+#endif /* !TARGET_API_MAC_CARBON */
+
+
+NPError main(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs, NPP_ShutdownUPP* aUnloadUpp)
+{
+  NPError rv = NPERR_NO_ERROR;
+
+  if (aUnloadUpp == NULL)
+    rv = NPERR_INVALID_FUNCTABLE_ERROR;
+
+  if (rv == NPERR_NO_ERROR)
+    rv = fillNetscapeFunctionTable(aNPNFuncs);
+
+  if (rv == NPERR_NO_ERROR) {
+    // defer static constructors until the global functions are initialized.
+    __InitCode__();
+    rv = fillPluginFunctionTable(aNPPFuncs);
+  }
+
+  *aUnloadUpp = NewNPP_ShutdownProc(Private_Shutdown);
+  SetUpQD();
+  rv = Private_Initialize();
+	
+  return rv;
+}
+#endif //XP_MAC
diff --git a/sdk/npn_gate.cpp b/sdk/npn_gate.cpp
new file mode 100644
index 0000000..f5caa32
--- /dev/null
+++ b/sdk/npn_gate.cpp
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+////////////////////////////////////////////////////////////
+//
+// Implementation of Netscape entry points (NPN_*)
+//
+#include "npplat.h"
+
+extern NPNetscapeFuncs NPNFuncs;
+
+void NPN_Version(int* plugin_major, int* plugin_minor, int* netscape_major, int* netscape_minor)
+{
+  *plugin_major   = NP_VERSION_MAJOR;
+  *plugin_minor   = NP_VERSION_MINOR;
+  *netscape_major = HIBYTE(NPNFuncs.version);
+  *netscape_minor = LOBYTE(NPNFuncs.version);
+}
+
+NPError NPN_GetURLNotify(NPP instance, const char *url, const char *target, void* notifyData)
+{
+	int navMinorVers = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+  if( navMinorVers >= NPVERS_HAS_NOTIFICATION )
+		rv = CallNPN_GetURLNotifyProc(NPNFuncs.geturlnotify, instance, url, target, notifyData);
+	else
+		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+NPError NPN_GetURL(NPP instance, const char *url, const char *target)
+{
+  NPError rv = CallNPN_GetURLProc(NPNFuncs.geturl, instance, url, target);
+  return rv;
+}
+
+NPError NPN_PostURLNotify(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData)
+{
+	int navMinorVers = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+	if( navMinorVers >= NPVERS_HAS_NOTIFICATION )
+		rv = CallNPN_PostURLNotifyProc(NPNFuncs.posturlnotify, instance, url, window, len, buf, file, notifyData);
+	else
+		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+NPError NPN_PostURL(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file)
+{
+  NPError rv = CallNPN_PostURLProc(NPNFuncs.posturl, instance, url, window, len, buf, file);
+  return rv;
+} 
+
+NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList)
+{
+  NPError rv = CallNPN_RequestReadProc(NPNFuncs.requestread, stream, rangeList);
+  return rv;
+}
+
+NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream)
+{
+	int navMinorVersion = NPNFuncs.version & 0xFF;
+
+  NPError rv = NPERR_NO_ERROR;
+
+	if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+		rv = CallNPN_NewStreamProc(NPNFuncs.newstream, instance, type, target, stream);
+	else
+		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+int32 NPN_Write(NPP instance, NPStream *stream, int32 len, void *buffer)
+{
+	int navMinorVersion = NPNFuncs.version & 0xFF;
+  int32 rv = 0;
+
+  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+		rv = CallNPN_WriteProc(NPNFuncs.write, instance, stream, len, buffer);
+	else
+		rv = -1;
+
+  return rv;
+}
+
+NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason)
+{
+	int navMinorVersion = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+		rv = CallNPN_DestroyStreamProc(NPNFuncs.destroystream, instance, stream, reason);
+	else
+		rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+void NPN_Status(NPP instance, const char *message)
+{
+  CallNPN_StatusProc(NPNFuncs.status, instance, message);
+}
+
+const char* NPN_UserAgent(NPP instance)
+{
+  const char * rv = NULL;
+  rv = CallNPN_UserAgentProc(NPNFuncs.uagent, instance);
+  return rv;
+}
+
+void* NPN_MemAlloc(uint32 size)
+{
+  void * rv = NULL;
+  rv = CallNPN_MemAllocProc(NPNFuncs.memalloc, size);
+  return rv;
+}
+
+void NPN_MemFree(void* ptr)
+{
+  CallNPN_MemFreeProc(NPNFuncs.memfree, ptr);
+}
+
+uint32 NPN_MemFlush(uint32 size)
+{
+  uint32 rv = CallNPN_MemFlushProc(NPNFuncs.memflush, size);
+  return rv;
+}
+
+void NPN_ReloadPlugins(NPBool reloadPages)
+{
+  CallNPN_ReloadPluginsProc(NPNFuncs.reloadplugins, reloadPages);
+}
+
+#ifdef OJI
+JRIEnv* NPN_GetJavaEnv(void)
+{
+  JRIEnv * rv = NULL;
+	rv = CallNPN_GetJavaEnvProc(NPNFuncs.getJavaEnv);
+  return rv;
+}
+
+jref NPN_GetJavaPeer(NPP instance)
+{
+  jref rv;
+  rv = CallNPN_GetJavaPeerProc(NPNFuncs.getJavaPeer, instance);
+  return rv;
+}
+#endif
+
+NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value)
+{
+  NPError rv = CallNPN_GetValueProc(NPNFuncs.getvalue, instance, variable, value);
+  return rv;
+}
+
+NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value)
+{
+  NPError rv = CallNPN_SetValueProc(NPNFuncs.setvalue, instance, variable, value);
+  return rv;
+}
+
+void NPN_InvalidateRect(NPP instance, NPRect *invalidRect)
+{
+  CallNPN_InvalidateRectProc(NPNFuncs.invalidaterect, instance, invalidRect);
+}
+
+void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion)
+{
+  CallNPN_InvalidateRegionProc(NPNFuncs.invalidateregion, instance, invalidRegion);
+}
+
+void NPN_ForceRedraw(NPP instance)
+{
+  CallNPN_ForceRedrawProc(NPNFuncs.forceredraw, instance);
+}
diff --git a/sdk/npp_gate.cpp b/sdk/npp_gate.cpp
new file mode 100644
index 0000000..3e22783
--- /dev/null
+++ b/sdk/npp_gate.cpp
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+////////////////////////////////////////////////////////////
+//
+// Implementation of plugin entry points (NPP_*)
+//
+#include "pluginbase.h"
+
+// here the plugin creates a plugin instance object which 
+// will be associated with this newly created NPP instance and 
+// will do all the necessary job
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)
+{   
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  // create a new plugin instance object
+  // initialization will be done when the associated window is ready
+  nsPluginCreateData ds;
+  
+  ds.instance = instance;
+  ds.type     = pluginType; 
+  ds.mode     = mode; 
+  ds.argc     = argc; 
+  ds.argn     = argn; 
+  ds.argv     = argv; 
+  ds.saved    = saved;
+
+  nsPluginInstanceBase * plugin = NS_NewPluginInstance(&ds);
+  if(plugin == NULL)
+    return NPERR_OUT_OF_MEMORY_ERROR;
+
+  // associate the plugin instance object with NPP instance
+  instance->pdata = (void *)plugin;
+  return rv;
+}
+
+// here is the place to clean up and destroy the nsPluginInstance object
+NPError NPP_Destroy (NPP instance, NPSavedData** save)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin != NULL) {
+    plugin->shut();
+    NS_DestroyPluginInstance(plugin);
+  }
+  return rv;
+}
+
+// during this call we know when the plugin window is ready or
+// is about to be destroyed so we can do some gui specific
+// initialization and shutdown
+NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow)
+{    
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  if(pNPWindow == NULL)
+    return NPERR_GENERIC_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  // window just created
+  if(!plugin->isInitialized() && (pNPWindow->window != NULL)) { 
+    if(!plugin->init(pNPWindow)) {
+      NS_DestroyPluginInstance(plugin);
+      return NPERR_MODULE_LOAD_FAILED_ERROR;
+    }
+  }
+
+  // window goes away
+  if((pNPWindow->window == NULL) && plugin->isInitialized())
+    return plugin->SetWindow(pNPWindow);
+
+  // window resized?
+  if(plugin->isInitialized() && (pNPWindow->window != NULL))
+    return plugin->SetWindow(pNPWindow);
+
+  // this should not happen, nothing to do
+  if((pNPWindow->window == NULL) && !plugin->isInitialized())
+    return plugin->SetWindow(pNPWindow);
+
+  return rv;
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->NewStream(type, stream, seekable, stype);
+  return rv;
+}
+
+int32 NPP_WriteReady (NPP instance, NPStream *stream)
+{
+  if(instance == NULL)
+    return 0x0fffffff;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return 0x0fffffff;
+
+  int32 rv = plugin->WriteReady(stream);
+  return rv;
+}
+
+int32 NPP_Write (NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
+{   
+  if(instance == NULL)
+    return len;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return len;
+
+  int32 rv = plugin->Write(stream, offset, len, buffer);
+  return rv;
+}
+
+NPError NPP_DestroyStream (NPP instance, NPStream *stream, NPError reason)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->DestroyStream(stream, reason);
+  return rv;
+}
+
+void NPP_StreamAsFile (NPP instance, NPStream* stream, const char* fname)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return;
+
+  plugin->StreamAsFile(stream, fname);
+}
+
+void NPP_Print (NPP instance, NPPrint* printInfo)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return;
+
+  plugin->Print(printInfo);
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return;
+
+  plugin->URLNotify(url, reason, notifyData);
+}
+
+NPError	NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->GetValue(variable, value);
+  return rv;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->SetValue(variable, value);
+  return rv;
+}
+
+int16	NPP_HandleEvent(NPP instance, void* event)
+{
+  if(instance == NULL)
+    return 0;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL) 
+    return 0;
+
+  uint16 rv = plugin->HandleEvent(event);
+  return rv;
+}
+
+#ifdef OJI
+jref NPP_GetJavaClass (void)
+{
+  return NULL;
+}
+#endif
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+
+// Mac needs these wrappers, see npplat.h for more info
+
+#ifdef XP_MAC
+
+NPError	Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)
+{
+  NPError rv = NPP_New(pluginType, instance, mode, argc, argn, argv, saved);
+  return rv;	
+}
+
+NPError Private_Destroy(NPP instance, NPSavedData** save)
+{
+  NPError rv = NPP_Destroy(instance, save);
+  return rv;
+}
+
+NPError Private_SetWindow(NPP instance, NPWindow* window)
+{
+  NPError rv = NPP_SetWindow(instance, window);
+  return rv;
+}
+
+NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+  NPError rv = NPP_NewStream(instance, type, stream, seekable, stype);
+  return rv;
+}
+
+int32 Private_WriteReady(NPP instance, NPStream* stream)
+{
+  int32 rv = NPP_WriteReady(instance, stream);
+  return rv;
+}
+
+int32 Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
+{
+  int32 rv = NPP_Write(instance, stream, offset, len, buffer);
+  return rv;
+}
+
+void Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+  NPP_StreamAsFile(instance, stream, fname);
+}
+
+
+NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason)
+{
+  NPError rv = NPP_DestroyStream(instance, stream, reason);
+  return rv;
+}
+
+int16 Private_HandleEvent(NPP instance, void* event)
+{
+  int16 rv = NPP_HandleEvent(instance, event);
+  return rv;
+}
+
+void Private_Print(NPP instance, NPPrint* platformPrint)
+{
+  NPP_Print(instance, platformPrint);
+}
+
+void Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+  NPP_URLNotify(instance, url, reason, notifyData);
+}
+
+jref Private_GetJavaClass(void)
+{
+  return NULL;
+}
+
+NPError Private_GetValue(NPP instance, NPPVariable variable, void *result)
+{
+  NPError rv = NPP_GetValue(instance, variable, result);
+  return rv;
+}
+
+NPError Private_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+  NPError rv = NPP_SetValue(instance, variable, value);
+  return rv;
+}
+
+#endif //XP_MAC
diff --git a/sdk/npplat.h b/sdk/npplat.h
new file mode 100644
index 0000000..637ac36
--- /dev/null
+++ b/sdk/npplat.h
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _NPPLAT_H_
+#define _NPPLAT_H_
+
+#include "npapi.h"
+#include "npupp.h"
+
+/**************************************************/
+/*                                                */
+/*                   Windows                      */
+/*                                                */
+/**************************************************/
+#ifdef XP_WIN
+#include "windows.h"
+#endif //XP_WIN
+
+/**************************************************/
+/*                                                */
+/*                    Unix                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_UNIX
+#include <stdio.h>
+#endif //XP_UNIX
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_MAC
+
+#include <Processes.h>
+#include <Gestalt.h>
+#include <CodeFragments.h>
+#include <Timer.h>
+#include <Resources.h>
+#include <ToolUtils.h>
+
+#include "jri.h"
+
+// The Mixed Mode procInfos defined in npupp.h assume Think C-
+// style calling conventions.  These conventions are used by
+// Metrowerks with the exception of pointer return types, which
+// in Metrowerks 68K are returned in A0, instead of the standard
+// D0. Thus, since NPN_MemAlloc and NPN_UserAgent return pointers,
+// Mixed Mode will return the values to a 68K plugin in D0, but 
+// a 68K plugin compiled by Metrowerks will expect the result in
+// A0.  The following pragma forces Metrowerks to use D0 instead.
+//
+#ifdef __MWERKS__
+#ifndef powerc
+#pragma pointers_in_D0
+#endif
+#endif
+
+#ifdef __MWERKS__
+#ifndef powerc
+#pragma pointers_in_A0
+#endif
+#endif
+
+// The following fix for static initializers (which fixes a preious
+// incompatibility with some parts of PowerPlant, was submitted by 
+// Jan Ulbrich.
+#ifdef __MWERKS__
+	#ifdef __cplusplus
+	extern "C" {
+	#endif
+		#ifndef powerc
+			extern void	__InitCode__(void);
+		#else
+			extern void __sinit(void);
+			#define __InitCode__ __sinit
+		#endif
+		extern void	__destroy_global_chain(void);
+	#ifdef __cplusplus
+	}
+	#endif // __cplusplus
+#endif // __MWERKS__
+
+// Wrapper functions for all calls from Netscape to the plugin.
+// These functions let the plugin developer just create the APIs
+// as documented and defined in npapi.h, without needing to 
+// install those functions in the function table or worry about
+// setting up globals for 68K plugins.
+NPError Private_Initialize(void);
+void    Private_Shutdown(void);
+NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved);
+NPError Private_Destroy(NPP instance, NPSavedData** save);
+NPError Private_SetWindow(NPP instance, NPWindow* window);
+NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype);
+NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason);
+int32   Private_WriteReady(NPP instance, NPStream* stream);
+int32   Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer);
+void    Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
+void    Private_Print(NPP instance, NPPrint* platformPrint);
+int16   Private_HandleEvent(NPP instance, void* event);
+void    Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData);
+jref    Private_GetJavaClass(void);
+NPError Private_GetValue(NPP instance, NPPVariable variable, void *result);
+NPError Private_SetValue(NPP instance, NPNVariable variable, void *value);
+
+#endif //XP_MAC
+
+#ifndef HIBYTE
+#define HIBYTE(i) (i >> 8)
+#endif
+
+#ifndef LOBYTE
+#define LOBYTE(i) (i & 0xff)
+#endif
+
+#endif //_NPPLAT_H_
diff --git a/sdk/pluginbase.h b/sdk/pluginbase.h
new file mode 100644
index 0000000..b7e3327
--- /dev/null
+++ b/sdk/pluginbase.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __PLUGININSTANCEBASE_H__
+#define __PLUGININSTANCEBASE_H__
+
+#include "npplat.h"
+
+struct nsPluginCreateData
+{
+  NPP instance;
+  NPMIMEType type; 
+  uint16 mode; 
+  int16 argc; 
+  char** argn; 
+  char** argv; 
+  NPSavedData* saved;
+};
+
+class nsPluginInstanceBase
+{
+public:
+  // these three methods must be implemented in the derived
+  // class platform specific way
+  virtual NPBool init(NPWindow* aWindow) = 0;
+  virtual void shut() = 0;
+  virtual NPBool isInitialized() = 0;
+
+  // implement all or part of those methods in the derived 
+  // class as needed
+  virtual NPError SetWindow(NPWindow* pNPWindow)                    { return NPERR_NO_ERROR; }
+  virtual NPError NewStream(NPMIMEType type, NPStream* stream, 
+                            NPBool seekable, uint16* stype)         { return NPERR_NO_ERROR; }
+  virtual NPError DestroyStream(NPStream *stream, NPError reason)   { return NPERR_NO_ERROR; }
+  virtual void    StreamAsFile(NPStream* stream, const char* fname) { return; }
+  virtual int32   WriteReady(NPStream *stream)                      { return 0x0fffffff; }
+  virtual int32   Write(NPStream *stream, int32 offset, 
+                        int32 len, void *buffer)                    { return len; }
+  virtual void    Print(NPPrint* printInfo)                         { return; }
+  virtual uint16  HandleEvent(void* event)                          { return 0; }
+  virtual void    URLNotify(const char* url, NPReason reason, 
+                            void* notifyData)                       { return; }
+  virtual NPError GetValue(NPPVariable variable, void *value)       { return NPERR_NO_ERROR; }
+  virtual NPError SetValue(NPNVariable variable, void *value)       { return NPERR_NO_ERROR; }
+};
+
+// functions that should be implemented for each specific plugin
+
+// creation and destruction of the object of the derived class
+nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct);
+void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin);
+
+// global plugin initialization and shutdown
+NPError NS_PluginInitialize();
+void NS_PluginShutdown();
+
+#ifdef XP_UNIX
+// global to get plugins name & description 
+NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue);
+#endif
+
+#endif // __PLUGININSTANCEBASE_H__
diff --git a/src/plugin.cpp b/src/plugin.cpp
new file mode 100644
index 0000000..b1e389d
--- /dev/null
+++ b/src/plugin.cpp
@@ -0,0 +1,775 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#define MOZ_X11
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include <cairo-xlib.h>
+#include <dlfcn.h>
+#include <pango/pangocairo.h>
+#include <packagekit/pk-package-id.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <libgnome/gnome-desktop-item.h>
+#include <libgnomevfs/gnome-vfs.h>
+
+#include "plugin.h"
+
+#define APPLICATION_DIR "/usr/share/applications"
+
+#define MIME_TYPES_HANDLED  "application/x-packagekit-plugin"
+#define PLUGIN_NAME         "Plugin for Installing Applications"
+#define MIME_TYPES_DESCRIPTION  MIME_TYPES_HANDLED":bsc:"PLUGIN_NAME
+#define PLUGIN_DESCRIPTION  PLUGIN_NAME
+
+#define MARGIN 5
+
+char* NPP_GetMIMEDescription(void)
+{
+    return (char *)(MIME_TYPES_DESCRIPTION);
+}
+
+static void *module_handle = 0;
+
+/* If our dependent libraries like libpackagekit get unloaded, bad stuff
+ * happens (they may have registered GLib types and so forth) so we need
+ * to keep them around. The (GNU extension) RTLD_NODELETE seems useful
+ * but isn't so much, since it only refers to a specific library and not
+ * its dependent libraries, so we'd have to identify specifically each
+ * of our dependencies that is not safe to unload and that is most of
+ * the GTK+/GNOME stack.
+ */
+static void
+make_module_resident()
+{
+    Dl_info info;
+
+    /* Get the (absolute) filename of this module */
+    if (!dladdr((void *)NPP_GetMIMEDescription, &info)) {
+        g_warning("Can't find filename for module");
+        return;
+    }
+
+    /* Now reopen it to get our own handle */
+    module_handle = dlopen(info.dli_fname, RTLD_NOW);
+    if (!module_handle) {
+        g_warning("Can't permanently open module %s", dlerror());
+        return;
+    }
+
+    /* the module will never be closed */
+}
+
+/////////////////////////////////////
+// general initialization and shutdown
+//
+NPError NS_PluginInitialize()
+{
+    if (module_handle != 0) /* Already initialized */
+        return NPERR_NO_ERROR;
+    
+    make_module_resident();
+    
+#ifdef ENABLE_NLS
+    bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+    
+    return NPERR_NO_ERROR;
+}
+
+void NS_PluginShutdown()
+{
+}
+
+// get values per plugin
+NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue)
+{
+    NPError err = NPERR_NO_ERROR;
+    switch (aVariable) {
+    case NPPVpluginNameString:
+        *((char **)aValue) = (char *)PLUGIN_NAME;
+        break;
+    case NPPVpluginDescriptionString:
+        *((char **)aValue) = (char *)PLUGIN_DESCRIPTION;
+        break;
+    default:
+        err = NPERR_INVALID_PARAM;
+        break;
+    }
+    return err;
+}
+
+/////////////////////////////////////////////////////////////
+//
+// construction and destruction of our plugin instance object
+//
+nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct)
+{
+    const char *displayName = "";
+    const char *packageNames = NULL;
+    const char *desktopNames = NULL;
+    
+    if(!aCreateDataStruct)
+        return NULL;
+
+    for (int i = 0; i < aCreateDataStruct->argc; i++) {
+        if (strcmp(aCreateDataStruct->argn[i], "displayname") == 0)
+            displayName = aCreateDataStruct->argv[i];
+        else if (strcmp(aCreateDataStruct->argn[i], "packagenames") == 0)
+            packageNames = aCreateDataStruct->argv[i];
+        else if (strcmp(aCreateDataStruct->argn[i], "desktopnames") == 0)
+            desktopNames = aCreateDataStruct->argv[i];
+    }
+      
+    nsPluginInstance * plugin = new nsPluginInstance(aCreateDataStruct->instance, displayName, packageNames, desktopNames);
+  
+    NPN_SetValue(aCreateDataStruct->instance,
+                 NPPVpluginWindowBool, (void *)FALSE);
+  
+    return plugin;
+}
+
+void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin)
+{
+    if(aPlugin)
+        delete (nsPluginInstance *)aPlugin;
+}
+
+////////////////////////////////////////
+//
+// nsPluginInstance class implementation
+//
+
+static std::vector<std::string>
+splitString(const char *str)
+{
+    std::vector<std::string> v;
+
+    if (str) {
+        char **split = g_strsplit(str, " ", -1);
+        for (char **s = split; *s; s++) {
+            char *stripped = strdup(*s);
+            g_strstrip(stripped);
+            v.push_back(stripped);
+            g_free(stripped);
+        }
+
+        g_strfreev(split);
+    }
+
+    return v;
+}
+
+nsPluginInstance::nsPluginInstance(NPP         aInstance,
+                                   const char *displayName,
+                                   const char *packageNames,
+                                   const char *desktopNames) :
+    nsPluginInstanceBase(),
+    mInstance(aInstance),
+    mInitialized(FALSE),
+    mStatus(IN_PROGRESS),
+    mDisplayName(displayName),
+    mPackageNames(splitString(packageNames)),
+    mDesktopNames(splitString(desktopNames)),
+    mWindow(0),
+    mLayout(0),
+    mInstallPackageHandle(0)
+{
+    recheck();
+}
+
+nsPluginInstance::~nsPluginInstance()
+{
+}
+
+NPBool nsPluginInstance::init(NPWindow* aWindow)
+{
+    if(aWindow == NULL)
+        return FALSE;
+  
+    if (SetWindow(aWindow))
+        mInitialized = TRUE;
+	
+    return mInitialized;
+}
+
+void nsPluginInstance::recheck()
+{
+    mStatus = IN_PROGRESS;
+    mAvailableVersion = "";
+    mAvailablePackageName = "";
+    
+    for (std::vector<std::string>::iterator i = mPackageNames.begin(); i != mPackageNames.end(); i++) {
+        GError *error = NULL;
+        PkClient *client = pk_client_new();
+        if (!pk_client_resolve(client, "none", i->c_str(), &error)) {
+            g_warning("%s", error->message);
+            g_clear_error(&error);
+            g_object_unref(client);
+        } else {
+            g_signal_connect(client, "package", G_CALLBACK(onClientPackage), this);
+            g_signal_connect(client, "error-code", G_CALLBACK(onClientErrorCode), this);
+            g_signal_connect(client, "finished", G_CALLBACK(onClientFinished), this);
+            mClients.push_back(client);
+        }
+    }
+
+    findDesktopFile();
+
+    if (mClients.empty() && getStatus() == IN_PROGRESS)
+        setStatus(UNAVAILABLE);
+}
+
+void nsPluginInstance::removeClient(PkClient *client)
+{
+    for (std::vector<PkClient *>::iterator i = mClients.begin(); i != mClients.end(); i++) {
+        if (*i == client) {
+            mClients.erase(i);
+            g_signal_handlers_disconnect_by_func(client, (void *)onClientPackage, this);
+            g_signal_handlers_disconnect_by_func(client, (void *)onClientErrorCode, this);
+            g_signal_handlers_disconnect_by_func(client, (void *)onClientFinished, this);
+            g_object_unref(client);
+            break;
+        }
+    }
+
+    if (mClients.empty()) {
+        if (getStatus() == IN_PROGRESS)
+            setStatus(UNAVAILABLE);
+    }
+}
+
+void nsPluginInstance::shut()
+{
+    clearLayout();
+
+    if (mInstallPackageHandle != 0)
+        pkp_execute_command_async_cancel(mInstallPackageHandle);
+
+    while (!mClients.empty())
+        removeClient(mClients.front());
+    
+    mInitialized = FALSE;
+}
+
+NPError nsPluginInstance::GetValue(NPPVariable aVariable, void *aValue)
+{
+    NPError err = NPERR_NO_ERROR;
+    switch (aVariable) {
+    case NPPVpluginNameString:
+    case NPPVpluginDescriptionString:
+        return NS_PluginGetValue(aVariable, aValue) ;
+        break;
+    default:
+        err = NPERR_INVALID_PARAM;
+        break;
+    }
+    return err;
+
+}
+
+NPError nsPluginInstance::SetWindow(NPWindow* aWindow)
+{
+    if (aWindow == NULL || (Window)aWindow->window != mWindow)
+        clearLayout();
+    
+    if (aWindow == NULL)
+        return FALSE;
+
+    mX = aWindow->x;
+    mY = aWindow->y;
+    mWidth = aWindow->width;
+    mHeight = aWindow->height;
+  
+    mWindow = (Window) aWindow->window;
+    NPSetWindowCallbackStruct *ws_info = (NPSetWindowCallbackStruct *)aWindow->ws_info;
+    mDisplay = ws_info->display;
+    mVisual = ws_info->visual;
+    mDepth = ws_info->depth;
+    mColormap = ws_info->colormap;
+  
+    return NPERR_NO_ERROR;
+}
+
+void
+nsPluginInstance::setStatus(PackageStatus status)
+{
+    if (mStatus != status) {
+        mStatus = status;
+        clearLayout();
+        refresh();
+    }
+}
+
+void
+nsPluginInstance::setAvailableVersion(const char *version)
+{
+    mAvailableVersion = version;
+    clearLayout();
+    refresh();
+}
+
+void
+nsPluginInstance::setAvailablePackageName(const char *name)
+{
+    mAvailablePackageName = name;
+}
+
+void
+nsPluginInstance::setInstalledVersion(const char *version)
+{
+    mInstalledVersion = version;
+    clearLayout();
+    refresh();
+}
+
+void
+nsPluginInstance::clearLayout()
+{
+    if (mLayout) {
+        g_object_unref(mLayout);
+        mLayout = 0;
+    }
+}
+
+static void
+append_markup(GString *str, const char *format, ...)
+{
+    va_list vap;
+    
+    va_start(vap, format);
+    char *tmp = g_markup_vprintf_escaped(format, vap);
+    va_end(vap);
+
+    g_string_append(str, tmp);
+    g_free(tmp);
+}
+
+static guint32
+rgba_from_gdk_color(GdkColor *color)
+{
+    return (((color->red   >> 8) << 24) |
+            ((color->green >> 8) << 16) |
+            ((color->blue  >> 8) << 8) |
+            0xff);
+}
+
+static void
+set_source_from_rgba(cairo_t *cr,
+                     guint32  rgba)
+{
+    cairo_set_source_rgba(cr,
+                          ((rgba & 0xff000000) >> 24) / 255.,
+                          ((rgba & 0x00ff0000) >> 16) / 255.,
+                          ((rgba & 0x0000ff00) >> 8) / 255.,
+                          (rgba & 0x000000ff) / 255.);
+                          
+}
+
+/* Retrieve the system colors and fonts.
+ * This looks incredibly expensive .... to create a GtkWindow for
+ * every expose ... but actually it's only moderately expensive;
+ * Creating a GtkWindow is just normal GObject creation overhead --
+ * the extra expense beyond that will come when we actually create
+ * the window.
+ */
+static void
+get_style(PangoFontDescription **font_desc,
+          guint32               *foreground,
+          guint32               *background,
+          guint32               *link)
+{
+    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+    gtk_widget_ensure_style(window);
+
+    *foreground = rgba_from_gdk_color(&window->style->text[GTK_STATE_NORMAL]);
+    *background = rgba_from_gdk_color(&window->style->base[GTK_STATE_NORMAL]);
+
+    GdkColor link_color = { 0, 0, 0, 0xeeee };
+    GdkColor *tmp = NULL;
+
+    gtk_widget_style_get (GTK_WIDGET (window),
+                          "link-color", &tmp, NULL);
+    if (tmp != NULL) {
+        link_color = *tmp;
+        gdk_color_free(tmp);
+    }
+
+    *link = rgba_from_gdk_color(&link_color);
+
+    *font_desc = pango_font_description_copy(window->style->font_desc);
+   
+    gtk_widget_destroy(window);
+}
+
+void
+nsPluginInstance::ensureLayout(cairo_t              *cr,
+                               PangoFontDescription *font_desc,
+                               guint32               link_color)
+{
+    GString *markup = g_string_new(NULL);
+    
+    if (mLayout)
+        return;
+    
+    mLayout = pango_cairo_create_layout(cr);
+    pango_layout_set_font_description(mLayout, font_desc);
+
+    switch (mStatus) {
+    case IN_PROGRESS:
+        append_markup(markup, _("Getting package information..."));
+        break;
+    case INSTALLED:
+        if (!mDesktopFile.empty())
+            append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Run %s</span>"),
+                          link_color >> 8,
+                          mDisplayName.c_str());
+        else
+            append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
+        if (!mInstalledVersion.empty())
+            append_markup(markup, _("\n<small>Installed version: %s</small>"), mInstalledVersion.c_str());
+        break;
+    case AVAILABLE:
+        append_markup(markup, _("<span color='#%06x' underline='single' size='larger'>Install %s Now</span>"),
+                      link_color >> 8,
+                      mDisplayName.c_str());
+        append_markup(markup, _("\n<small>Version: %s</small>"), mAvailableVersion.c_str());
+        break;
+    case UNAVAILABLE:
+        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
+        append_markup(markup, _("\n<small>No packages found for your system</small>"));
+        break;
+    case INSTALLING:
+        append_markup(markup, _("<big>%s</big>"), mDisplayName.c_str());
+        append_markup(markup, _("\n<small>Installing...</small>"));
+        break;
+    }
+
+    pango_layout_set_markup(mLayout, markup->str, -1);
+    g_string_free(markup, TRUE);
+}
+
+void
+nsPluginInstance::refresh()
+{
+    NPRect rect;
+
+    /* Coordinates here are relative to the plugin's origin (mX,mY) */
+    
+    rect.left = 0;
+    rect.right =  mWidth;
+    rect.top = 0;
+    rect.bottom = mHeight;
+    
+    NPN_InvalidateRect(mInstance, &rect);
+}
+                               
+void
+nsPluginInstance::handleGraphicsExpose(XGraphicsExposeEvent *xev)
+{
+    cairo_surface_t *surface = cairo_xlib_surface_create (mDisplay, xev->drawable, mVisual, mX + mWidth, mY + mHeight);
+    cairo_t *cr = cairo_create(surface);
+    guint32 foreground, background, link;
+    PangoFontDescription *font_desc;
+
+    get_style(&font_desc, &foreground, &background, &link);
+
+    cairo_rectangle(cr,xev->x, xev->y, xev->width, xev->height);
+    cairo_clip(cr);
+
+    set_source_from_rgba(cr, background);
+    cairo_rectangle(cr, mX, mY, mWidth, mHeight);
+    cairo_fill(cr);
+
+    cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
+    cairo_rectangle(cr, mX + 0.5, mY + 0.5, mWidth - 1, mHeight - 1);
+    cairo_set_line_width(cr, 1);
+    cairo_stroke(cr);
+
+    ensureLayout(cr, font_desc, link);
+    int width, height;
+    pango_layout_get_pixel_size(mLayout, &width, &height);
+
+    if (width < mWidth - MARGIN * 2 && height < mHeight - MARGIN * 2) {
+        cairo_move_to(cr, mX + MARGIN, mY + MARGIN);
+        set_source_from_rgba(cr, foreground);
+        pango_cairo_show_layout(cr, mLayout);
+    }
+    
+    cairo_surface_destroy(surface);
+}
+
+void
+nsPluginInstance::handleButtonPress(XButtonEvent *xev)
+{
+}
+
+void
+nsPluginInstance::handleButtonRelease(XButtonEvent *xev)
+{
+    if (!mDesktopFile.empty())
+        runApplication();
+    else if (!mAvailablePackageName.empty())
+        installPackage();
+}
+
+void
+nsPluginInstance::handleMotionNotify(XMotionEvent *xev)
+{
+}
+
+void
+nsPluginInstance::handleEnterNotify(XCrossingEvent *xev)
+{
+}
+
+void
+nsPluginInstance::handleLeaveNotify(XCrossingEvent *xev)
+{
+}
+
+uint16
+nsPluginInstance::HandleEvent(void *event)
+{
+    XEvent *xev = (XEvent *)event;
+
+    switch (xev->xany.type) {
+    case GraphicsExpose:
+        handleGraphicsExpose((XGraphicsExposeEvent *)event);
+        return 1;
+    case ButtonPress:
+        handleButtonPress((XButtonEvent *)event);
+        return 1;
+    case ButtonRelease:
+        handleButtonRelease((XButtonEvent *)event);
+        return 1;
+    case MotionNotify:
+        handleMotionNotify((XMotionEvent *)event);
+        return 1;
+    case EnterNotify:
+        handleEnterNotify((XCrossingEvent *)event);
+        return 1;
+    case LeaveNotify:
+        handleLeaveNotify((XCrossingEvent *)event);
+        return 1;
+    }
+
+    return 0;
+}
+
+static guint32
+get_server_timestamp()
+{
+    GtkWidget *invisible = gtk_invisible_new();
+    gtk_widget_realize(invisible);
+    return gdk_x11_get_server_time(invisible->window);
+    gtk_widget_destroy(invisible);
+}
+
+static gboolean
+validate_name(const char *name)
+{
+    const char *p;
+    
+    for (p = name; *p; p++) {
+        char c = *p;
+        
+        if (!((c >= 'A' && c <= 'Z') ||
+              (c >= 'a' && c <= 'z') ||
+              (c >= '0' && c <= '9') ||
+              (c == '.') ||
+              (c == '_') ||
+              (c == '-')))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+void
+nsPluginInstance::findDesktopFile()
+{
+    for (std::vector<std::string>::iterator i = mDesktopNames.begin(); i != mDesktopNames.end(); i++) {
+        if (!validate_name(i->c_str())) {
+            g_warning("Bad desktop name: '%s'", i->c_str());
+            continue;
+        }
+        
+        char *filename = g_strconcat(i->c_str(), ".desktop", NULL);
+        char *path = g_build_filename(APPLICATION_DIR, filename, NULL);
+        g_free(filename);
+
+        if (g_file_test(path, G_FILE_TEST_EXISTS)) {
+            mDesktopFile = path;
+            break;
+        }
+
+        g_free(path);
+    }
+
+    if (!mDesktopFile.empty())
+        setStatus(INSTALLED);
+}
+                  
+void
+nsPluginInstance::runApplication (void)
+{
+    GError *error = NULL;
+    
+    /* This is idempotent and fairly cheap, so do it here to avoid initializing
+     * gnome-vfs on plugin startup
+     */
+    gnome_vfs_init();
+
+    if (mDesktopFile.c_str() == 0) {
+        g_warning("Didn't find application to launch");
+        return;
+    }
+
+    GnomeDesktopItem *item = gnome_desktop_item_new_from_file(mDesktopFile.c_str(), GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS, &error);
+    if (!item) {
+        g_warning("%s\n", error->message);
+        g_clear_error(&error);
+        gnome_desktop_item_unref(item);
+        return;
+    }
+
+    guint32 launch_time = gtk_get_current_event_time();
+    if (launch_time == GDK_CURRENT_TIME)
+        launch_time = get_server_timestamp();
+
+    if (!gnome_desktop_item_launch(item, NULL, (GnomeDesktopItemLaunchFlags)0, &error)) {
+        g_warning("%s\n", error->message);
+        g_clear_error(&error);
+        gnome_desktop_item_unref(item);
+        return;
+    }
+}
+
+void
+nsPluginInstance::installPackage (void)
+{
+    if (mAvailablePackageName.empty()) {
+        g_warning("No available package to install");
+        return;
+    }
+
+    if (mInstallPackageHandle != 0) {
+        g_warning("Already installing package");
+        return;
+    }
+
+    char *argv[3];
+    argv[0] = (char *)"gpk-install-package";
+    argv[1] = (char *)mAvailablePackageName.c_str();
+    argv[2] = 0;
+
+    mInstallPackageHandle = pkp_execute_command_async(argv, onInstallFinished, this);
+    setStatus(INSTALLING);
+}
+
+void
+nsPluginInstance::onClientPackage(PkClient	  *client,
+                                  PkInfoEnum	   info,
+                                  const gchar	   *package_id,
+                                  const gchar	   *summary,
+                                  nsPluginInstance *instance)
+{
+    fprintf(stderr, "package: %d %s %s\n", info, package_id, summary);
+
+    PkPackageId *id = pk_package_id_new_from_string(package_id);
+    
+    if (info == PK_INFO_ENUM_AVAILABLE) {
+        if (instance->getStatus() != INSTALLED)
+            instance->setStatus(AVAILABLE);
+        instance->setAvailableVersion(id->version);
+        instance->setAvailablePackageName(id->name);
+    } else if (info == PK_INFO_ENUM_INSTALLED) {
+        instance->setStatus(INSTALLED);
+        instance->setInstalledVersion(id->version);
+    }
+    
+    pk_package_id_free(id);
+}
+
+void
+nsPluginInstance::onClientErrorCode(PkClient	     *client,
+                                    PkErrorCodeEnum   code,
+                                    const gchar	     *details,
+                                    nsPluginInstance *instance)
+{
+    fprintf(stderr, "error code: %d %s\n", code, details);
+    instance->removeClient(client);
+}
+
+void
+nsPluginInstance::onClientFinished(PkClient	    *client,
+                                   PkExitEnum	     exit,
+                                   guint	     runtime,
+                                   nsPluginInstance *instance)
+{
+    fprintf(stderr, "finished: %d\n", exit);
+    instance->removeClient(client);
+}
+    
+void
+nsPluginInstance::onInstallFinished(GError        *error,
+                                    int            status,
+                                    const char    *output,
+                                    void          *callback_data)
+{
+    nsPluginInstance *instance = (nsPluginInstance *)callback_data;
+
+    instance->mInstallPackageHandle = 0;
+    
+    if (error) {
+        g_warning("Error occurred during install: %s", error->message);
+    }
+
+    if (status != 0) {
+        g_warning("gpk-install-command exited with non-zero status %d", status);
+    }
+
+    instance->recheck();
+}
diff --git a/src/plugin.h b/src/plugin.h
new file mode 100644
index 0000000..59c1255
--- /dev/null
+++ b/src/plugin.h
@@ -0,0 +1,142 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __PLUGIN_H__
+#define __PLUGIN_H__
+
+#include <X11/Xlib.h>
+#include <pango/pango.h>
+#include <packagekit/pk-client.h>
+#include <cairo.h>
+#include <gtk/gtk.h>
+
+#include <string>
+#include <vector>
+
+#include "pluginbase.h"
+#include "util.h"
+
+enum PackageStatus { IN_PROGRESS, INSTALLED, AVAILABLE, UNAVAILABLE, INSTALLING };
+
+class nsPluginInstance : public nsPluginInstanceBase
+{
+public:
+    nsPluginInstance(NPP aInstance, const char *displayName, const char *packageNames, const char *desktopNames);
+    virtual ~nsPluginInstance();
+
+    NPBool init(NPWindow* aWindow);
+    void shut();
+    NPBool isInitialized() {return mInitialized;}
+    NPError GetValue(NPPVariable variable, void *value);
+    NPError SetWindow(NPWindow* aWindow);
+    uint16 HandleEvent(void *event);
+
+    void setStatus(PackageStatus status);
+    PackageStatus getStatus() { return mStatus; }
+    void setAvailableVersion(const char *version);
+    void setAvailablePackageName(const char *name);
+    void setInstalledVersion(const char *version);
+
+private:
+    void recheck();
+    void findDesktopFile();
+    void runApplication();
+    void installPackage();
+    
+    void ensureLayout(cairo_t *cr,
+                      PangoFontDescription *font_desc,
+                      guint32 link_color);
+    void clearLayout();
+    void refresh();
+    
+    void handleGraphicsExpose(XGraphicsExposeEvent *xev);
+    void handleButtonPress(XButtonEvent *xev);
+    void handleButtonRelease(XButtonEvent *xev);
+    void handleMotionNotify(XMotionEvent *xev);
+    void handleEnterNotify(XCrossingEvent *xev);
+    void handleLeaveNotify(XCrossingEvent *xev);
+
+    void removeClient(PkClient *client);
+    
+    static void onClientPackage(PkClient 	   *client,
+                                PkInfoEnum	    info,
+                                const gchar	   *package_id,
+                                const gchar	   *summary,
+                                nsPluginInstance   *instance);
+    static void onClientErrorCode(PkClient	   *client,
+                                  PkErrorCodeEnum  code,
+                                  const gchar	   *details,
+                                  nsPluginInstance *instance);
+    static void onClientFinished(PkClient	  *client,
+                                 PkExitEnum	   exit,
+                                 guint		   runtime,
+                                 nsPluginInstance *instance);
+    static void onInstallFinished(GError        *error,
+                                  int            status,
+                                  const char    *output,
+                                  void          *callback_data);
+    
+    NPP mInstance;
+    NPBool mInitialized;
+    PackageStatus mStatus;
+    std::string mAvailableVersion;
+    std::string mAvailablePackageName;
+    std::string mInstalledVersion;
+    std::string mDesktopFile;
+
+    std::string mDisplayName;
+    std::vector<std::string> mPackageNames;
+    std::vector<std::string> mDesktopNames;
+    
+    Window mWindow;
+    Display *mDisplay;
+    int mX, mY;
+    int mWidth, mHeight;
+    Visual* mVisual;
+    Colormap mColormap;
+    unsigned int mDepth;
+  
+    PangoLayout *mLayout;
+    int mLinkStart;
+    int mLinkEnd;
+
+    std::vector<PkClient *> mClients;
+
+    PkpExecuteCommandAsyncHandle *mInstallPackageHandle;
+};
+
+#endif // __PLUGIN_H__
diff --git a/src/util.cpp b/src/util.cpp
new file mode 100644
index 0000000..de8629e
--- /dev/null
+++ b/src/util.cpp
@@ -0,0 +1,187 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is PackageKit plugin code.
+ *
+ * The Initial Developer of the Original Code is
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <config.h>
+#include "util.h"
+
+struct PkpExecuteCommandAsyncHandle {
+    PkpExecuteCommandCallback callback;
+    void *callback_data;
+
+    GError *error;
+    int status;
+    GString *output;
+
+    guint io_watch;
+    guint child_watch;
+    
+    gboolean exited;
+    gboolean closed;
+};
+    
+static void
+pkp_execute_command_async_free(PkpExecuteCommandAsyncHandle *handle)
+{
+    if (handle->io_watch)
+        g_source_remove(handle->io_watch);
+    if (handle->child_watch)
+        g_source_remove(handle->child_watch);
+    
+    if (handle->error)
+        g_error_free(handle->error);
+    
+    g_string_free(handle->output, TRUE);
+    g_free(handle);
+}
+
+static void
+pkp_execute_command_async_finish(PkpExecuteCommandAsyncHandle *handle)
+{
+    handle->callback(handle->error, handle->status, handle->output->str, handle->callback_data);
+
+    pkp_execute_command_async_free(handle);
+}
+     
+static void
+pkp_execute_async_child_watch(GPid     pid,
+			      int      status,
+			      gpointer data)
+{
+    PkpExecuteCommandAsyncHandle *handle = (PkpExecuteCommandAsyncHandle *)data;
+
+    handle->exited = TRUE;
+    handle->child_watch = 0;
+    handle->status = status;
+
+    if (handle->exited && handle->closed)
+        pkp_execute_command_async_finish(handle);
+}
+    
+static gboolean
+pkp_execute_async_io_watch(GIOChannel   *source,
+			   GIOCondition  condition,
+			   gpointer      data)
+{
+    PkpExecuteCommandAsyncHandle *handle = (PkpExecuteCommandAsyncHandle *)data;
+    GIOStatus status;
+    char buf[1024];
+    gsize bytes_read;
+
+    handle->io_watch = 0;
+    
+    status = g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, &handle->error);
+    switch (status) {
+    case G_IO_STATUS_ERROR:
+        g_io_channel_close(source);
+        handle->closed = TRUE;
+        if (handle->exited && handle->closed)
+            pkp_execute_command_async_finish(handle);
+        return FALSE;
+    case G_IO_STATUS_NORMAL:
+        g_string_append_len(handle->output, buf, bytes_read);
+        break;
+    case G_IO_STATUS_EOF:
+        g_io_channel_close(source);
+        handle->closed = TRUE;
+        if (handle->exited && handle->closed)
+            pkp_execute_command_async_finish(handle);
+        
+        return FALSE;
+    case G_IO_STATUS_AGAIN:
+        /* Should not be reached */
+        break;
+    }
+
+    return TRUE;
+}
+
+PkpExecuteCommandAsyncHandle *
+pkp_execute_command_async(char                     **argv,
+			  PkpExecuteCommandCallback  callback,
+			  void                      *callback_data)
+{
+    PkpExecuteCommandAsyncHandle *handle;
+    GPid child_pid;
+    int out_fd;
+    GIOChannel *channel;
+    const char *locale_encoding;
+
+    handle = g_new(PkpExecuteCommandAsyncHandle, 1);
+    
+    handle->callback = callback;
+    handle->callback_data = callback_data;
+    
+    handle->error = NULL;
+    handle->status = -1;
+    handle->output = g_string_new(NULL);
+
+    handle->closed = FALSE;
+    handle->exited = FALSE;
+    
+    if (!g_spawn_async_with_pipes(NULL, argv, NULL,
+                                  (GSpawnFlags)(G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD),
+                                  NULL, NULL,
+                                  &child_pid,
+                                  NULL, &out_fd, NULL,
+                                  &handle->error)) {
+        pkp_execute_command_async_finish(handle);
+        return NULL;
+    }
+
+    channel = g_io_channel_unix_new(out_fd);
+    
+    g_get_charset(&locale_encoding);
+    g_io_channel_set_encoding(channel, locale_encoding, NULL);
+
+    handle->io_watch = g_io_add_watch(channel,
+                                      (GIOCondition)(G_IO_IN | G_IO_HUP),
+                                      pkp_execute_async_io_watch,
+                                      handle);
+    g_io_channel_unref(channel);
+
+    handle->child_watch = g_child_watch_add(child_pid,
+                                            pkp_execute_async_child_watch,
+                                            handle);
+
+    return handle;
+}
+
+void
+pkp_execute_command_async_cancel (PkpExecuteCommandAsyncHandle *handle)
+{
+    pkp_execute_command_async_free(handle);
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..daff9a0
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,52 @@
+/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is PackageKit plugin code.
+ *
+ * The Initial Developer of the Original Code is
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <glib.h>
+
+struct PkpExecuteCommandAsyncHandle;
+
+typedef void (*PkpExecuteCommandCallback) (GError                    *error,
+					   int                        status,
+					   const char                *output,
+					   void                      *callback_data);
+
+PkpExecuteCommandAsyncHandle *pkp_execute_command_async (char                         **argv,
+							 PkpExecuteCommandCallback      callback,
+							 void                          *callback_data);
+
+/* This doesn't actually try to kill the command, it just cancels the callback */
+void pkp_execute_command_async_cancel (PkpExecuteCommandAsyncHandle *handle);
diff --git a/tests/test.html b/tests/test.html
new file mode 100644
index 0000000..7afb043
--- /dev/null
+++ b/tests/test.html
@@ -0,0 +1,31 @@
+<html>
+  <head>
+    <title>Test for PackageKit Plugin</title>
+    <style type="text/css">
+      body {
+        background: #ffeedd;
+      }
+      
+      .packagekit-plugin {
+         width: 30em;
+         height: 5em;
+      }
+    </style>
+  </head>
+  <body>
+    <object type="application/x-packagekit-plugin" width="500" height="200" class="packagekit-plugin">
+<!--      <param name="packagenames" value="firefox mozilla-firefox"/>
+      <param name="desktopnames" value="firefox mozilla-firefox"/>
+      -->
+<!--      <param name="displayname" value="KStars"/>
+      <param name="packagenames" value="kdeedu"/>
+      <param name="desktopnames" value="kstars"/> -->
+<!--      <param name="displayname" value="Mugshot"/>
+      <param name="packagenames" value="mugshot"/>
+      <param name="desktopnames" value="mugshot"/>  -->
+      <param name="displayname" value="GNU Backgammon"/>
+      <param name="packagenames" value="gnubg"/>
+      <param name="desktopnames" value="fedora-gnubg"/>
+    </object>
+  </body>
+</hml>
\ No newline at end of file


More information about the PackageKit-commit mailing list