summaryrefslogtreecommitdiff
path: root/packages/packagekit/files/d1e096c3267c1c9492041382b954e9327bc8bbec.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packages/packagekit/files/d1e096c3267c1c9492041382b954e9327bc8bbec.patch')
-rw-r--r--packages/packagekit/files/d1e096c3267c1c9492041382b954e9327bc8bbec.patch2938
1 files changed, 2938 insertions, 0 deletions
diff --git a/packages/packagekit/files/d1e096c3267c1c9492041382b954e9327bc8bbec.patch b/packages/packagekit/files/d1e096c3267c1c9492041382b954e9327bc8bbec.patch
new file mode 100644
index 0000000000..f050a50002
--- /dev/null
+++ b/packages/packagekit/files/d1e096c3267c1c9492041382b954e9327bc8bbec.patch
@@ -0,0 +1,2938 @@
+diff --git a/backends/alpm/pk-backend-alpm.c b/backends/alpm/pk-backend-alpm.c
+index 3270e42..45e7a44 100644
+--- a/backends/alpm/pk-backend-alpm.c
++++ b/backends/alpm/pk-backend-alpm.c
+@@ -895,7 +895,7 @@ backend_get_repo_list (PkBackend *backend, PkFilterEnum filters)
+ static gboolean
+ backend_install_files_thread (PkBackend *backend)
+ {
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ gchar **full_paths = pk_backend_get_strv (backend, "full_paths");
+
+@@ -1050,7 +1050,7 @@ backend_install_packages (PkBackend *backend, gchar **package_ids)
+ static gboolean
+ backend_refresh_cache_thread (PkBackend *backend)
+ {
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ if (alpm_trans_init (PM_TRANS_TYPE_SYNC, PM_TRANS_FLAG_NOSCRIPTLET, cb_trans_evt, cb_trans_conv, cb_trans_progress) != 0) {
+ pk_backend_error_code (backend, PK_ERROR_ENUM_TRANSACTION_ERROR, alpm_strerror (pm_errno));
+diff --git a/backends/apt/pk-apt-build-db.cpp b/backends/apt/pk-apt-build-db.cpp
+index d47c348..885275d 100644
+--- a/backends/apt/pk-apt-build-db.cpp
++++ b/backends/apt/pk-apt-build-db.cpp
+@@ -40,7 +40,7 @@ void apt_build_db(PkBackend * backend, sqlite3 *db)
+ sqlite3_stmt *package = NULL;
+
+ pk_backend_set_status(backend, PK_STATUS_ENUM_QUERY);
+- pk_backend_no_percentage_updates(backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ sdir = g_build_filename(_config->Find("Dir").c_str(),_config->Find("Dir::State").c_str(),_config->Find("Dir::State::lists").c_str(), NULL);
+ dir = g_dir_open(sdir,0,&error);
+diff --git a/backends/apt/pk-sqlite-pkg-cache.cpp b/backends/apt/pk-sqlite-pkg-cache.cpp
+index 770fcdf..1bf9a50 100644
+--- a/backends/apt/pk-sqlite-pkg-cache.cpp
++++ b/backends/apt/pk-sqlite-pkg-cache.cpp
+@@ -81,7 +81,7 @@ sqlite_search_packages_thread (PkBackend *backend)
+ const gchar *search;
+
+ pk_backend_set_status(backend, PK_STATUS_ENUM_QUERY);
+- pk_backend_no_percentage_updates(backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+ type = pk_backend_get_uint (backend, "type");
+ search = pk_backend_get_string (backend, "search");
+
+@@ -176,7 +176,7 @@ sqlite_get_details_thread (PkBackend *backend)
+ }
+
+ pk_backend_set_status(backend, PK_STATUS_ENUM_QUERY);
+- pk_backend_no_percentage_updates(backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ pk_debug("finding %s", pi->name);
+
+diff --git a/backends/box/pk-backend-box.c b/backends/box/pk-backend-box.c
+index b7b8167..9263781 100644
+--- a/backends/box/pk-backend-box.c
++++ b/backends/box/pk-backend-box.c
+@@ -138,7 +138,7 @@ backend_find_packages_thread (PkBackend *backend)
+ filter_box = filter_box | PKG_SEARCH_DETAILS;
+ }
+
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ db = db_open();
+
+diff --git a/backends/dummy/pk-backend-dummy.c b/backends/dummy/pk-backend-dummy.c
+index 49d4e5a..2df445e 100644
+--- a/backends/dummy/pk-backend-dummy.c
++++ b/backends/dummy/pk-backend-dummy.c
+@@ -254,7 +254,7 @@ static void
+ backend_get_updates (PkBackend *backend, PkFilterEnum filters)
+ {
+ pk_backend_set_status (backend, PK_STATUS_ENUM_QUERY);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+ /* check network state */
+ if (!pk_backend_is_online (backend)) {
+ pk_backend_error_code (backend, PK_ERROR_ENUM_NO_NETWORK, "Cannot check when offline");
+@@ -268,6 +268,8 @@ static gboolean
+ backend_install_timeout (gpointer data)
+ {
+ PkBackend *backend = (PkBackend *) data;
++ guint sub_percent;
++
+ if (_progress_percentage == 100) {
+ pk_backend_finished (backend);
+ return FALSE;
+@@ -283,9 +285,18 @@ backend_install_timeout (gpointer data)
+ pk_backend_package (backend, PK_INFO_ENUM_INSTALLING,
+ "gtkhtml2-devel;2.19.1-0.fc8;i386;fedora",
+ "Devel files for gtkhtml");
++ /* this duplicate package should be ignored */
++ pk_backend_package (backend, PK_INFO_ENUM_INSTALLING,
++ "gtkhtml2-devel;2.19.1-0.fc8;i386;fedora", NULL);
+ pk_backend_set_status (backend, PK_STATUS_ENUM_INSTALL);
+ }
+- _progress_percentage += 10;
++ if (_progress_percentage > 30 && _progress_percentage < 50) {
++ sub_percent = ((gfloat) (_progress_percentage - 30.0f) / 20.0f) * 100.0f;
++ pk_backend_set_sub_percentage (backend, sub_percent);
++ } else {
++ pk_backend_set_sub_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
++ }
++ _progress_percentage += 1;
+ pk_backend_set_percentage (backend, _progress_percentage);
+ return TRUE;
+ }
+@@ -348,7 +359,7 @@ backend_install_packages (PkBackend *backend, gchar **package_ids)
+ pk_backend_package (backend, PK_INFO_ENUM_DOWNLOADING,
+ "gtkhtml2;2.19.1-4.fc8;i386;fedora",
+ "An HTML widget for GTK+ 2.0");
+- _signal_timeout = g_timeout_add (1000, backend_install_timeout, backend);
++ _signal_timeout = g_timeout_add (100, backend_install_timeout, backend);
+ }
+
+ /**
+@@ -526,7 +537,7 @@ backend_search_name_timeout (gpointer data)
+ static void
+ backend_search_name (PkBackend *backend, PkFilterEnum filters, const gchar *search)
+ {
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+ pk_backend_set_allow_cancel (backend, TRUE);
+ pk_backend_set_status (backend, PK_STATUS_ENUM_QUERY);
+ _signal_timeout = g_timeout_add (2000, backend_search_name_timeout, backend);
+diff --git a/backends/opkg/pk-backend-opkg.c b/backends/opkg/pk-backend-opkg.c
+index ecc97be..7649bab 100644
+--- a/backends/opkg/pk-backend-opkg.c
++++ b/backends/opkg/pk-backend-opkg.c
+@@ -155,6 +155,38 @@ pk_opkg_progress_cb (opkg_t *opkg, const opkg_progress_data_t *pdata, void *data
+ return;
+
+ pk_backend_set_percentage (backend, pdata->percentage);
++ if (pdata->package)
++ {
++ gchar *uid;
++ opkg_package_t *pkg = pdata->package;
++ gint status = PK_INFO_ENUM_UNKNOWN;
++
++ uid = g_strdup_printf ("%s;%s;%s;",
++ pkg->name, pkg->version, pkg->architecture);
++
++ if (pdata->action == OPKG_DOWNLOAD)
++ status = PK_INFO_ENUM_DOWNLOADING;
++ else if (pdata->action == OPKG_INSTALL)
++ status = PK_INFO_ENUM_INSTALLING;
++ else if (pdata->action == OPKG_REMOVE)
++ status = PK_INFO_ENUM_REMOVING;
++
++ pk_backend_package (backend, status, uid, pkg->description);
++ g_free (uid);
++ }
++
++ switch (pdata->action)
++ {
++ case OPKG_DOWNLOAD:
++ pk_backend_set_status (backend, PK_STATUS_ENUM_DOWNLOAD);
++ break;
++ case OPKG_INSTALL:
++ pk_backend_set_status (backend, PK_STATUS_ENUM_INSTALL);
++ break;
++ case OPKG_REMOVE:
++ pk_backend_set_status (backend, PK_STATUS_ENUM_REMOVE);
++ break;
++ }
+ }
+
+ static gboolean
+@@ -163,8 +195,12 @@ backend_refresh_cache_thread (PkBackend *backend)
+ int ret;
+
+ ret = opkg_update_package_lists (opkg, pk_opkg_progress_cb, backend);
++
+ if (ret) {
+- opkg_unknown_error (backend, ret, "Refreshing cache");
++ if (ret == OPKG_DOWNLOAD_FAILED)
++ pk_backend_error_code (backend, PK_ERROR_ENUM_REPO_NOT_AVAILABLE, NULL);
++ else
++ opkg_unknown_error (backend, ret, "Refreshing cache");
+ }
+ pk_backend_finished (backend);
+
+@@ -178,7 +214,7 @@ static void
+ backend_refresh_cache (PkBackend *backend, gboolean force)
+ {
+ pk_backend_set_status (backend, PK_STATUS_ENUM_REFRESH_CACHE);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+
+ pk_backend_thread_create (backend, backend_refresh_cache_thread);
+@@ -279,7 +315,7 @@ backend_search_name (PkBackend *backend, PkFilterEnum filters, const gchar *sear
+
+
+ pk_backend_set_status (backend, PK_STATUS_ENUM_QUERY);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ params = g_new0 (SearchParams, 1);
+ params->filters = filters;
+@@ -301,7 +337,7 @@ backend_search_description (PkBackend *backend, PkFilterEnum filters, const gcha
+
+
+ pk_backend_set_status (backend, PK_STATUS_ENUM_QUERY);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ params = g_new0 (SearchParams, 1);
+ params->filters = filters;
+@@ -320,7 +356,7 @@ backend_search_group (PkBackend *backend, PkFilterEnum filters, const gchar *sea
+
+
+ pk_backend_set_status (backend, PK_STATUS_ENUM_QUERY);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ params = g_new0 (SearchParams, 1);
+ params->filters = filters;
+@@ -337,30 +373,52 @@ static gboolean
+ backend_install_packages_thread (PkBackend *backend)
+ {
+ PkPackageId *pi;
+- gint err;
+- const gchar *package_id;
++ gint err, i;
++ gchar **package_ids;
+
+- package_id = pk_backend_get_string (backend, "pkid");
+- pk_backend_package (backend, PK_INFO_ENUM_INSTALLING, package_id, NULL);
++ package_ids = pk_backend_get_strv (backend, "pkids");
+
+- pi = pk_package_id_new_from_string (package_id);
++ err = 0;
++
++ for (i = 0; package_ids[i]; i++)
++ {
++ pk_backend_package (backend, PK_INFO_ENUM_INSTALLING, package_ids[0], NULL);
+
+- err = opkg_install_package (opkg, pi->name, pk_opkg_progress_cb, backend);
+- if (err != 0)
+- opkg_unknown_error (backend, err, "Install");
++ pi = pk_package_id_new_from_string (package_ids[0]);
++
++ err = opkg_install_package (opkg, pi->name, pk_opkg_progress_cb, backend);
++ switch (err)
++ {
++ case OPKG_NO_ERROR:
++ break;
++ case OPKG_DEPENDANCIES_FAILED:
++ pk_backend_error_code (backend, PK_ERROR_ENUM_DEP_RESOLUTION_FAILED, NULL);
++ break;
++ case OPKG_PACKAGE_ALREADY_INSTALLED:
++ pk_backend_error_code (backend, PK_ERROR_ENUM_PACKAGE_ALREADY_INSTALLED, NULL);
++ break;
++ case OPKG_PACKAGE_NOT_AVAILABLE:
++ pk_backend_error_code (backend, PK_ERROR_ENUM_PACKAGE_NOT_FOUND, NULL);
++ break;
++ default:
++ opkg_unknown_error (backend, err, "Install");
++ }
++ pk_package_id_free (pi);
++ if (err != 0)
++ break;
++ }
+
+- pk_package_id_free (pi);
+ pk_backend_finished (backend);
+ return (err == 0);
+ }
+
+ static void
+-backend_install_packages (PkBackend *backend, gchar **package_id)
++backend_install_packages (PkBackend *backend, gchar **package_ids)
+ {
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+ pk_backend_set_status (backend, PK_STATUS_ENUM_INSTALL);
+
+- pk_backend_set_string (backend, "pkid", package_id[0]);
++ pk_backend_set_strv (backend, "pkids", package_ids);
+
+ pk_backend_thread_create (backend, backend_install_packages_thread);
+ }
+@@ -369,7 +427,7 @@ static gboolean
+ backend_remove_packages_thread (PkBackend *backend)
+ {
+ PkPackageId *pi;
+- gint err;
++ gint err, i;
+ gchar **package_ids;
+ gboolean allow_deps;
+ gboolean autoremove;
+@@ -382,19 +440,34 @@ backend_remove_packages_thread (PkBackend *backend)
+ autoremove = GPOINTER_TO_INT (data[2]);
+ g_free (data);
+
+- pi = pk_package_id_new_from_string (package_ids[0]);
+- pk_backend_package (backend, PK_INFO_ENUM_REMOVING, package_ids[0], NULL);
+-
+ opkg_set_option (opkg, "autoremove", &autoremove);
+ opkg_set_option (opkg, "force_removal_of_dependent_packages", &allow_deps);
+
+- err = opkg_remove_package (opkg, pi->name, pk_opkg_progress_cb, backend);
++ err = 0;
+
+- /* TODO: improve error reporting */
+- if (err != 0)
+- opkg_unknown_error (backend, err, "Remove");
++ for (i = 0; package_ids[i]; i++)
++ {
++ pi = pk_package_id_new_from_string (package_ids[0]);
++ pk_backend_package (backend, PK_INFO_ENUM_REMOVING, package_ids[0], NULL);
++
++ err = opkg_remove_package (opkg, pi->name, pk_opkg_progress_cb, backend);
++
++ switch (err)
++ {
++ case OPKG_NO_ERROR:
++ break;
++ case OPKG_PACKAGE_NOT_INSTALLED:
++ pk_backend_error_code (backend, PK_ERROR_ENUM_PACKAGE_NOT_INSTALLED, NULL);
++ break;
++ default:
++ opkg_unknown_error (backend, err, "Remove");
++ }
++ pk_package_id_free (pi);
++
++ if (err != 0)
++ break;
++ }
+
+- pk_package_id_free (pi);
+ pk_backend_finished (backend);
+ return (err == 0);
+ }
+@@ -405,7 +478,7 @@ backend_remove_packages (PkBackend *backend, gchar **package_ids, gboolean allow
+ gpointer *params;
+
+ pk_backend_set_status (backend, PK_STATUS_ENUM_REMOVE);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ /* params is a small array we can pack our thread parameters into */
+ params = g_new0 (gpointer, 2);
+@@ -450,7 +523,7 @@ static void
+ backend_update_system (PkBackend *backend)
+ {
+ pk_backend_set_status (backend, PK_STATUS_ENUM_UPDATE);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ pk_backend_thread_create (backend, backend_update_system_thread);
+ }
+@@ -478,8 +551,14 @@ backend_update_package_thread (PkBackend *backend)
+ }
+
+ err = opkg_upgrade_package (opkg, pi->name, pk_opkg_progress_cb, backend);
+-
+- if (err != 0) {
++ switch (err)
++ {
++ case OPKG_NO_ERROR:
++ break;
++ case OPKG_PACKAGE_NOT_INSTALLED:
++ pk_backend_error_code (backend, PK_ERROR_ENUM_PACKAGE_NOT_INSTALLED, NULL);
++ break;
++ default:
+ opkg_unknown_error (backend, err, "Update package");
+ }
+
+@@ -494,7 +573,7 @@ backend_update_packages (PkBackend *backend, gchar **package_ids)
+ gint i;
+
+ pk_backend_set_status (backend, PK_STATUS_ENUM_UPDATE);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ for (i = 0; package_ids[i]; i++) {
+ pk_backend_set_string (backend, "pkgid", package_ids[i]);
+@@ -536,7 +615,7 @@ static void
+ backend_get_updates (PkBackend *backend, PkFilterEnum filters)
+ {
+ pk_backend_set_status (backend, PK_STATUS_ENUM_UPDATE);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ pk_backend_thread_create (backend, backend_get_updates_thread);
+ }
+@@ -572,19 +651,26 @@ backend_get_details_thread (PkBackend *backend)
+ if (pi == NULL)
+ {
+ pk_backend_error_code (backend, PK_ERROR_ENUM_PACKAGE_ID_INVALID, "invalid package id");
+- pk_package_id_free (pi);
++ pk_backend_finished (backend);
+ return FALSE;
+ }
+
+
+ pkg = opkg_find_package (opkg, pi->name, pi->version, pi->arch, pi->data);
++ pk_package_id_free (pi);
++
++ if (!pkg)
++ {
++ pk_backend_error_code (backend, PK_ERROR_ENUM_PACKAGE_NOT_FOUND, NULL);
++ pk_backend_finished (backend);
++ return FALSE;
++ }
+
+ newid = g_strdup_printf ("%s;%s;%s;%s", pkg->name, pkg->version, pkg->architecture, pkg->repository);
+
+ pk_backend_details (backend, newid, NULL, 0, pkg->description, pkg->url, pkg->size);
+
+ g_free (newid);
+- pk_package_id_free (pi);
+ pk_backend_finished (backend);
+ return TRUE;
+ }
+@@ -592,7 +678,7 @@ backend_get_details_thread (PkBackend *backend)
+ static void
+ backend_get_details (PkBackend *backend, const gchar *package_id)
+ {
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+ pk_backend_thread_create (backend, backend_get_details_thread);
+ }
+
+diff --git a/backends/test/pk-backend-test-dbus.c b/backends/test/pk-backend-test-dbus.c
+index 5dfea32..76686dc 100644
+--- a/backends/test/pk-backend-test-dbus.c
++++ b/backends/test/pk-backend-test-dbus.c
+@@ -36,7 +36,7 @@ static void
+ backend_search_name (PkBackend *backend, PkFilterEnum filters, const gchar *search)
+ {
+ pk_backend_set_allow_cancel (backend, TRUE);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+ pk_backend_dbus_search_name (dbus, filters, search);
+ }
+
+diff --git a/backends/test/pk-backend-test-spawn.c b/backends/test/pk-backend-test-spawn.c
+index 2958c05..584f44c 100644
+--- a/backends/test/pk-backend-test-spawn.c
++++ b/backends/test/pk-backend-test-spawn.c
+@@ -35,7 +35,7 @@ backend_search_name (PkBackend *backend, PkFilterEnum filters, const gchar *sear
+ {
+ gchar *filters_text;
+ pk_backend_set_allow_cancel (backend, TRUE);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+ filters_text = pk_filter_enums_to_text (filters);
+ pk_backend_spawn_helper (spawn, "search-name.sh", filters_text, search, NULL);
+ g_free (filters_text);
+diff --git a/backends/test/pk-backend-test-succeed.c b/backends/test/pk-backend-test-succeed.c
+index 17cdc6e..c046c1d 100644
+--- a/backends/test/pk-backend-test-succeed.c
++++ b/backends/test/pk-backend-test-succeed.c
+@@ -236,7 +236,7 @@ backend_search_name_timeout (gpointer data)
+ static void
+ backend_search_name (PkBackend *backend, PkFilterEnum filters, const gchar *search)
+ {
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+ g_timeout_add (200000, backend_search_name_timeout, backend);
+ }
+
+diff --git a/backends/yum/helpers/yumBackend.py b/backends/yum/helpers/yumBackend.py
+index f76e27c..5b2da8f 100644
+--- a/backends/yum/helpers/yumBackend.py
++++ b/backends/yum/helpers/yumBackend.py
+@@ -801,6 +801,8 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ old_throttle = self.yumbase.conf.throttle
+ self.yumbase.conf.throttle = "60%" # Set bandwidth throttle to 60%
+ # to avoid taking all the system's bandwidth.
++ old_skip_broken = self.yumbase.conf.skip_broken
++ self.yumbase.conf.skip_broken = 1
+
+ try:
+ txmbr = self.yumbase.update() # Add all updates to Transaction
+@@ -812,6 +814,7 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ self.error(ERROR_NO_PACKAGES_TO_UPDATE,"Nothing to do")
+
+ self.yumbase.conf.throttle = old_throttle
++ self.yumbase.conf.skip_broken = old_skip_broken
+
+ def refresh_cache(self):
+ '''
+@@ -905,7 +908,7 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ if txmbrs:
+ self._runYumTransaction()
+ else:
+- self.error(ERROR_PACKAGE_ALREADY_INSTALLED,"This package could not be installed as it is already installed")
++ self.error(ERROR_PACKAGE_ALREADY_INSTALLED,"The package is already installed")
+
+ def _checkForNewer(self,po):
+ pkgs = self.yumbase.pkgSack.returnNewestByName(name=po.name)
+@@ -1003,7 +1006,7 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ return False
+
+ if self._is_inst(po):
+- self.error(ERROR_PACKAGE_ALREADY_INSTALLED, "%s is already installed" % str(po))
++ self.error(ERROR_PACKAGE_ALREADY_INSTALLED, "The package %s is already installed" % str(po))
+ return False
+
+ if len(self.yumbase.conf.exclude) > 0:
+diff --git a/backends/yum2/helpers/testyum2.py b/backends/yum2/helpers/testyum2.py
+index cdec507..85b47f9 100755
+--- a/backends/yum2/helpers/testyum2.py
++++ b/backends/yum2/helpers/testyum2.py
+@@ -80,7 +80,7 @@ try:
+ #iface.GetPackages(FILTER_INSTALLED,'no')
+ if cmd == 'get-repolist' or cmd == 'all':
+ print "Testing GetRepoList()"
+- iface.GetRepoList()
++ iface.GetRepoList("")
+ if cmd == 'get-updatedetail' or cmd == 'all':
+ print "Testing GetUpdateDetail(PKG_ID)"
+ iface.GetUpdateDetail(PKG_ID)
+diff --git a/backends/yum2/helpers/yumDBUSBackend.py b/backends/yum2/helpers/yumDBUSBackend.py
+index 9cfed94..29f5b03 100755
+--- a/backends/yum2/helpers/yumDBUSBackend.py
++++ b/backends/yum2/helpers/yumDBUSBackend.py
+@@ -434,7 +434,7 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ try:
+ pkgGroupDict = self._buildGroupDict()
+ fltlist = filters.split(';')
+- found = {}
++ installed_nevra = [] # yum returns packages as available even when installed
+
+ if not FILTER_NOT_INSTALLED in fltlist:
+ # Check installed for group
+@@ -450,21 +450,31 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ group = groupMap[cg] # use the pk group name, instead of yum 'category/group'
+ if group == key:
+ if self._do_extra_filtering(pkg, fltlist):
+- self._show_package(pkg, INFO_INSTALLED)
++ package_list.append((pkg,INFO_INSTALLED))
++ installed_nevra.append(self._get_nevra(pkg))
++
+ if not FILTER_INSTALLED in fltlist:
+ # Check available for group
+ for pkg in self.yumbase.pkgSack:
+ if self._cancel_check("Search cancelled."):
+ # _cancel_check() sets the error message, unlocks yum, and calls Finished()
+ return
+- group = GROUP_OTHER
+- if pkgGroupDict.has_key(pkg.name):
+- cg = pkgGroupDict[pkg.name]
+- if groupMap.has_key(cg):
+- group = groupMap[cg]
+- if group == key:
+- if self._do_extra_filtering(pkg, fltlist):
+- self._show_package(pkg, INFO_AVAILABLE)
++
++ nevra = self._get_nevra(pkg)
++ if nevra not in installed_nevra:
++ group = GROUP_OTHER
++ if pkgGroupDict.has_key(pkg.name):
++ cg = pkgGroupDict[pkg.name]
++ if groupMap.has_key(cg):
++ group = groupMap[cg]
++ if group == key:
++ if self._do_extra_filtering(pkg, fltlist):
++ package_list.append((pkg,INFO_AVAILABLE))
++
++ except yum.Errors.GroupsError,e:
++ self._unlock_yum()
++ self.ErrorCode(ERROR_GROUP_NOT_FOUND, str(e))
++ self.Finished(EXIT_FAILED)
+ except yum.Errors.RepoError,e:
+ self.Message(MESSAGE_NOTICE, "The package cache is invalid and is being rebuilt.")
+ self._refresh_yum_cache()
+@@ -473,6 +483,14 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+
+ return
+
++ # basename filter if specified
++ if FILTER_BASENAME in fltlist:
++ for (pkg,status) in self._basename_filter(package_list):
++ self._show_package(pkg,status)
++ else:
++ for (pkg,status) in package_list:
++ self._show_package(pkg,status)
++
+ self._unlock_yum()
+ self.Finished(EXIT_SUCCESS)
+
+@@ -724,6 +742,11 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ #we might have a rounding error
+ self.PercentageChanged(100)
+
++ except yum.Errors.RepoError,e:
++ self._unlock_yum()
++ self.ErrorCode(ERROR_REPO_CONFIGURATION_ERROR,str(e))
++ self.Finished(EXIT_FAILED)
++ self.Exit()
+ except yum.Errors.YumBaseError, e:
+ self._unlock_yum()
+ # This should be a better-defined error, but I'm not sure
+@@ -837,6 +860,7 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ Needed to be implemented in a sub class
+ '''
+ if inst_file.endswith('.src.rpm'):
++ self._unlock_yum()
+ self.ErrorCode(ERROR_CANNOT_INSTALL_SOURCE_PACKAGE,'Backend will not install a src rpm file')
+ self.Finished(EXIT_FAILED)
+ return
+@@ -1385,30 +1409,30 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ res = self.yumbase.searchGenerator(searchlist, [key])
+ fltlist = filters.split(';')
+
+- available = []
+- count = 1
++ seen_nevra = [] # yum returns packages as available even when installed
++ pkg_list = [] # only do the second iteration on not installed pkgs
++ package_list = [] #we can't do emitting as found if we are post-processing
++
+ for (pkg,values) in res:
+ if self._cancel_check("Search cancelled."):
+ return False
+ # are we installed?
+ if pkg.repo.id == 'installed':
+- if FILTER_NOT_INSTALLED not in fltlist:
+- if self._do_extra_filtering(pkg,fltlist):
+- count+=1
+- if count > 100:
+- break
+- self._show_package(pkg, INFO_INSTALLED)
++ if self._do_extra_filtering(pkg,fltlist):
++ package_list.append((pkg,INFO_INSTALLED))
++ seen_nevra.append(self._get_nevra(pkg))
+ else:
+- available.append(pkg)
++ pkg_list.append(pkg)
+
+- # Now show available packages.
+- if FILTER_INSTALLED not in fltlist:
+- for pkg in available:
+- if self._cancel_check("Search cancelled."):
+- return False
+- if self._do_extra_filtering(pkg,fltlist):
+- self._show_package(pkg, INFO_AVAILABLE)
++ for pkg in pkg_list:
++ if self._cancel_check("Search cancelled."):
++ return False
+
++ nevra = self._get_nevra(pkg)
++ if nevra not in seen_nevra:
++ if self._do_extra_filtering(pkg,fltlist):
++ package_list.append((pkg,INFO_AVAILABLE))
++ seen_nevra.append(self._get_nevra(pkg))
+ except yum.Errors.RepoError,e:
+ self.Message(MESSAGE_NOTICE, "The package cache is invalid and is being rebuilt.")
+ self._refresh_yum_cache()
+@@ -1417,13 +1441,22 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+
+ return False
+
++ # basename filter if specified
++ if FILTER_BASENAME in fltlist:
++ for (pkg,status) in self._basename_filter(package_list):
++ self._show_package(pkg,status)
++ else:
++ for (pkg,status) in package_list:
++ self._show_package(pkg,status)
++
+ return True
+
+ def _do_extra_filtering(self,pkg,filterList):
+ ''' do extra filtering (gui,devel etc) '''
+ for filter in filterList:
+ if filter in (FILTER_INSTALLED, FILTER_NOT_INSTALLED):
+- continue
++ if not self._do_installed_filtering(filter,pkg):
++ return False
+ elif filter in (FILTER_GUI, FILTER_NOT_GUI):
+ if not self._do_gui_filtering(filter, pkg):
+ return False
+@@ -1433,11 +1466,17 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ elif filter in (FILTER_FREE, FILTER_NOT_FREE):
+ if not self._do_free_filtering(filter, pkg):
+ return False
+- elif filter in (FILTER_BASENAME, FILTER_NOT_BASENAME):
+- if not self._do_basename_filtering(filter, pkg):
+- return False
+ return True
+
++ def _do_installed_filtering(self,flt,pkg):
++ isInstalled = False
++ if flt == FILTER_INSTALLED:
++ wantInstalled = True
++ else:
++ wantInstalled = False
++ isInstalled = pkg.repo.id == 'installed'
++ return isInstalled == wantInstalled
++
+ def _do_gui_filtering(self,flt,pkg):
+ isGUI = False
+ if flt == FILTER_GUI:
+@@ -1477,32 +1516,7 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+
+ return isFree == wantFree
+
+- def _do_basename_filtering(self,flt,pkg):
+- if flt == FILTER_BASENAME:
+- wantBase = True
+- else:
+- wantBase = False
+-
+- isBase = self._check_basename(pkg)
+-
+- return isBase == wantBase
+
+- def _check_basename(self, pkg):
+- '''
+- If a package does not have a source rpm (If that ever
+- happens), or it does have a source RPM, and the package's name
+- is the same as the source RPM's name, then we assume it is the
+- 'base' package.
+- '''
+- basename = pkg.name
+-
+- if pkg.sourcerpm:
+- basename = rpmUtils.miscutils.splitFilename(pkg.sourcerpm)[0]
+-
+- if basename == pkg.name:
+- return True
+-
+- return False
+
+ def _is_development_repo(self, repo):
+ if repo.endswith('-debuginfo'):
+@@ -1576,28 +1590,41 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ '''
+ find a package based on a package id (name;version;arch;repoid)
+ '''
+- # Split up the id
+- (n,idver,a,d) = self.get_package_from_id(id)
+- # get e,v,r from package id version
+- e,v,r = self._getEVR(idver)
++ # is this an real id or just an name
++ if len(id.split(';')) > 1:
++ # Split up the id
++ (n,idver,a,d) = self.get_package_from_id(id)
++ # get e,v,r from package id version
++ e,v,r = self._getEVR(idver)
++ else:
++ n = id
++ e = v = r = a = None
+ # search the rpmdb for the nevra
+ pkgs = self.yumbase.rpmdb.searchNevra(name=n,epoch=e,ver=v,rel=r,arch=a)
+- # if the package is found, then return it
++ # if the package is found, then return it (do not have to match the repo_id)
+ if len(pkgs) != 0:
+ return pkgs[0],True
+ # search the pkgSack for the nevra
+- pkgs = self.yumbase.pkgSack.searchNevra(name=n,epoch=e,ver=v,rel=r,arch=a)
+- # if the package is found, then return it
+- if len(pkgs) != 0:
+- return pkgs[0],False
+- else:
++ try:
++ pkgs = self.yumbase.pkgSack.searchNevra(name=n,epoch=e,ver=v,rel=r,arch=a)
++ except yum.Errors.RepoError,e:
++ self.error(ERROR_REPO_NOT_AVAILABLE,str(e))
++ # nothing found
++ if len(pkgs) == 0:
+ return None,False
++ # one NEVRA in a single repo
++ if len(pkgs) == 1:
++ return pkgs[0],False
++ # we might have the same NEVRA in multiple repos, match by repo name
++ for pkg in pkgs:
++ if d == pkg.repoid:
++ return pkg,False
++ # repo id did not match
++ return None,False
+
+ def _is_inst(self,pkg):
+ return self.yumbase.rpmdb.installed(po=pkg)
+
+-
+-
+ def _installable(self, pkg, ematch=False):
+
+ """check if the package is reasonably installable, true/false"""
+@@ -1764,6 +1791,57 @@ class PackageKitYumBackend(PackageKitBaseBackend):
+ return INFO_ENHANCEMENT
+ else:
+ return INFO_UNKNOWN
++ def _is_main_package(self,repo):
++ if repo.endswith('-debuginfo'):
++ return False
++ if repo.endswith('-devel'):
++ return False
++ if repo.endswith('-libs'):
++ return False
++ return True
++
++ def _basename_filter(self,package_list):
++ '''
++ Filter the list so that the number of packages are reduced.
++ This is done by only displaying gtk2 rather than gtk2-devel, gtk2-debuginfo, etc.
++ This imlementation is done by comparing the SRPM name, and if not falling back
++ to the first entry.
++ We have to fall back else we don't emit packages where the SRPM does not produce a
++ RPM with the same name, for instance, mono produces mono-core, mono-data and mono-winforms.
++ @package_list: a (pkg,status) list of packages
++ A new list is returned that has been filtered
++ '''
++ base_list = []
++ output_list = []
++ base_list_already_got = []
++
++ #find out the srpm name and add to a new array of compound data
++ for (pkg,status) in package_list:
++ if pkg.sourcerpm:
++ base = rpmUtils.miscutils.splitFilename(pkg.sourcerpm)[0]
++ base_list.append ((pkg,status,base,pkg.version));
++ else:
++ base_list.append ((pkg,status,'nosrpm',pkg.version));
++
++ #find all the packages that match thier basename name (done seporately so we get the "best" match)
++ for (pkg,status,base,version) in base_list:
++ if base == pkg.name and (base,version) not in base_list_already_got:
++ output_list.append((pkg,status))
++ base_list_already_got.append ((base,version))
++
++ #for all the ones not yet got, can we match against a non devel match?
++ for (pkg,status,base,version) in base_list:
++ if (base,version) not in base_list_already_got:
++ if self._is_main_package(pkg.name):
++ output_list.append((pkg,status))
++ base_list_already_got.append ((base,version))
++
++ #add the remainder of the packages, which should just be the single debuginfo's
++ for (pkg,status,base,version) in base_list:
++ if (base,version) not in base_list_already_got:
++ output_list.append((pkg,status))
++ base_list_already_got.append ((base,version))
++ return output_list
+
+ def _get_obsoleted(self,name):
+ obsoletes = self.yumbase.up.getObsoletesTuples( newest=1 )
+diff --git a/backends/zypp/pk-backend-zypp.cpp b/backends/zypp/pk-backend-zypp.cpp
+index 746da82..15c4b4f 100644
+--- a/backends/zypp/pk-backend-zypp.cpp
++++ b/backends/zypp/pk-backend-zypp.cpp
+@@ -1170,7 +1170,7 @@ backend_find_packages_thread (PkBackend *backend)
+ mode = pk_backend_get_uint (backend, "mode");
+
+ pk_backend_set_status (backend, PK_STATUS_ENUM_QUERY);
+- pk_backend_no_percentage_updates (backend);
++ pk_backend_set_percentage (backend, PK_BACKEND_PERCENTAGE_INVALID);
+
+ std::vector<zypp::sat::Solvable> *v = new std::vector<zypp::sat::Solvable>;
+ std::vector<zypp::sat::Solvable> *v2 = new std::vector<zypp::sat::Solvable>;
+diff --git a/client/pk-console.c b/client/pk-console.c
+index 8f69068..5a05a8e 100644
+--- a/client/pk-console.c
++++ b/client/pk-console.c
+@@ -50,7 +50,7 @@ static gboolean awaiting_space = FALSE;
+ static gboolean trusted = TRUE;
+ static guint timer_id = 0;
+ static guint percentage_last = 0;
+-static gchar *filename = NULL;
++static gchar **files_cache = NULL;
+ static PkControl *control = NULL;
+ static PkClient *client = NULL;
+ static PkClient *client_task = NULL;
+@@ -494,6 +494,12 @@ pk_console_perhaps_resolve (PkClient *client, PkFilterEnum filter, const gchar *
+ return g_strdup (package);
+ }
+
++ ret = pk_client_reset (client_task, error);
++ if (ret == FALSE) {
++ pk_warning ("failed to reset client task");
++ return NULL;
++ }
++
+ /* we need to resolve it */
+ ret = pk_client_resolve (client_task, filter, package, error);
+ if (ret == FALSE) {
+@@ -549,20 +555,93 @@ pk_console_perhaps_resolve (PkClient *client, PkFilterEnum filter, const gchar *
+ }
+
+ /**
+- * pk_console_install_package:
++ * pk_console_install_stuff:
+ **/
+ static gboolean
+-pk_console_install_package (PkClient *client, const gchar *package, GError **error)
++pk_console_install_stuff (PkClient *client, gchar **packages, GError **error)
+ {
+- gboolean ret;
+- gchar *package_id;
+- package_id = pk_console_perhaps_resolve (client, PK_FILTER_ENUM_NOT_INSTALLED, package, error);
+- if (package_id == NULL) {
+- g_print ("%s\n", _("Could not find a package with that name to install, or package already installed"));
+- return FALSE;
++ gboolean ret = TRUE;
++ gboolean is_local;
++ gchar *package_id = NULL;
++ gchar **package_ids = NULL;
++ gchar **files = NULL;
++ guint i;
++ guint length;
++ GPtrArray *array_packages;
++ GPtrArray *array_files;
++
++ array_packages = g_ptr_array_new ();
++ array_files = g_ptr_array_new ();
++ length = g_strv_length (packages);
++ for (i=2; i<length; i++) {
++ is_local = g_file_test (packages[i], G_FILE_TEST_EXISTS);
++ if (is_local) {
++ g_ptr_array_add (array_files, g_strdup (packages[i]));
++ } else {
++ package_id = pk_console_perhaps_resolve (client, PK_FILTER_ENUM_NOT_INSTALLED, packages[i], error);
++ if (package_id == NULL) {
++ g_print ("%s\n", _("Could not find a package with that name to install, or package already installed"));
++ ret = FALSE;
++ break;
++ }
++ g_ptr_array_add (array_packages, package_id);
++ }
+ }
+- ret = pk_client_install_package (client, package_id, error);
+- g_free (package_id);
++
++ /* one of the resolves failed */
++ if (!ret) {
++ pk_warning ("resolve failed");
++ goto out;
++ }
++
++
++ /* any to process? */
++ if (array_packages->len > 0) {
++ /* convert to strv */
++ package_ids = pk_ptr_array_to_argv (array_packages);
++
++ /* reset */
++ ret = pk_client_reset (client, error);
++ if (!ret) {
++ pk_warning ("failed to reset");
++ goto out;
++ }
++
++ ret = pk_client_install_package (client, package_id, error);
++ if (!ret) {
++ pk_warning ("failed to install packages");
++ goto out;
++ }
++ }
++
++ /* any to process? */
++ if (array_files->len > 0) {
++ /* convert to strv */
++ files = pk_ptr_array_to_argv (array_files);
++
++ /* save for untrusted callback */
++ g_strfreev (files_cache);
++ files_cache = g_strdupv (files);
++
++ /* reset */
++ ret = pk_client_reset (client, error);
++ if (!ret) {
++ pk_warning ("failed to reset");
++ goto out;
++ }
++
++ ret = pk_client_install_files (client, trusted, files, error);
++ if (!ret) {
++ pk_warning ("failed to install files");
++ goto out;
++ }
++ }
++
++out:
++ g_strfreev (package_ids);
++ g_strfreev (files);
++ g_ptr_array_free (array_files, TRUE);
++ g_ptr_array_free (array_packages, TRUE);
+ return ret;
+ }
+
+@@ -570,16 +649,16 @@ pk_console_install_package (PkClient *client, const gchar *package, GError **err
+ * pk_console_remove_only:
+ **/
+ static gboolean
+-pk_console_remove_only (PkClient *client, const gchar *package_id, gboolean force, gboolean autoremove, GError **error)
++pk_console_remove_only (PkClient *client, gchar **package_ids, gboolean force, GError **error)
+ {
+ gboolean ret;
+
+- pk_debug ("remove %s", package_id);
++ pk_debug ("remove+ %s", package_ids[0]);
+ ret = pk_client_reset (client, error);
+ if (!ret) {
+ return ret;
+ }
+- return pk_client_remove_package (client, package_id, force, autoremove, error);
++ return pk_client_remove_packages (client, package_ids, force, FALSE, error);
+ }
+
+ /**
+@@ -625,64 +704,99 @@ pk_console_get_prompt (const gchar *question, gboolean defaultyes)
+ }
+
+ /**
+- * pk_console_remove_package:
++ * pk_console_remove_packages:
+ **/
+ static gboolean
+-pk_console_remove_package (PkClient *client, const gchar *package, GError **error)
++pk_console_remove_packages (PkClient *client, gchar **packages, GError **error)
+ {
+ gchar *package_id;
+- gboolean ret;
+- guint length;
++ gboolean ret = TRUE;
+ PkPackageItem *item;
+ PkPackageId *ident;
+- guint i;
++ guint i, j;
++ guint size;
++ guint length;
+ gboolean remove;
++ GPtrArray *array;
++ gchar **package_ids = NULL;
++ PkPackageList *list;
++
++ array = g_ptr_array_new ();
++ list = pk_package_list_new ();
++ length = g_strv_length (packages);
++ for (i=2; i<length; i++) {
++ package_id = pk_console_perhaps_resolve (client, PK_FILTER_ENUM_INSTALLED, packages[i], error);
++ if (package_id == NULL) {
++ g_print ("%s:%s\n", _("Could not find a package to remove"), packages[i]);
++ ret = FALSE;
++ break;
++ }
++ g_ptr_array_add (array, g_strdup (package_id));
++ pk_debug ("resolved to %s", package_id);
++ g_free (package_id);
++ }
+
+- package_id = pk_console_perhaps_resolve (client, PK_FILTER_ENUM_INSTALLED, package, error);
+- if (package_id == NULL) {
+- g_print ("%s\n", _("Could not find a package with that name to remove"));
+- return FALSE;
++ /* one of the resolves failed */
++ if (!ret) {
++ goto out;
+ }
+
++ /* convert to strv */
++ package_ids = pk_ptr_array_to_argv (array);
++
+ /* are we dumb and can't check for requires? */
+ if (!pk_enums_contain (roles, PK_ROLE_ENUM_GET_REQUIRES)) {
+ /* no, just try to remove it without deps */
+- ret = pk_console_remove_only (client, package_id, FALSE, FALSE, error);
+- g_free (package_id);
+- return ret;
++ ret = pk_console_remove_only (client, package_ids, FALSE, error);
++ goto out;
+ }
+
+- /* see if any packages require this one */
+- ret = pk_client_reset (client_task, error);
+- if (!ret) {
+- pk_warning ("failed to reset");
+- return FALSE;
++ /* get the requires packages for each package_id */
++ length = g_strv_length (package_ids);
++ for (i=0; i<length; i++) {
++ ret = pk_client_reset (client_task, error);
++ if (!ret) {
++ pk_warning ("failed to reset");
++ break;
++ }
++
++ pk_debug ("Getting installed requires for %s", package_ids[i]);
++ /* see if any packages require this one */
++ ret = pk_client_get_requires (client_task, PK_FILTER_ENUM_INSTALLED, package_ids[i], TRUE, error);
++ if (!ret) {
++ pk_warning ("failed to get requires");
++ break;
++ }
++
++ /* see how many packages there are */
++ size = pk_client_package_buffer_get_size (client_task);
++ for (j=0; j<size; j++) {
++ item = pk_client_package_buffer_get_item (client_task, j);
++ pk_package_list_add_item (list, item);
++ }
+ }
+
+- pk_debug ("Getting installed requires for %s", package_id);
+- ret = pk_client_get_requires (client_task, PK_FILTER_ENUM_INSTALLED, package_id, TRUE, error);
++ /* one of the get-requires failed */
+ if (!ret) {
+- return FALSE;
++ goto out;
+ }
+
+- /* see how many packages there are */
+- length = pk_client_package_buffer_get_size (client_task);
+-
+ /* if there are no required packages, just do the remove */
++ length = pk_package_list_get_size (list);
+ if (length == 0) {
+ pk_debug ("no requires");
+- ret = pk_console_remove_only (client, package_id, FALSE, FALSE, error);
+- g_free (package_id);
+- return ret;
++ ret = pk_console_remove_only (client, package_ids, FALSE, error);
++ goto out;
+ }
+
++
+ /* present this to the user */
+ if (awaiting_space) {
+ g_print ("\n");
+ }
+ g_print ("%s:\n", _("The following packages have to be removed"));
+ for (i=0; i<length; i++) {
+- item = pk_client_package_buffer_get_item (client_task, i);
++ item = pk_package_list_get_item (list, i);
+ ident = pk_package_id_new_from_string (item->package_id);
+ g_print ("%i\t%s-%s\n", i, ident->name, ident->version);
+ pk_package_id_free (ident);
+@@ -694,14 +808,17 @@ pk_console_remove_package (PkClient *client, const gchar *package, GError **erro
+ /* we chickened out */
+ if (remove == FALSE) {
+ g_print ("%s\n", _("Cancelled!"));
+- g_free (package_id);
+- return FALSE;
++ ret = FALSE;
++ goto out;
+ }
+
+ /* remove all the stuff */
+- ret = pk_console_remove_only (client, package_id, TRUE, FALSE, error);
+- g_free (package_id);
++ ret = pk_console_remove_only (client, package_ids, TRUE, error);
+
++out:
++ g_object_unref (list);
++ g_strfreev (package_ids);
++ g_ptr_array_free (array, TRUE);
+ return ret;
+ }
+
+@@ -840,7 +957,7 @@ pk_console_error_code_cb (PkClient *client, PkErrorCodeEnum error_code, const gc
+ error_code == PK_ERROR_ENUM_MISSING_GPG_SIGNATURE && trusted) {
+ pk_debug ("need to try again with trusted FALSE");
+ trusted = FALSE;
+- ret = pk_client_install_file (client_install_files, trusted, filename, &error);
++ ret = pk_client_install_files (client_install_files, trusted, files_cache, &error);
+ /* we succeeded, so wait for the requeue */
+ if (!ret) {
+ pk_warning ("failed to install file second time: %s", error->message);
+@@ -1153,7 +1270,6 @@ main (int argc, char *argv[])
+ const gchar *value = NULL;
+ const gchar *details = NULL;
+ const gchar *parameter = NULL;
+- PkRoleEnum roles;
+ PkGroupEnum groups;
+ gchar *text;
+ ret = FALSE;
+@@ -1324,15 +1440,7 @@ main (int argc, char *argv[])
+ g_print (_("You need to specify a package or file to install"));
+ goto out;
+ }
+- /* is it a local file? */
+- ret = g_file_test (value, G_FILE_TEST_EXISTS);
+- if (ret) {
+- ret = pk_client_install_file (client, trusted, value, &error);
+- /* we need this for the untrusted try */
+- filename = g_strdup (value);
+- } else {
+- ret = pk_console_install_package (client, value, &error);
+- }
++ ret = pk_console_install_stuff (client, argv, &error);
+
+ } else if (strcmp (mode, "install-sig") == 0) {
+ if (value == NULL || details == NULL || parameter == NULL) {
+@@ -1346,7 +1454,7 @@ main (int argc, char *argv[])
+ g_print (_("You need to specify a package to remove"));
+ goto out;
+ }
+- ret = pk_console_remove_package (client, value, &error);
++ ret = pk_console_remove_packages (client, argv, &error);
+
+ } else if (strcmp (mode, "accept-eula") == 0) {
+ if (value == NULL) {
+@@ -1465,9 +1573,9 @@ main (int argc, char *argv[])
+ ret = pk_client_get_packages (client, filters, &error);
+
+ } else if (strcmp (mode, "get-actions") == 0) {
+- roles = pk_control_get_actions (control);
+ text = pk_role_enums_to_text (roles);
+- g_print ("roles=%s\n", text);
++ g_strdelimit (text, ";", '\n');
++ g_print ("%s\n", text);
+ g_free (text);
+ maybe_sync = FALSE;
+ /* these can never fail */
+@@ -1476,7 +1584,8 @@ main (int argc, char *argv[])
+ } else if (strcmp (mode, "get-filters") == 0) {
+ filters = pk_control_get_filters (control);
+ text = pk_filter_enums_to_text (filters);
+- g_print ("filters=%s\n", text);
++ g_strdelimit (text, ";", '\n');
++ g_print ("%s\n", text);
+ g_free (text);
+ maybe_sync = FALSE;
+ /* these can never fail */
+@@ -1485,7 +1594,8 @@ main (int argc, char *argv[])
+ } else if (strcmp (mode, "get-groups") == 0) {
+ groups = pk_control_get_groups (control);
+ text = pk_group_enums_to_text (groups);
+- g_print ("groups=%s\n", text);
++ g_strdelimit (text, ";", '\n');
++ g_print ("%s\n", text);
+ g_free (text);
+ maybe_sync = FALSE;
+ /* these can never fail */
+@@ -1525,7 +1635,7 @@ out:
+ g_free (options_help);
+ g_free (filter);
+ g_free (summary);
+- g_free (filename);
++ g_strfreev (files_cache);
+ g_object_unref (control);
+ g_object_unref (client);
+ g_object_unref (client_task);
+diff --git a/configure.ac b/configure.ac
+index 9d734e1..f614d2b 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -535,7 +535,7 @@ if test x$enable_box = xyes; then
+ fi
+
+ if test x$enable_opkg = xyes; then
+- PKG_CHECK_MODULES(OPKG, libopkg = 0.1.3)
++ PKG_CHECK_MODULES(OPKG, libopkg = 0.1.4)
+ AC_SUBST(OPKG_CFLAGS)
+ AC_SUBST(OPKG_LIBS)
+ fi
+diff --git a/contrib/yum-packagekit/refresh-packagekit.py b/contrib/yum-packagekit/refresh-packagekit.py
+index 9c0bdf4..b440539 100644
+--- a/contrib/yum-packagekit/refresh-packagekit.py
++++ b/contrib/yum-packagekit/refresh-packagekit.py
+@@ -35,7 +35,7 @@ def posttrans_hook(conduit):
+ '/org/freedesktop/PackageKit')
+ packagekit_iface = dbus.Interface(packagekit_proxy, 'org.freedesktop.PackageKit')
+ packagekit_iface.StateHasChanged('posttrans')
+- except dbus.DBusException, e:
++ except Exception, e:
+ conduit.info(2, "Unable to send message to PackageKit")
+ conduit.info(6, "%s" %(e,))
+
+diff --git a/data/tests/Makefile.am b/data/tests/Makefile.am
+index d15dd6c..6935e66 100644
+--- a/data/tests/Makefile.am
++++ b/data/tests/Makefile.am
+@@ -5,6 +5,7 @@ NULL =
+
+ TEST_FILES = \
+ pk-spawn-test.sh \
++ pk-spawn-proxy.sh \
+ pk-spawn-test-sigquit.sh \
+ pk-spawn-test-profiling.sh \
+ $(NULL)
+diff --git a/data/tests/pk-spawn-proxy.sh b/data/tests/pk-spawn-proxy.sh
+new file mode 100755
+index 0000000..57774f1
+--- /dev/null
++++ b/data/tests/pk-spawn-proxy.sh
+@@ -0,0 +1,20 @@
++#!/bin/bash
++# Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
++# Licensed under the GNU General Public License Version 2
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 2 of the License, or
++# (at your option) any later version.
++
++if [ -z "${http_proxy}" ]; then
++ echo "no http proxy"
++ exit 1
++fi
++
++if [ -z "${ftp_proxy}" ]; then
++ echo "no ftp proxy"
++ exit 1
++fi
++
++echo -e "percentage\t100"
++
+diff --git a/docs/html/index.html b/docs/html/index.html
+index 7270eb7..d60f825 100644
+--- a/docs/html/index.html
++++ b/docs/html/index.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ <td width="95%" valign="middle"><p class="title">PackageKit Main Page</p></td>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ </tr>
+ </table>
+
+@@ -20,10 +20,10 @@
+ <table align="center" cellpadding="5px" border="0">
+
+ <tr>
+- <td align="center"><a href="pk-intro.html"><img src="img/large-accessories-text-editor.png" width="128" alt="[img]"/></a></td>
+- <td align="center"><a href="pk-using.html"><img src="img/large-preferences-system.png" width="128" alt="[img]"/></a></td>
+- <td align="center"><a href="pk-download.html"><img src="img/large-dialog-information.png" width="128" alt="[img]"/></a></td>
+- <td align="center"><a href="pk-screenshots.html"><img src="img/large-emblem-photos.png" width="128" alt="[img]"/></a></td>
++ <td align="center"><a href="pk-intro.html"><img src="img/large-accessories-text-editor.png" width="128" alt=""/></a></td>
++ <td align="center"><a href="pk-using.html"><img src="img/large-preferences-system.png" width="128" alt=""/></a></td>
++ <td align="center"><a href="pk-download.html"><img src="img/large-dialog-information.png" width="128" alt=""/></a></td>
++ <td align="center"><a href="pk-screenshots.html"><img src="img/large-emblem-photos.png" width="128" alt=""/></a></td>
+ </tr>
+ <tr>
+ <td><p class="indextitle"><a href="pk-intro.html" class="indextitle">What is<br/>PackageKit?</a></p></td>
+@@ -32,10 +32,10 @@
+ <td><p class="indextitle"><a href="pk-screenshots.html" class="indextitle">Screenshots</a></p></td>
+ </tr>
+ <tr>
+- <td align="center"><a href="pk-authors.html"><img src="img/large-authors.png" width="128" alt="[img]"/></a></td>
+- <td align="center"><a href="pk-bugs.html"><img src="img/large-applications-development.png" width="128" alt="[img]"/></a></td>
+- <td align="center"><a href="pk-help.html"><img src="img/large-system-users.png" width="128" alt="[img]"/></a></td>
+- <td align="center"><a href="pk-faq.html"><img src="img/large-help-browser.png" width="128" alt="[img]"/></a></td>
++ <td align="center"><a href="pk-authors.html"><img src="img/large-authors.png" width="128" alt=""/></a></td>
++ <td align="center"><a href="pk-bugs.html"><img src="img/large-applications-development.png" width="128" alt=""/></a></td>
++ <td align="center"><a href="pk-help.html"><img src="img/large-system-users.png" width="128" alt=""/></a></td>
++ <td align="center"><a href="pk-faq.html"><img src="img/large-help-browser.png" width="128" alt=""/></a></td>
+ </tr>
+ <tr>
+ <td><p class="indextitle"><a href="pk-authors.html" class="indextitle">Who develops<br/>PackageKit?</a></p></td>
+diff --git a/docs/html/pk-authors.html b/docs/html/pk-authors.html
+index 08289ad..607a7a4 100644
+--- a/docs/html/pk-authors.html
++++ b/docs/html/pk-authors.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ <td width="95%" valign="middle"><p class="title">Who develops PackageKit?</p></td>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ </tr>
+ </table>
+
+@@ -22,7 +22,7 @@
+ <table cellpadding="10">
+ <tr>
+ <td>
+- <img src="img/author-hughsie.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-hughsie.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Richard Hughes</h2>
+@@ -48,7 +48,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-kenvandine.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-kenvandine.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Ken VanDine</h2>
+@@ -66,7 +66,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-btimothy.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-btimothy.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Boyd Timothy</h2>
+@@ -84,7 +84,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-rnorwood.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-rnorwood.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Robin Norwood</h2>
+@@ -101,7 +101,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-tomparker.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-tomparker.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Tom Parker</h2>
+@@ -118,7 +118,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-timlau.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-timlau.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Tim Lauridsen</h2>
+@@ -139,7 +139,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-lmacken.png" alt="[img]"/>
++ <img src="img/author-lmacken.png" alt=""/>
+ </td>
+ <td>
+ <h2>Luke Macken</h2>
+@@ -154,7 +154,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-grzegorzdabrowski.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-grzegorzdabrowski.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Grzegorz DÄ…browski</h2>
+@@ -173,7 +173,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-caglar.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-caglar.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>S.Çağlar Onur</h2>
+@@ -195,7 +195,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-elliot.png" alt="[img]"/>
++ <img src="img/author-elliot.png" alt=""/>
+ </td>
+ <td>
+ <h2>Elliot Peele</h2>
+@@ -210,7 +210,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-jbowes.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-jbowes.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>James Bowes</h2>
+@@ -227,7 +227,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-unknown.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-unknown.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Thomas Wood</h2>
+@@ -247,7 +247,7 @@
+
+ <tr>
+ <td>
+- <img src="img/author-unknown.png" alt="[img]"/><!-- image should be 120px wide -->
++ <img src="img/author-unknown.png" alt=""/><!-- image should be 120px wide -->
+ </td>
+ <td>
+ <h2>Scott Reeves</h2>
+diff --git a/docs/html/pk-bugs.html b/docs/html/pk-bugs.html
+index 89f7c48..2ee12ea 100644
+--- a/docs/html/pk-bugs.html
++++ b/docs/html/pk-bugs.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ <td width="95%" valign="middle"><p class="title">Reporting Bugs</p></td>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ </tr>
+ </table>
+
+diff --git a/docs/html/pk-download.html b/docs/html/pk-download.html
+index 6f796b8..0cdc85c 100644
+--- a/docs/html/pk-download.html
++++ b/docs/html/pk-download.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ <td width="95%" valign="middle"><p class="title">Where can I download it?</p></td>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ </tr>
+ </table>
+
+diff --git a/docs/html/pk-faq.html b/docs/html/pk-faq.html
+index a25e2b7..efa8344 100644
+--- a/docs/html/pk-faq.html
++++ b/docs/html/pk-faq.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><center><img src="img/packagekit.png" alt="[img]"/></center></td>
++ <td><center><img src="img/packagekit.png" alt=""/></center></td>
+ <td width="95%" valign="middle"><p class="title">Frequently Asked Questions</p></td>
+- <td><center><img src="img/packagekit.png" alt="[img]"/></center></td>
++ <td><center><img src="img/packagekit.png" alt=""/></center></td>
+ </tr>
+ </table>
+
+@@ -22,6 +22,7 @@
+ <h2>Table Of Contents</h2>
+ <ul>
+ <li><a href="#how-complete">How complete are the backends?</a></li>
++<li><a href="#run-as-root">When run as root, gpk-application and pkcon do not work!</a></li>
+ <li><a href="#session-system">Why is there a session service and and a system service?</a></li>
+ <li><a href="#session-methods">How do I use PackageKit in my application?</a></li>
+ <li><a href="#rawhide-updates">Why don't I get update details with Fedora Rawhide?</a></li>
+@@ -565,6 +566,19 @@
+ </table>
+
+ <hr>
++<h3><a name="run-as-root">When run as root, <code>gpk-application</code> and <code>pkcon</code> do not work!</a></h3>
++<p>
++GTK+ tools should not be run as the root user, <b>PERIOD</b>.
++Any GTK+ program run as the root user is a massive security hole -- GTK+ just isn't designed with
++this in mind.
++There are <b>numerous</b> attack vectors when running as root, and programs shouldn't do such
++insane and insecure things.
++</p>
++<p>
++Please see <a href="http://www.gtk.org/setuid.html">the GTK+ explanation</a> for more rationale.
++</p>
++
++<hr>
+ <h3><a name="session-system">Why is there a session service <b>and</b> and a system service?</a></h3>
+ <p>
+ PackageKit runs a process <code>packagekitd</code> that is a daemon that runs per-system.
+diff --git a/docs/html/pk-help.html b/docs/html/pk-help.html
+index 5b44d50..5bc7827 100644
+--- a/docs/html/pk-help.html
++++ b/docs/html/pk-help.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ <td width="95%" valign="middle"><p class="title">How can I help?</p></td>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ </tr>
+ </table>
+
+@@ -72,9 +72,9 @@ as for instructions!
+ <b>0.2.2</b> - To be released June 2008
+ </p>
+ <ul>
+-<li>Multiple package install and remove from pkcon <i>(0%)</i></li>
++<li>Network proxy server support <i>(70%)</i></li>
++<li>Multiple package install and remove from pkcon <i>(80%)</i></li>
+ <li>NetworkManager integration so we can detect GPRS (and modem) connections. <i>(10%)</i></li>
+-<li>Filter for source packages. <i>(0%)</i></li>
+ </ul>
+ <p>
+ <b>0.2.3</b> - To be released July 2008
+@@ -84,6 +84,12 @@ as for instructions!
+ <li>Multiple package installs from gpk-application <i>(0%)</i></li>
+ <li>Ignoring packages from the update viewer per-session <i>(10%)</i></li>
+ </ul>
++<p>
++<b>0.3.0</b> - To be released December 2008
++</p>
++<ul>
++<li>More composite types <code>s</code> to <code>as</code> <i>(0%)</i></li>
++</ul>
+
+ <p>Back to the <a href="index.html">main page</a></p>
+
+diff --git a/docs/html/pk-intro.html b/docs/html/pk-intro.html
+index c42be21..64f72fc 100644
+--- a/docs/html/pk-intro.html
++++ b/docs/html/pk-intro.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ <td width="95%" valign="middle"><p class="title">What is PackageKit?</p></td>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ </tr>
+ </table>
+
+diff --git a/docs/html/pk-screenshots.html b/docs/html/pk-screenshots.html
+index 78bd01f..5c7d4fa 100644
+--- a/docs/html/pk-screenshots.html
++++ b/docs/html/pk-screenshots.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><center><img src="img/packagekit.png" alt="[img]"/></center></td>
++ <td><center><img src="img/packagekit.png" alt=""/></center></td>
+ <td width="95%" valign="middle"><p class="title">Screenshots</p></td>
+- <td><center><img src="img/packagekit.png" alt="[img]"/></center></td>
++ <td><center><img src="img/packagekit.png" alt=""/></center></td>
+ </tr>
+ </table>
+
+@@ -26,77 +26,77 @@
+
+ <h1><a name="gnome">GNOME Screenshots</a></h1>
+
+-<center><img src="img/gpk-application-search.png" alt="[img]"/></center>
++<center><img src="img/gpk-application-search.png" alt=""/></center>
+ <p class="caption">Add/Remove Software search</p>
+
+-<center><img src="img/gpk-application-groups.png" alt="[img]"/></center>
++<center><img src="img/gpk-application-groups.png" alt=""/></center>
+ <p class="caption">Add/Remove Software groups</p>
+
+-<center><img src="img/gpk-log.png" alt="[img]"/></center>
++<center><img src="img/gpk-log.png" alt=""/></center>
+ <p class="caption">Transaction viewer</p>
+
+-<center><img src="img/gpk-updates-overview.png" alt="[img]"/></center>
++<center><img src="img/gpk-updates-overview.png" alt=""/></center>
+ <p class="caption">Update viewer overview</p>
+
+-<center><img src="img/gpk-updates.png" alt="[img]"/></center>
++<center><img src="img/gpk-updates.png" alt=""/></center>
+ <p class="caption">Update viewer</p>
+
+-<center><img src="img/gpk-prefs.png" alt="[img]"/></center>
++<center><img src="img/gpk-prefs.png" alt=""/></center>
+ <p class="caption">Auto update preferences</p>
+
+-<center><img src="img/gpk-progress.png" alt="[img]"/></center>
++<center><img src="img/gpk-progress.png" alt=""/></center>
+ <p class="caption">Progress dialog</p>
+
+-<center><img src="img/gpk-added-deps.png" alt="[img]"/></center>
++<center><img src="img/gpk-added-deps.png" alt=""/></center>
+ <p class="caption">Added check warning</p>
+
+-<center><img src="img/gpk-eula.png" alt="[img]"/></center>
++<center><img src="img/gpk-eula.png" alt=""/></center>
+ <p class="caption">EULA dialog</p>
+
+-<center><img src="img/gpk-remove-confirm.png" alt="[img]"/></center>
++<center><img src="img/gpk-remove-confirm.png" alt=""/></center>
+ <p class="caption">Remove check warning</p>
+
+-<center><img src="img/gpk-repo-auth.png" alt="[img]"/></center>
++<center><img src="img/gpk-repo-auth.png" alt=""/></center>
+ <p class="caption">Repository authentication</p>
+
+-<center><img src="img/gpk-repo.png" alt="[img]"/></center>
++<center><img src="img/gpk-repo.png" alt=""/></center>
+ <p class="caption">Repository viewer</p>
+
+-<center><img src="img/gpk-backend-status.png" alt="[img]"/></center>
++<center><img src="img/gpk-backend-status.png" alt=""/></center>
+ <p class="caption">PackageKit backend status</p>
+
+-<center><img src="img/gpk-updates-warning.png" alt="[img]"/></center>
++<center><img src="img/gpk-updates-warning.png" alt=""/></center>
+ <p class="caption">Libnotify updates warning</p>
+
+-<center><img src="img/gpk-waiting.png" alt="[img]"/></center>
++<center><img src="img/gpk-waiting.png" alt=""/></center>
+ <p class="caption">Tasks waiting</p>
+
+-<center><img src="img/gpk-battery.png" alt="[img]"/></center>
++<center><img src="img/gpk-battery.png" alt=""/></center>
+ <p class="caption">Intergration with gnome-power-manager</p>
+
+-<center><img src="img/gpk-inhibit.png" alt="[img]"/></center>
++<center><img src="img/gpk-inhibit.png" alt=""/></center>
+ <p class="caption">Inhibit with gnome-power-manager</p>
+
+-<center><img src="img/gpk-require-restart.png" alt="[img]"/></center>
++<center><img src="img/gpk-require-restart.png" alt=""/></center>
+ <p class="caption">We sometimes need to do a restart</p>
+
+-<center><img src="img/gpk-auto-update.png" alt="[img]"/></center>
++<center><img src="img/gpk-auto-update.png" alt=""/></center>
+ <p class="caption">Auto update install dialog</p>
+
+ <h1><a name="kde">KDE Screenshots</a></h1>
+
+-<center><img src="img/kpk-search.png" alt="[img]"/></center>
++<center><img src="img/kpk-search.png" alt=""/></center>
+ <p class="caption">KPackageKit Searching</p>
+
+-<center><img src="img/kpk-information.png" alt="[img]"/></center>
++<center><img src="img/kpk-information.png" alt=""/></center>
+ <p class="caption">KPackageKit Package Information</p>
+
+-<center><img src="img/pk-opensuse-updater.png" alt="[img]"/></center>
++<center><img src="img/pk-opensuse-updater.png" alt=""/></center>
+ <p class="caption">OpenSuse Updater</p>
+
+ <h1><a name="moko">OpenMoko Screenshots</a></h1>
+
+-<center><img src="img/assassin.png" alt="[img]"/></center>
++<center><img src="img/assassin.png" alt=""/></center>
+ <p class="caption">Assassin</p>
+
+ <p>Back to the <a href="index.html">main page</a></p>
+diff --git a/docs/html/pk-using.html b/docs/html/pk-using.html
+index cc455c7..b2b028e 100644
+--- a/docs/html/pk-using.html
++++ b/docs/html/pk-using.html
+@@ -9,9 +9,9 @@
+
+ <table align="center" class="title">
+ <tr>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ <td width="95%" valign="middle"><p class="title">How do I use PackageKit?</p></td>
+- <td><img src="img/packagekit.png" alt="[img]"/></td>
++ <td><img src="img/packagekit.png" alt=""/></td>
+ </tr>
+ </table>
+
+diff --git a/docs/spec/pk-concepts.xml b/docs/spec/pk-concepts.xml
+index 312c5a4..0b75b10 100644
+--- a/docs/spec/pk-concepts.xml
++++ b/docs/spec/pk-concepts.xml
+@@ -127,6 +127,13 @@
+ This allows the used to choose non-native packages if a multi-lib policy is allowed.
+ </entry>
+ </row>
++ <row>
++ <entry><literal>source</literal> or <literal>~source</literal></entry>
++ <entry>
++ The source filter will only return source packages.
++ These are typically useful when rebuilding other packages.
++ </entry>
++ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+diff --git a/etc/PackageKit.conf.in b/etc/PackageKit.conf.in
+index a6af99b..8f9bd57 100644
+--- a/etc/PackageKit.conf.in
++++ b/etc/PackageKit.conf.in
+@@ -31,3 +31,13 @@ ShutdownTimeout=300
+ # default=@defaultbackend@
+ DefaultBackend=@defaultbackend@
+
++# Proxy settings, uncomment as required
++#
++# NOTE: PackageKit does not use these settings, they are passed to backends.
++# Backends may ignore these values, or they may be updated from the session.
++#
++# They are in the format username:password@server:port
++#
++# ProxyHTTP=username:password@server.lan:8080
++# ProxyFTP=username:password@server.lan:21
++
+diff --git a/libpackagekit/Makefile.am b/libpackagekit/Makefile.am
+index 6b8c6b8..aeafe44 100644
+--- a/libpackagekit/Makefile.am
++++ b/libpackagekit/Makefile.am
+@@ -37,6 +37,7 @@ libpackagekit_include_HEADERS = \
+ pk-connection.h \
+ pk-package-id.h \
+ pk-package-ids.h \
++ pk-package-item.h \
+ pk-package-list.h \
+ pk-enum.h \
+ pk-common.h \
+@@ -59,6 +60,8 @@ libpackagekit_la_SOURCES = \
+ pk-package-id.h \
+ pk-package-ids.c \
+ pk-package-ids.h \
++ pk-package-item.c \
++ pk-package-item.h \
+ pk-package-list.c \
+ pk-package-list.h \
+ pk-enum.h \
+diff --git a/libpackagekit/pk-enum.c b/libpackagekit/pk-enum.c
+index b5b6ac3..5743dcb 100644
+--- a/libpackagekit/pk-enum.c
++++ b/libpackagekit/pk-enum.c
+@@ -193,6 +193,8 @@ static PkEnumMatch enum_filter[] = {
+ {PK_FILTER_ENUM_NOT_NEWEST, "~newest"},
+ {PK_FILTER_ENUM_ARCH, "arch"},
+ {PK_FILTER_ENUM_NOT_ARCH, "~arch"},
++ {PK_FILTER_ENUM_SOURCE, "source"},
++ {PK_FILTER_ENUM_NOT_SOURCE, "~source"},
+ {0, NULL}
+ };
+
+diff --git a/libpackagekit/pk-enum.h b/libpackagekit/pk-enum.h
+index 33e8a91..e616b64 100644
+--- a/libpackagekit/pk-enum.h
++++ b/libpackagekit/pk-enum.h
+@@ -182,7 +182,9 @@ typedef enum {
+ PK_FILTER_ENUM_NOT_NEWEST = 1 << 15,
+ PK_FILTER_ENUM_ARCH = 1 << 16,
+ PK_FILTER_ENUM_NOT_ARCH = 1 << 17,
+- PK_FILTER_ENUM_UNKNOWN = 1 << 18
++ PK_FILTER_ENUM_SOURCE = 1 << 18,
++ PK_FILTER_ENUM_NOT_SOURCE = 1 << 19,
++ PK_FILTER_ENUM_UNKNOWN = 1 << 20
+ } PkFilterEnum;
+
+ /**
+diff --git a/libpackagekit/pk-package-item.c b/libpackagekit/pk-package-item.c
+new file mode 100644
+index 0000000..87905dc
+--- /dev/null
++++ b/libpackagekit/pk-package-item.c
+@@ -0,0 +1,190 @@
++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
++ *
++ * Copyright (C) 2007-2008 Richard Hughes <richard@hughsie.com>
++ *
++ * Licensed under the GNU General Public License Version 2
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++/**
++ * SECTION:pk-package-item
++ * @short_description: A cached Package structure
++ *
++ * These provide a way to query and store a single package.
++ */
++
++#include "config.h"
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <time.h>
++#include <errno.h>
++
++#include <string.h>
++#include <sys/time.h>
++#include <sys/types.h>
++#ifdef HAVE_UNISTD_H
++#include <unistd.h>
++#endif /* HAVE_UNISTD_H */
++
++#include <glib/gi18n.h>
++
++#include "pk-debug.h"
++#include "pk-common.h"
++#include "pk-package-item.h"
++
++/**
++ * pk_package_item_new:
++ **/
++PkPackageItem *
++pk_package_item_new (PkInfoEnum info, const gchar *package_id, const gchar *summary)
++{
++ PkPackageItem *item;
++
++ g_return_val_if_fail (package_id != NULL, FALSE);
++
++ pk_debug ("adding to cache item package %s, %s, %s", pk_info_enum_to_text (info), package_id, summary);
++ item = g_new0 (PkPackageItem, 1);
++ item->info = info;
++ item->package_id = g_strdup (package_id);
++ item->summary = g_strdup (summary);
++ return item;
++}
++
++/**
++ * pk_package_item_free:
++ **/
++gboolean
++pk_package_item_free (PkPackageItem *item)
++{
++ if (item == NULL) {
++ return FALSE;
++ }
++ g_free (item->package_id);
++ g_free (item->summary);
++ g_free (item);
++ return TRUE;
++}
++
++/**
++ * pk_package_item_equal:
++ *
++ * Only compares the package_id's and the info enum
++ **/
++gboolean
++pk_package_item_equal (PkPackageItem *item1, PkPackageItem *item2)
++{
++ if (item1 == NULL || item2 == NULL) {
++ return FALSE;
++ }
++ return (item1->info == item2->info &&
++ pk_strequal (item1->package_id, item2->package_id));
++}
++
++/**
++ * pk_package_item_copy:
++ *
++ * Copy a PkPackageItem
++ **/
++PkPackageItem *
++pk_package_item_copy (PkPackageItem *item)
++{
++ g_return_val_if_fail (item != NULL, NULL);
++ return pk_package_item_new (item->info, item->package_id, item->summary);
++}
++
++/***************************************************************************
++ *** MAKE CHECK TESTS ***
++ ***************************************************************************/
++#ifdef PK_BUILD_TESTS
++#include <libselftest.h>
++
++void
++libst_package_item (LibSelfTest *test)
++{
++ PkPackageItem *item1;
++ PkPackageItem *item2;
++ PkPackageItem *item3;
++ gboolean ret;
++
++ if (libst_start (test, "PkPackageItem", CLASS_AUTO) == FALSE) {
++ return;
++ }
++
++ /************************************************************/
++ libst_title (test, "add entry");
++ item1 = pk_package_item_new (PK_INFO_ENUM_INSTALLED, "gnome;1.23;i386;data", "GNOME!");
++ if (item1 != NULL) {
++ libst_success (test, NULL);
++ } else {
++ libst_failed (test, NULL);
++ }
++
++ /************************************************************/
++ libst_title (test, "add entry");
++ item2 = pk_package_item_new (PK_INFO_ENUM_INSTALLED, "gnome;1.23;i386;data", "GNOME foo!");
++ if (item2 != NULL) {
++ libst_success (test, NULL);
++ } else {
++ libst_failed (test, NULL);
++ }
++
++ /************************************************************/
++ libst_title (test, "copy entry");
++ item3 = pk_package_item_copy (item2);
++ if (item3 != NULL) {
++ libst_success (test, NULL);
++ } else {
++ libst_failed (test, NULL);
++ }
++
++ /************************************************************/
++ libst_title (test, "check equal");
++ ret = pk_package_item_equal (item1, item3);
++ if (ret) {
++ libst_success (test, NULL);
++ } else {
++ libst_failed (test, NULL);
++ }
++
++ pk_package_item_free (item2);
++ pk_package_item_free (item3);
++
++ /************************************************************/
++ libst_title (test, "add entry");
++ item2 = pk_package_item_new (PK_INFO_ENUM_INSTALLED, "gnome-do;1.23;i386;data", "GNOME doo!");
++ if (item2 != NULL) {
++ libst_success (test, NULL);
++ } else {
++ libst_failed (test, NULL);
++ }
++
++ /************************************************************/
++ libst_title (test, "check !equal");
++ ret = pk_package_item_equal (item1, item2);
++ if (!ret) {
++ libst_success (test, NULL);
++ } else {
++ libst_failed (test, NULL);
++ }
++
++ pk_package_item_free (item1);
++ pk_package_item_free (item2);
++
++ libst_end (test);
++}
++#endif
++
+diff --git a/libpackagekit/pk-package-item.h b/libpackagekit/pk-package-item.h
+new file mode 100644
+index 0000000..c41a6ea
+--- /dev/null
++++ b/libpackagekit/pk-package-item.h
+@@ -0,0 +1,48 @@
++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
++ *
++ * Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
++ *
++ * Licensed under the GNU General Public License Version 2
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#ifndef __PK_PACKAGE_ITEM_H
++#define __PK_PACKAGE_ITEM_H
++
++#include <glib-object.h>
++#include <pk-enum.h>
++
++/**
++ * PkPackageItem:
++ *
++ * A cached store for the complete Package object
++ */
++typedef struct {
++ PkInfoEnum info;
++ gchar *package_id;
++ gchar *summary;
++} PkPackageItem;
++
++PkPackageItem *pk_package_item_new (PkInfoEnum info,
++ const gchar *package_id,
++ const gchar *summary);
++gboolean pk_package_item_free (PkPackageItem *item);
++PkPackageItem *pk_package_item_copy (PkPackageItem *item);
++gboolean pk_package_item_equal (PkPackageItem *item1,
++ PkPackageItem *item2);
++
++#endif /* __PK_PACKAGE_ITEM_H */
++
+diff --git a/libpackagekit/pk-package-list.c b/libpackagekit/pk-package-list.c
+index b0a1a71..5d95e1b 100644
+--- a/libpackagekit/pk-package-list.c
++++ b/libpackagekit/pk-package-list.c
+@@ -45,6 +45,7 @@
+ #include "pk-debug.h"
+ #include "pk-common.h"
+ #include "pk-package-id.h"
++#include "pk-package-item.h"
+ #include "pk-package-list.h"
+
+ static void pk_package_list_class_init (PkPackageListClass *klass);
+@@ -77,16 +78,42 @@ pk_package_list_add (PkPackageList *plist, PkInfoEnum info, const gchar *package
+ g_return_val_if_fail (package_id != NULL, FALSE);
+
+ pk_debug ("adding to cache array package %s, %s, %s", pk_info_enum_to_text (info), package_id, summary);
+- item = g_new0 (PkPackageItem, 1);
+- item->info = info;
+- item->package_id = g_strdup (package_id);
+- item->summary = g_strdup (summary);
++ item = pk_package_item_new (info, package_id, summary);
+ g_ptr_array_add (plist->priv->array, item);
+
+ return TRUE;
+ }
+
+ /**
++ * pk_package_list_add_item:
++ *
++ * Makes a deep copy, and adds to the array
++ **/
++gboolean
++pk_package_list_add_item (PkPackageList *plist, PkPackageItem *item)
++{
++ gboolean ret;
++ PkPackageItem *item_new;
++
++ g_return_val_if_fail (PK_IS_PACKAGE_LIST (plist), FALSE);
++ g_return_val_if_fail (item != NULL, FALSE);
++
++ ret = pk_package_list_contains_item (plist, item);
++ if (ret) {
++ pk_debug ("already added item");
++ return FALSE;
++ }
++
++ pk_debug ("adding to cache array package %s, %s, %s",
++ pk_info_enum_to_text (item->info), item->package_id, item->summary);
++
++ item_new = pk_package_item_copy (item);
++ g_ptr_array_add (plist->priv->array, item_new);
++
++ return TRUE;
++}
++
++/**
+ * pk_package_list_get_string:
+ **/
+ gchar *
+@@ -152,9 +179,7 @@ pk_package_list_clear (PkPackageList *plist)
+
+ while (plist->priv->array->len > 0) {
+ item = g_ptr_array_index (plist->priv->array, 0);
+- g_free (item->package_id);
+- g_free (item->summary);
+- g_free (item);
++ pk_package_item_free (item);
+ g_ptr_array_remove_index_fast (plist->priv->array, 0);
+ }
+ return TRUE;
+@@ -186,6 +211,31 @@ pk_package_list_contains (PkPackageList *plist, const gchar *package_id)
+ }
+
+ /**
++ * pk_package_list_contains_item:
++ **/
++gboolean
++pk_package_list_contains_item (PkPackageList *plist, PkPackageItem *item)
++{
++ PkPackageItem *item_temp;
++ guint i;
++ guint length;
++ gboolean ret = FALSE;
++
++ g_return_val_if_fail (PK_IS_PACKAGE_LIST (plist), FALSE);
++ g_return_val_if_fail (item != NULL, FALSE);
++
++ length = plist->priv->array->len;
++ for (i=0; i<length; i++) {
++ item_temp = g_ptr_array_index (plist->priv->array, i);
++ ret = pk_package_item_equal (item_temp, item);
++ if (ret) {
++ break;
++ }
++ }
++ return ret;
++}
++
++/**
+ * pk_package_list_class_init:
+ * @klass: The PkPackageListClass
+ **/
+diff --git a/libpackagekit/pk-package-list.h b/libpackagekit/pk-package-list.h
+index 9178f77..9734af4 100644
+--- a/libpackagekit/pk-package-list.h
++++ b/libpackagekit/pk-package-list.h
+@@ -25,6 +25,8 @@
+ #include <glib-object.h>
+ #include <pk-enum.h>
+
++#include "pk-package-item.h"
++
+ G_BEGIN_DECLS
+
+ #define PK_TYPE_PACKAGE_LIST (pk_package_list_get_type ())
+@@ -49,26 +51,18 @@ struct _PkPackageListClass
+ GObjectClass parent_class;
+ };
+
+-/**
+- * PkPackageItem:
+- *
+- * A cached store for the complete Package object
+- */
+-typedef struct
+-{
+- PkInfoEnum info;
+- gchar *package_id;
+- gchar *summary;
+-} PkPackageItem;
+-
+ GType pk_package_list_get_type (void) G_GNUC_CONST;
+ PkPackageList *pk_package_list_new (void);
+ gboolean pk_package_list_add (PkPackageList *plist,
+ PkInfoEnum info,
+ const gchar *package_id,
+ const gchar *summary);
++gboolean pk_package_list_add_item (PkPackageList *plist,
++ PkPackageItem *item);
+ gboolean pk_package_list_contains (PkPackageList *plist,
+ const gchar *package_id);
++gboolean pk_package_list_contains_item (PkPackageList *plist,
++ PkPackageItem *item);
+ gchar *pk_package_list_get_string (PkPackageList *plist)
+ G_GNUC_WARN_UNUSED_RESULT;
+ guint pk_package_list_get_size (PkPackageList *plist);
+diff --git a/libpackagekit/pk-self-test.c b/libpackagekit/pk-self-test.c
+index 62e225b..bf151fb 100644
+--- a/libpackagekit/pk-self-test.c
++++ b/libpackagekit/pk-self-test.c
+@@ -29,6 +29,7 @@
+ /* prototypes */
+ void libst_package_id (LibSelfTest *test);
+ void libst_package_ids (LibSelfTest *test);
++void libst_package_item (LibSelfTest *test);
+ void libst_package_list (LibSelfTest *test);
+ void libst_enum (LibSelfTest *test);
+ void libst_common (LibSelfTest *test);
+@@ -51,6 +52,7 @@ main (int argc, char **argv)
+ libst_common (&test);
+ libst_package_id (&test);
+ libst_package_ids (&test);
++ libst_package_item (&test);
+ libst_package_list (&test);
+ libst_enum (&test);
+ libst_extra (&test);
+diff --git a/python/packagekit/daemonBackend.py b/python/packagekit/daemonBackend.py
+index 3711f01..5253b39 100644
+--- a/python/packagekit/daemonBackend.py
++++ b/python/packagekit/daemonBackend.py
+@@ -789,6 +789,21 @@ class PackageKitBaseBackend(dbus.service.Object):
+ self.Finished(EXIT_FAILED)
+
+ @dbus.service.method(PACKAGEKIT_DBUS_INTERFACE,
++ in_signature='ss', out_signature='')
++ def SetProxy(self, proxy_http, proxy_ftp):
++ '''
++ Set the proxy
++ '''
++ pklog.info("SetProxy(%s, %s)" % (proxy_http, proxy_ftp))
++ self.doSetProxy(proxy_http, proxy_ftp)
++
++ def doSetProxy(self, proxy_http, proxy_ftp):
++ '''
++ Should be replaced in the corresponding backend sub class
++ '''
++ # do not use Finished() in this method
++
++ @dbus.service.method(PACKAGEKIT_DBUS_INTERFACE,
+ in_signature='s', out_signature='')
+ def InstallPublicKey(self, keyurl):
+ '''
+diff --git a/src/pk-backend-dbus.c b/src/pk-backend-dbus.c
+index b06e584..4c6837a 100644
+--- a/src/pk-backend-dbus.c
++++ b/src/pk-backend-dbus.c
+@@ -123,16 +123,6 @@ pk_backend_dbus_sub_percentage_changed_cb (DBusGProxy *proxy, guint sub_percenta
+ }
+
+ /**
+- * pk_backend_dbus_no_percentage_updates_cb:
+- **/
+-static void
+-pk_backend_dbus_no_percentage_updates_cb (DBusGProxy *proxy, PkBackendDbus *backend_dbus)
+-{
+- pk_debug ("got signal");
+- pk_backend_no_percentage_updates (backend_dbus->priv->backend);
+-}
+-
+-/**
+ * pk_backend_dbus_package_cb:
+ **/
+ static void
+@@ -325,8 +315,6 @@ pk_backend_dbus_remove_callbacks (PkBackendDbus *backend_dbus)
+ G_CALLBACK (pk_backend_dbus_percentage_changed_cb), backend_dbus);
+ dbus_g_proxy_disconnect_signal (proxy, "SubPercentageChanged",
+ G_CALLBACK (pk_backend_dbus_sub_percentage_changed_cb), backend_dbus);
+- dbus_g_proxy_disconnect_signal (proxy, "NoPercentageChanged",
+- G_CALLBACK (pk_backend_dbus_no_percentage_updates_cb), backend_dbus);
+ dbus_g_proxy_disconnect_signal (proxy, "Package",
+ G_CALLBACK (pk_backend_dbus_package_cb), backend_dbus);
+ dbus_g_proxy_disconnect_signal (proxy, "Details",
+@@ -353,6 +341,31 @@ pk_backend_dbus_remove_callbacks (PkBackendDbus *backend_dbus)
+ }
+
+ /**
++ * pk_backend_dbus_set_proxy:
++ **/
++static gboolean
++pk_backend_dbus_set_proxy (PkBackendDbus *backend_dbus, const gchar *proxy_http, const gchar *proxy_ftp)
++{
++ gboolean ret;
++ GError *error = NULL;
++
++ g_return_val_if_fail (PK_IS_BACKEND_DBUS (backend_dbus), FALSE);
++ g_return_val_if_fail (backend_dbus->priv->proxy != NULL, FALSE);
++
++ /* new sync method call */
++ pk_backend_dbus_time_reset (backend_dbus);
++ ret = dbus_g_proxy_call (backend_dbus->priv->proxy, "SetProxy", &error,
++ G_TYPE_STRING, proxy_http,
++ G_TYPE_STRING, proxy_ftp,
++ G_TYPE_INVALID, G_TYPE_INVALID);
++ if (error != NULL) {
++ pk_warning ("%s", error->message);
++ g_error_free (error);
++ }
++ return ret;
++}
++
++/**
+ * pk_backend_dbus_set_name:
+ **/
+ gboolean
+@@ -385,7 +398,6 @@ pk_backend_dbus_set_name (PkBackendDbus *backend_dbus, const gchar *service)
+ G_TYPE_UINT, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (proxy, "SubPercentageChanged",
+ G_TYPE_UINT, G_TYPE_INVALID);
+- dbus_g_proxy_add_signal (proxy, "NoPercentageChanged", G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (proxy, "Package",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (proxy, "Details",
+@@ -424,8 +436,6 @@ pk_backend_dbus_set_name (PkBackendDbus *backend_dbus, const gchar *service)
+ G_CALLBACK (pk_backend_dbus_percentage_changed_cb), backend_dbus, NULL);
+ dbus_g_proxy_connect_signal (proxy, "SubPercentageChanged",
+ G_CALLBACK (pk_backend_dbus_sub_percentage_changed_cb), backend_dbus, NULL);
+- dbus_g_proxy_connect_signal (proxy, "NoPercentageChanged",
+- G_CALLBACK (pk_backend_dbus_no_percentage_updates_cb), backend_dbus, NULL);
+ dbus_g_proxy_connect_signal (proxy, "Package",
+ G_CALLBACK (pk_backend_dbus_package_cb), backend_dbus, NULL);
+ dbus_g_proxy_connect_signal (proxy, "Details",
+@@ -465,6 +475,18 @@ pk_backend_dbus_set_name (PkBackendDbus *backend_dbus, const gchar *service)
+ pk_backend_finished (backend_dbus->priv->backend);
+ g_error_free (error);
+ }
++
++ /* set the proxy */
++ if (ret) {
++ gchar *proxy_http;
++ gchar *proxy_ftp;
++ proxy_http = pk_backend_get_proxy_http (backend_dbus->priv->backend);
++ proxy_ftp = pk_backend_get_proxy_http (backend_dbus->priv->backend);
++ pk_backend_dbus_set_proxy (backend_dbus, proxy_http, proxy_ftp);
++ g_free (proxy_http);
++ g_free (proxy_ftp);
++ }
++
+ if (ret) {
+ pk_backend_dbus_time_check (backend_dbus);
+ }
+diff --git a/src/pk-backend-internal.h b/src/pk-backend-internal.h
+index 2213fed..2bffaff 100644
+--- a/src/pk-backend-internal.h
++++ b/src/pk-backend-internal.h
+@@ -59,6 +59,9 @@ gboolean pk_backend_reset (PkBackend *backend);
+ gboolean pk_backend_set_name (PkBackend *backend,
+ const gchar *name)
+ G_GNUC_WARN_UNUSED_RESULT;
++gboolean pk_backend_set_proxy (PkBackend *backend,
++ const gchar *proxy_http,
++ const gchar *proxy_ftp);
+ gchar *pk_backend_get_name (PkBackend *backend)
+ G_GNUC_WARN_UNUSED_RESULT;
+ gboolean pk_backend_get_backend_detail (PkBackend *backend,
+diff --git a/src/pk-backend-spawn.c b/src/pk-backend-spawn.c
+index f9e8b68..f9c9f12 100644
+--- a/src/pk-backend-spawn.c
++++ b/src/pk-backend-spawn.c
+@@ -313,7 +313,7 @@ pk_backend_spawn_parse_stdout (PkBackendSpawn *backend_spawn, const gchar *line)
+ ret = FALSE;
+ goto out;
+ }
+- pk_backend_no_percentage_updates (backend_spawn->priv->backend);
++ pk_backend_set_percentage (backend_spawn->priv->backend, PK_BACKEND_PERCENTAGE_INVALID);
+ } else if (pk_strequal (command, "repo-signature-required")) {
+
+ if (size != 9+99) {
+@@ -440,6 +440,44 @@ pk_backend_spawn_helper_new (PkBackendSpawn *backend_spawn)
+ }
+
+ /**
++ * pk_backend_spawn_get_envp:
++ *
++ * Return all the environment variables the script will need
++ **/
++static gchar **
++pk_backend_spawn_get_envp (PkBackendSpawn *backend_spawn)
++{
++ gchar **envp;
++ gchar *value;
++ gchar *line;
++ GPtrArray *array;
++
++ array = g_ptr_array_new ();
++
++ /* http_proxy */
++ value = pk_backend_get_proxy_http (backend_spawn->priv->backend);
++ if (!pk_strzero (value)) {
++ line = g_strdup_printf ("%s=%s", "http_proxy", value);
++ pk_debug ("setting evp '%s'", line);
++ g_ptr_array_add (array, line);
++ }
++ g_free (value);
++
++ /* ftp_proxy */
++ value = pk_backend_get_proxy_ftp (backend_spawn->priv->backend);
++ if (!pk_strzero (value)) {
++ line = g_strdup_printf ("%s=%s", "ftp_proxy", value);
++ pk_debug ("setting evp '%s'", line);
++ g_ptr_array_add (array, line);
++ }
++ g_free (value);
++
++ envp = pk_ptr_array_to_argv (array);
++ g_ptr_array_free (array, TRUE);
++ return envp;
++}
++
++/**
+ * pk_backend_spawn_helper_va_list:
+ **/
+ static gboolean
+@@ -448,6 +486,7 @@ pk_backend_spawn_helper_va_list (PkBackendSpawn *backend_spawn, const gchar *exe
+ gboolean ret;
+ gchar *filename;
+ gchar **argv;
++ gchar **envp;
+
+ g_return_val_if_fail (PK_IS_BACKEND_SPAWN (backend_spawn), FALSE);
+
+@@ -476,7 +515,8 @@ pk_backend_spawn_helper_va_list (PkBackendSpawn *backend_spawn, const gchar *exe
+ argv[0] = g_strdup (filename);
+
+ pk_backend_spawn_helper_new (backend_spawn);
+- ret = pk_spawn_argv (backend_spawn->priv->spawn, argv);
++ envp = pk_backend_spawn_get_envp (backend_spawn);
++ ret = pk_spawn_argv (backend_spawn->priv->spawn, argv, envp);
+ if (!ret) {
+ pk_backend_spawn_helper_delete (backend_spawn);
+ pk_backend_error_code (backend_spawn->priv->backend, PK_ERROR_ENUM_INTERNAL_ERROR,
+diff --git a/src/pk-backend.c b/src/pk-backend.c
+index 225c488..37ed024 100644
+--- a/src/pk-backend.c
++++ b/src/pk-backend.c
+@@ -33,6 +33,7 @@
+ #include <glib/gprintf.h>
+ #include <pk-network.h>
+
++#include "pk-package-item.h"
+ #include "pk-debug.h"
+ #include "pk-common.h"
+ #include "pk-marshal.h"
+@@ -44,13 +45,6 @@
+ #define PK_BACKEND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_BACKEND, PkBackendPrivate))
+
+ /**
+- * PK_BACKEND_PERCENTAGE_INVALID:
+- *
+- * The unknown percentage value
+- */
+-#define PK_BACKEND_PERCENTAGE_INVALID 101
+-
+-/**
+ * PK_BACKEND_PERCENTAGE_DEFAULT:
+ *
+ * The default percentage value, should never be emitted, but should be
+@@ -84,12 +78,15 @@ struct _PkBackendPrivate
+ GHashTable *eulas;
+ gchar *name;
+ gchar *c_tid;
++ gchar *proxy_http;
++ gchar *proxy_ftp;
+ gboolean locked;
+ gboolean set_error;
+ gboolean set_signature;
+ gboolean set_eula;
+ gboolean has_sent_package;
+ PkNetwork *network;
++ PkPackageItem *last_package;
+ PkRoleEnum role; /* this never changes for the lifetime of a transaction */
+ PkStatusEnum status; /* this changes */
+ PkExitEnum exit;
+@@ -592,6 +589,44 @@ out:
+ }
+
+ /**
++ * pk_backend_set_proxy:
++ **/
++gboolean
++pk_backend_set_proxy (PkBackend *backend, const gchar *proxy_http, const gchar *proxy_ftp)
++{
++ g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
++ g_free (backend->priv->proxy_http);
++ g_free (backend->priv->proxy_ftp);
++ backend->priv->proxy_http = g_strdup (proxy_http);
++ backend->priv->proxy_ftp = g_strdup (proxy_ftp);
++ return TRUE;
++}
++
++/**
++ * pk_backend_get_proxy_http:
++ *
++ * Return value: proxy string in the form username:password@server:port
++ **/
++gchar *
++pk_backend_get_proxy_http (PkBackend *backend)
++{
++ g_return_val_if_fail (PK_IS_BACKEND (backend), NULL);
++ return g_strdup (backend->priv->proxy_http);
++}
++
++/**
++ * pk_backend_get_proxy_ftp:
++ *
++ * Return value: proxy string in the form username:password@server:port
++ **/
++gchar *
++pk_backend_get_proxy_ftp (PkBackend *backend)
++{
++ g_return_val_if_fail (PK_IS_BACKEND (backend), NULL);
++ return g_strdup (backend->priv->proxy_ftp);
++}
++
++/**
+ * pk_backend_lock:
+ *
+ * Responsible for initialising the external backend object.
+@@ -803,35 +838,6 @@ pk_backend_set_sub_percentage (PkBackend *backend, guint percentage)
+ }
+
+ /**
+- * pk_backend_no_percentage_updates:
+- **/
+-gboolean
+-pk_backend_no_percentage_updates (PkBackend *backend)
+-{
+- g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
+- g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
+-
+- /* have we already set an error? */
+- if (backend->priv->set_error) {
+- pk_warning ("already set error, cannot process");
+- return FALSE;
+- }
+-
+- /* set the same twice? */
+- if (backend->priv->last_percentage == PK_BACKEND_PERCENTAGE_INVALID) {
+- pk_debug ("duplicate set of %i", PK_BACKEND_PERCENTAGE_INVALID);
+- return FALSE;
+- }
+-
+- /* invalidate previous percentage */
+- backend->priv->last_percentage = PK_BACKEND_PERCENTAGE_INVALID;
+-
+- /* emit the progress changed signal */
+- pk_backend_emit_progress_changed (backend);
+- return TRUE;
+-}
+-
+-/**
+ * pk_backend_set_status:
+ **/
+ gboolean
+@@ -901,11 +907,26 @@ gboolean
+ pk_backend_package (PkBackend *backend, PkInfoEnum info, const gchar *package_id, const gchar *summary)
+ {
+ gchar *summary_safe;
++ PkPackageItem *item;
++ gboolean ret;
+
+ g_return_val_if_fail (PK_IS_BACKEND (backend), FALSE);
+ g_return_val_if_fail (package_id != NULL, FALSE);
+ g_return_val_if_fail (backend->priv->locked != FALSE, FALSE);
+
++ /* check against the old one */
++ item = pk_package_item_new (info, package_id, summary);
++ ret = pk_package_item_equal (item, backend->priv->last_package);
++ if (ret) {
++ pk_package_item_free (item);
++ pk_debug ("skipping duplicate %s", package_id);
++ return FALSE;
++ }
++ /* update the 'last' package */
++ pk_package_item_free (backend->priv->last_package);
++ backend->priv->last_package = pk_package_item_copy (item);
++ pk_package_item_free (item);
++
+ /* have we already set an error? */
+ if (backend->priv->set_error) {
+ pk_warning ("already set error, cannot process");
+@@ -1689,6 +1710,8 @@ pk_backend_finalize (GObject *object)
+ pk_debug ("backend finalise");
+
+ pk_backend_reset (backend);
++ g_free (backend->priv->proxy_http);
++ g_free (backend->priv->proxy_ftp);
+ g_free (backend->priv->name);
+ g_free (backend->priv->c_tid);
+ g_object_unref (backend->priv->time);
+@@ -1818,6 +1841,7 @@ pk_backend_reset (PkBackend *backend)
+
+ /* TODO: need to wait for Finished() if running */
+
++ pk_package_item_free (backend->priv->last_package);
+ backend->priv->set_error = FALSE;
+ backend->priv->set_signature = FALSE;
+ backend->priv->set_eula = FALSE;
+@@ -1825,6 +1849,7 @@ pk_backend_reset (PkBackend *backend)
+ backend->priv->finished = FALSE;
+ backend->priv->has_sent_package = FALSE;
+ backend->priv->thread = NULL;
++ backend->priv->last_package = NULL;
+ backend->priv->status = PK_STATUS_ENUM_UNKNOWN;
+ backend->priv->exit = PK_EXIT_ENUM_UNKNOWN;
+ backend->priv->role = PK_ROLE_ENUM_UNKNOWN;
+@@ -1855,8 +1880,11 @@ pk_backend_init (PkBackend *backend)
+ backend->priv->handle = NULL;
+ backend->priv->name = NULL;
+ backend->priv->c_tid = NULL;
++ backend->priv->proxy_http = NULL;
++ backend->priv->proxy_ftp = NULL;
+ backend->priv->file_changed_func = NULL;
+ backend->priv->file_changed_data = NULL;
++ backend->priv->last_package = NULL;
+ backend->priv->locked = FALSE;
+ backend->priv->signal_finished = 0;
+ backend->priv->signal_error_timeout = 0;
+diff --git a/src/pk-backend.h b/src/pk-backend.h
+index 95b7fa8..fb17e3c 100644
+--- a/src/pk-backend.h
++++ b/src/pk-backend.h
+@@ -30,6 +30,13 @@
+
+ G_BEGIN_DECLS
+
++/**
++ * PK_BACKEND_PERCENTAGE_INVALID:
++ *
++ * The unknown percentage value
++ */
++#define PK_BACKEND_PERCENTAGE_INVALID 101
++
+ typedef struct _PkBackend PkBackend;
+
+ /* set the state */
+@@ -51,7 +58,6 @@ gboolean pk_backend_set_sub_percentage (PkBackend *backend,
+ guint percentage);
+ gboolean pk_backend_set_exit_code (PkBackend *backend,
+ PkExitEnum exit);
+-gboolean pk_backend_no_percentage_updates (PkBackend *backend);
+ gboolean pk_backend_set_transaction_data (PkBackend *backend,
+ const gchar *data);
+
+@@ -66,6 +72,8 @@ gboolean pk_backend_get_progress (PkBackend *backend,
+ guint *elapsed,
+ guint *remaining);
+ guint pk_backend_get_runtime (PkBackend *backend);
++gchar *pk_backend_get_proxy_ftp (PkBackend *backend);
++gchar *pk_backend_get_proxy_http (PkBackend *backend);
+
+ /* signal helpers */
+ gboolean pk_backend_finished (PkBackend *backend);
+diff --git a/src/pk-engine.c b/src/pk-engine.c
+index db81d36..028a0d0 100644
+--- a/src/pk-engine.c
++++ b/src/pk-engine.c
+@@ -101,6 +101,7 @@ struct PkEnginePrivate
+ PkNetwork *network;
+ PkSecurity *security;
+ PkNotify *notify;
++ PkConf *conf;
+ PkFileMonitor *file_monitor;
+ PkRoleEnum actions;
+ PkGroupEnum groups;
+@@ -579,10 +580,15 @@ pk_engine_init (PkEngine *engine)
+ DBusGConnection *connection;
+ gboolean ret;
+ gchar *filename;
++ gchar *proxy_http;
++ gchar *proxy_ftp;
+
+ engine->priv = PK_ENGINE_GET_PRIVATE (engine);
+ engine->priv->restart_schedule = FALSE;
+
++ /* use the config file */
++ engine->priv->conf = pk_conf_new ();
++
+ /* setup the backend backend */
+ engine->priv->backend = pk_backend_new ();
+ g_signal_connect (engine->priv->backend, "finished",
+@@ -639,6 +645,13 @@ pk_engine_init (PkEngine *engine)
+ G_CALLBACK (pk_engine_file_monitor_changed_cb), engine);
+ g_free (filename);
+
++ /* set the proxy */
++ proxy_http = pk_conf_get_string (engine->priv->conf, "ProxyHTTP");
++ proxy_ftp = pk_conf_get_string (engine->priv->conf, "ProxyFTP");
++ pk_backend_set_proxy (engine->priv->backend, proxy_http, proxy_ftp);
++ g_free (proxy_http);
++ g_free (proxy_ftp);
++
+ engine->priv->transaction_list = pk_transaction_list_new ();
+ g_signal_connect (engine->priv->transaction_list, "changed",
+ G_CALLBACK (pk_engine_transaction_list_changed_cb), engine);
+@@ -696,6 +709,7 @@ pk_engine_finalize (GObject *object)
+ g_object_unref (engine->priv->notify);
+ g_object_unref (engine->priv->backend);
+ g_object_unref (engine->priv->cache);
++ g_object_unref (engine->priv->conf);
+
+ G_OBJECT_CLASS (pk_engine_parent_class)->finalize (object);
+ }
+diff --git a/src/pk-network-unix.c b/src/pk-network-unix.c
+index 11c23a2..74b266c 100644
+--- a/src/pk-network-unix.c
++++ b/src/pk-network-unix.c
+@@ -138,6 +138,11 @@ pk_network_unix_get_network_state (PkNetworkUnix *network_unix)
+ continue;
+ }
+
++ /* is loopback? */
++ if (pk_strequal (sections[0], "lo")) {
++ continue;
++ }
++
+ /* is correct parameters? */
+ number_sections = g_strv_length (sections);
+ if (number_sections != 11) {
+@@ -145,9 +150,8 @@ pk_network_unix_get_network_state (PkNetworkUnix *network_unix)
+ continue;
+ }
+
+- /* is MTU and gateway nonzero? */
+- if (!pk_strequal (sections[8], "0") &&
+- !pk_strequal (sections[2], "00000000")) {
++ /* is gateway nonzero? */
++ if (!pk_strequal (sections[2], "00000000")) {
+ pk_debug ("interface %s is valid", sections[0]);
+ online = TRUE;
+ }
+diff --git a/src/pk-network.c b/src/pk-network.c
+index 9656958..0ad839e 100644
+--- a/src/pk-network.c
++++ b/src/pk-network.c
+@@ -39,6 +39,7 @@
+ #ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif /* HAVE_UNISTD_H */
++#include <libgbus.h>
+
+ #include <glib/gi18n.h>
+
+@@ -67,6 +68,7 @@ struct _PkNetworkPrivate
+ PkNetworkNm *net_nm;
+ PkNetworkUnix *net_unix;
+ PkConf *conf;
++ LibGBus *nm_bus;
+ };
+
+ enum {
+@@ -154,6 +156,7 @@ pk_network_class_init (PkNetworkClass *klass)
+ static void
+ pk_network_init (PkNetwork *network)
+ {
++ gboolean nm_alive;
+ network->priv = PK_NETWORK_GET_PRIVATE (network);
+ network->priv->conf = pk_conf_new ();
+ network->priv->net_nm = pk_network_nm_new ();
+@@ -167,6 +170,17 @@ pk_network_init (PkNetwork *network)
+ network->priv->use_nm = pk_conf_get_bool (network->priv->conf, "UseNetworkManager");
+ network->priv->use_unix = pk_conf_get_bool (network->priv->conf, "UseNetworkHeuristic");
+
++ /* check if NM is on the bus */
++ network->priv->nm_bus = libgbus_new ();
++ libgbus_assign (network->priv->nm_bus, LIBGBUS_SYSTEM, "org.freedesktop.NetworkManager");
++ nm_alive = libgbus_is_connected (network->priv->nm_bus);
++
++ /* NetworkManager isn't up, so we can't use it */
++ if (network->priv->use_nm && !nm_alive) {
++ pk_warning ("UseNetworkManager true, but org.freedesktop.NetworkManager not up");
++ network->priv->use_nm = FALSE;
++ }
++
+ #if !PK_BUILD_NETWORKMANAGER
+ /* check we can actually use the default */
+ if (network->priv->use_nm) {
+@@ -190,6 +204,7 @@ pk_network_finalize (GObject *object)
+
+ g_return_if_fail (network->priv != NULL);
+ g_object_unref (network->priv->conf);
++ g_object_unref (network->priv->nm_bus);
+ g_object_unref (network->priv->net_nm);
+ g_object_unref (network->priv->net_unix);
+ G_OBJECT_CLASS (pk_network_parent_class)->finalize (object);
+diff --git a/src/pk-spawn.c b/src/pk-spawn.c
+index 9b415b1..c4622f9 100644
+--- a/src/pk-spawn.c
++++ b/src/pk-spawn.c
+@@ -273,7 +273,7 @@ pk_spawn_kill (PkSpawn *spawn)
+ *
+ **/
+ gboolean
+-pk_spawn_argv (PkSpawn *spawn, gchar **argv)
++pk_spawn_argv (PkSpawn *spawn, gchar **argv, gchar **envp)
+ {
+ gboolean ret;
+
+@@ -284,7 +284,7 @@ pk_spawn_argv (PkSpawn *spawn, gchar **argv)
+ spawn->priv->finished = FALSE;
+
+ /* create spawned object for tracking */
+- ret = g_spawn_async_with_pipes (NULL, argv, NULL,
++ ret = g_spawn_async_with_pipes (NULL, argv, envp,
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+ NULL, NULL, &spawn->priv->child_pid,
+ NULL, /* stdin */
+@@ -484,6 +484,7 @@ libst_spawn (LibSelfTest *test)
+ gboolean ret;
+ gchar *path;
+ gchar **argv;
++ gchar **envp;
+
+ if (libst_start (test, "PkSpawn", CLASS_AUTO) == FALSE) {
+ return;
+@@ -496,7 +497,7 @@ libst_spawn (LibSelfTest *test)
+ libst_title (test, "make sure return error for missing file");
+ mexit = BAD_EXIT;
+ argv = g_strsplit ("pk-spawn-test-xxx.sh", " ", 0);
+- ret = pk_spawn_argv (spawn, argv);
++ ret = pk_spawn_argv (spawn, argv, NULL);
+ g_strfreev (argv);
+ if (ret == FALSE) {
+ libst_success (test, "failed to run invalid file");
+@@ -517,7 +518,7 @@ libst_spawn (LibSelfTest *test)
+ mexit = -1;
+ path = pk_test_get_data ("pk-spawn-test.sh");
+ argv = g_strsplit (path, " ", 0);
+- ret = pk_spawn_argv (spawn, argv);
++ ret = pk_spawn_argv (spawn, argv, NULL);
+ g_free (path);
+ g_strfreev (argv);
+ if (ret) {
+@@ -558,11 +559,34 @@ libst_spawn (LibSelfTest *test)
+ new_spawn_object (test, &spawn);
+
+ /************************************************************/
++ libst_title (test, "make sure we set the proxy");
++ mexit = -1;
++ path = pk_test_get_data ("pk-spawn-proxy.sh");
++ argv = g_strsplit (path, " ", 0);
++ envp = g_strsplit ("http_proxy=username:password@server:port "
++ "ftp_proxy=username:password@server:port", " ", 0);
++ ret = pk_spawn_argv (spawn, argv, envp);
++ g_free (path);
++ g_strfreev (argv);
++ if (ret) {
++ libst_success (test, "ran correct file");
++ } else {
++ libst_failed (test, "did not run helper");
++ }
++
++ /* wait for finished */
++ libst_loopwait (test, 10000);
++ libst_loopcheck (test);
++
++ /* get new object */
++ new_spawn_object (test, &spawn);
++
++ /************************************************************/
+ libst_title (test, "make sure run correct helper, and kill it");
+ mexit = BAD_EXIT;
+ path = pk_test_get_data ("pk-spawn-test.sh");
+ argv = g_strsplit (path, " ", 0);
+- ret = pk_spawn_argv (spawn, argv);
++ ret = pk_spawn_argv (spawn, argv, NULL);
+ g_free (path);
+ g_strfreev (argv);
+ if (ret) {
+@@ -592,7 +616,7 @@ libst_spawn (LibSelfTest *test)
+ mexit = BAD_EXIT;
+ path = pk_test_get_data ("pk-spawn-test-sigquit.sh");
+ argv = g_strsplit (path, " ", 0);
+- ret = pk_spawn_argv (spawn, argv);
++ ret = pk_spawn_argv (spawn, argv, NULL);
+ g_free (path);
+ g_strfreev (argv);
+ if (ret) {
+@@ -618,7 +642,7 @@ libst_spawn (LibSelfTest *test)
+ libst_title (test, "run lots of data for profiling");
+ path = pk_test_get_data ("pk-spawn-test-profiling.sh");
+ argv = g_strsplit (path, " ", 0);
+- ret = pk_spawn_argv (spawn, argv);
++ ret = pk_spawn_argv (spawn, argv, NULL);
+ g_free (path);
+ g_strfreev (argv);
+ if (ret) {
+diff --git a/src/pk-spawn.h b/src/pk-spawn.h
+index 1b20fef..0e58859 100644
+--- a/src/pk-spawn.h
++++ b/src/pk-spawn.h
+@@ -52,7 +52,8 @@ GType pk_spawn_get_type (void) G_GNUC_CONST;
+ PkSpawn *pk_spawn_new (void);
+
+ gboolean pk_spawn_argv (PkSpawn *spawn,
+- gchar **argv)
++ gchar **argv,
++ gchar **envp)
+ G_GNUC_WARN_UNUSED_RESULT;
+ gboolean pk_spawn_kill (PkSpawn *spawn);
+