[gnome-documents/f19] Backport support for previewing password protected PDFs (GNOME #700716)

Debarshi Ray rishi at fedoraproject.org
Thu Jul 11 13:05:40 UTC 2013


commit c67ead5e5d25f16b17f926241518fbe6ce06c2bb
Author: Debarshi Ray <debarshir at gnome.org>
Date:   Thu Jul 11 15:05:16 2013 +0200

    Backport support for previewing password protected PDFs (GNOME #700716)

 ...ort-previewing-of-password-protected-PDFs.patch |  484 ++++++++++++++++++++
 gnome-documents.spec                               |   13 +-
 2 files changed, 496 insertions(+), 1 deletions(-)
---
diff --git a/0001-Support-previewing-of-password-protected-PDFs.patch b/0001-Support-previewing-of-password-protected-PDFs.patch
new file mode 100644
index 0000000..47c49bb
--- /dev/null
+++ b/0001-Support-previewing-of-password-protected-PDFs.patch
@@ -0,0 +1,484 @@
+From 2781cd44e764b89f0342f5efc2afc5ca35e54efc Mon Sep 17 00:00:00 2001
+From: Debarshi Ray <debarshir at gnome.org>
+Date: Mon, 27 May 2013 16:48:49 +0200
+Subject: [PATCH] Support previewing of password protected PDFs
+
+Delay changing the window mode when loading a document, so that we are
+still in the overview when presenting the dialog to enter the password.
+We switch the mode to preview when load-finished or load-error has
+been received.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=700716
+---
+ src/Makefile-js.am      |   1 +
+ src/documents.js        |  24 ++++++-----
+ src/embed.js            |  28 ++++++++++---
+ src/lib/gd-pdf-loader.c |  40 +++++++++++++++---
+ src/lib/gd-pdf-loader.h |   1 +
+ src/mainToolbar.js      |   3 +-
+ src/password.js         | 105 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 181 insertions(+), 21 deletions(-)
+ create mode 100644 src/password.js
+
+diff --git a/src/Makefile-js.am b/src/Makefile-js.am
+index 9387e0f..764981e 100644
+--- a/src/Makefile-js.am
++++ b/src/Makefile-js.am
+@@ -11,6 +11,7 @@ dist_js_DATA = \
+     manager.js \
+     miners.js \
+     notifications.js \
++    password.js \
+     places.js \
+     presentation.js \
+     preview.js \
+diff --git a/src/documents.js b/src/documents.js
+index ed5fa92..c412b27 100644
+--- a/src/documents.js
++++ b/src/documents.js
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (c) 2011, 2012 Red Hat, Inc.
++ * Copyright (c) 2011, 2012, 2013 Red Hat, Inc.
+  *
+  * Gnome Documents is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by the
+@@ -19,6 +19,7 @@
+  *
+  */
+ 
++const EvDocument = imports.gi.EvinceDocument;
+ const EvView = imports.gi.EvinceView;
+ const GdkPixbuf = imports.gi.GdkPixbuf;
+ const Gio = imports.gi.Gio;
+@@ -548,7 +549,7 @@ const DocCommon = new Lang.Class({
+     },
+ 
+     print: function(toplevel) {
+-        this.load(null, Lang.bind(this,
++        this.load(null, null, Lang.bind(this,
+             function(doc, docModel, error) {
+                 if (error) {
+                     log('Unable to print document ' + this.uri + ': ' + error);
+@@ -621,8 +622,8 @@ const LocalDocument = new Lang.Class({
+             this.typeDescription = Gio.content_type_get_description(this.mimeType);
+     },
+ 
+-    load: function(cancellable, callback) {
+-        GdPrivate.pdf_loader_load_uri_async(this.uri, cancellable, Lang.bind(this,
++    load: function(passwd, cancellable, callback) {
++        GdPrivate.pdf_loader_load_uri_async(this.uri, passwd, cancellable, Lang.bind(this,
+             function(source, res) {
+                 try {
+                     let docModel = GdPrivate.pdf_loader_load_uri_finish(res);
+@@ -687,7 +688,7 @@ const GoogleDocument = new Lang.Class({
+                  }));
+     },
+ 
+-    load: function(cancellable, callback) {
++    load: function(passwd, cancellable, callback) {
+         this._createGDataEntry(cancellable, Lang.bind(this,
+             function(entry, service, exception) {
+                 if (exception) {
+@@ -839,7 +840,7 @@ const SkydriveDocument = new Lang.Class({
+                  }));
+     },
+ 
+-    load: function(cancellable, callback) {
++    load: function(passwd, cancellable, callback) {
+         this._createZpjEntry(cancellable, Lang.bind(this,
+             function(entry, service, exception) {
+                 if (exception) {
+@@ -1018,6 +1019,11 @@ const DocumentManager = new Lang.Class({
+         if (error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+             return;
+ 
++        if (error.matches(EvDocument.DocumentError, EvDocument.DocumentError.ENCRYPTED)) {
++            this.emit('password-needed', doc);
++            return;
++        }
++
+         // Translators: %s is the title of a document
+         let message = _("Oops! Unable to load ā€œ%sā€").format(doc.name);
+         let exception = this._humanizeError(error);
+@@ -1042,7 +1048,7 @@ const DocumentManager = new Lang.Class({
+         this.emit('load-finished', doc, docModel);
+     },
+ 
+-    reloadActiveItem: function() {
++    reloadActiveItem: function(passwd) {
+         let doc = this.getActiveItem();
+ 
+         if (!doc)
+@@ -1055,7 +1061,7 @@ const DocumentManager = new Lang.Class({
+         this._clearActiveDocModel();
+ 
+         this._loaderCancellable = new Gio.Cancellable();
+-        doc.load(this._loaderCancellable, Lang.bind(this, this._onDocumentLoaded));
++        doc.load(passwd, this._loaderCancellable, Lang.bind(this, this._onDocumentLoaded));
+         this.emit('load-started', doc);
+     },
+ 
+@@ -1079,7 +1085,7 @@ const DocumentManager = new Lang.Class({
+         recentManager.add_item(doc.uri);
+ 
+         this._loaderCancellable = new Gio.Cancellable();
+-        doc.load(this._loaderCancellable, Lang.bind(this, this._onDocumentLoaded));
++        doc.load(null, this._loaderCancellable, Lang.bind(this, this._onDocumentLoaded));
+         this.emit('load-started', doc);
+     },
+ 
+diff --git a/src/embed.js b/src/embed.js
+index 1d1744a..13b7e66 100644
+--- a/src/embed.js
++++ b/src/embed.js
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (c) 2011 Red Hat, Inc.
++ * Copyright (c) 2011, 2013 Red Hat, Inc.
+  *
+  * Gnome Documents is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by the
+@@ -25,6 +25,7 @@ const Mainloop = imports.mainloop;
+ const Application = imports.application;
+ const MainToolbar = imports.mainToolbar;
+ const Notifications = imports.notifications;
++const Password = imports.password;
+ const Preview = imports.preview;
+ const Edit = imports.edit;
+ const Selections = imports.selections;
+@@ -269,6 +270,8 @@ const Embed = new Lang.Class({
+                                             Lang.bind(this, this._onLoadFinished));
+         Application.documentManager.connect('load-error',
+                                             Lang.bind(this, this._onLoadError));
++        Application.documentManager.connect('password-needed',
++                                            Lang.bind(this, this._onPasswordNeeded));
+ 
+         this._onQueryStatusChanged();
+ 
+@@ -354,15 +357,13 @@ const Embed = new Lang.Class({
+     },
+ 
+     _onActiveItemChanged: function(manager, doc) {
+-        let newMode = WindowMode.WindowMode.OVERVIEW;
+-
+         if (doc) {
+             let collection = Application.collectionManager.getItemById(doc.id);
+             if (!collection)
+-                newMode = WindowMode.WindowMode.PREVIEW;
++                return;
+         }
+ 
+-        Application.modeController.setWindowMode(newMode);
++        Application.modeController.setWindowMode(WindowMode.WindowMode.OVERVIEW);
+     },
+ 
+     _clearLoadTimer: function() {
+@@ -385,6 +386,8 @@ const Embed = new Lang.Class({
+     },
+ 
+     _onLoadFinished: function(manager, doc, docModel) {
++        Application.modeController.setWindowMode(WindowMode.WindowMode.PREVIEW);
++
+         docModel.set_sizing_mode(EvView.SizingMode.AUTOMATIC);
+         docModel.set_page_layout(EvView.PageLayout.AUTOMATIC);
+         this._toolbar.setModel(docModel);
+@@ -397,11 +400,26 @@ const Embed = new Lang.Class({
+     },
+ 
+     _onLoadError: function(manager, doc, message, exception) {
++        Application.modeController.setWindowMode(WindowMode.WindowMode.PREVIEW);
++
+         this._clearLoadTimer();
+         this._spinnerBox.stop();
+         this._setError(message, exception.message);
+     },
+ 
++    _onPasswordNeeded: function(manager, doc) {
++        this._clearLoadTimer();
++        this._spinnerBox.stop();
++
++        let dialog = new Password.PasswordDialog(doc);
++        dialog.widget.connect('response', Lang.bind(this,
++            function(widget, response) {
++                dialog.widget.destroy();
++                if (response == Gtk.ResponseType.CANCEL)
++                    Application.documentManager.setActiveItem(null);
++            }));
++    },
++
+     _prepareForOverview: function() {
+         if (this._preview)
+             this._preview.setModel(null);
+diff --git a/src/lib/gd-pdf-loader.c b/src/lib/gd-pdf-loader.c
+index 6b1aed2..f835f80 100644
+--- a/src/lib/gd-pdf-loader.c
++++ b/src/lib/gd-pdf-loader.c
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (c) 2011, 2012 Red Hat, Inc.
++ * Copyright (c) 2011, 2012, 2013 Red Hat, Inc.
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU Lesser General Public License as published by 
+@@ -41,6 +41,9 @@ typedef struct {
+   gchar *pdf_path;
+   GPid unoconv_pid;
+ 
++  gchar *passwd;
++  gboolean passwd_tried;
++
+   GFile *download_file;
+   GInputStream *stream;
+ 
+@@ -135,6 +138,7 @@ pdf_load_job_free (PdfLoadJob *job)
+   g_clear_object (&job->zpj_entry);
+ 
+   g_free (job->uri);
++  g_free (job->passwd);
+   g_free (job->resource_id);
+ 
+   if (job->pdf_path != NULL) {
+@@ -157,6 +161,7 @@ pdf_load_job_new (GSimpleAsyncResult *result,
+                   const gchar *uri,
+                   GDataEntry *gdata_entry,
+                   ZpjSkydriveEntry *zpj_entry,
++                  const gchar *passwd,
+                   GCancellable *cancellable)
+ {
+   PdfLoadJob *retval;
+@@ -169,6 +174,8 @@ pdf_load_job_new (GSimpleAsyncResult *result,
+ 
+   if (uri != NULL)
+     retval->uri = g_strdup (uri);
++  if (passwd != NULL)
++    retval->passwd = g_strdup (passwd);
+   if (gdata_entry != NULL)
+     retval->gdata_entry = g_object_ref (gdata_entry);
+   if (zpj_entry != NULL)
+@@ -222,14 +229,23 @@ ev_load_job_done (EvJob *ev_job,
+   PdfLoadJob *job = user_data;
+ 
+   if (ev_job_is_failed (ev_job) || (ev_job->document == NULL)) {
+-    if (job->from_old_cache)
++    if (job->from_old_cache) {
+       pdf_load_job_force_refresh_cache (job);
+-    else
++    } else if (g_error_matches (ev_job->error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_ENCRYPTED)
++               && job->passwd != NULL
++               && !job->passwd_tried) {
++      /* EvJobLoad tries using the password only after the job has
++       * failed once.
++       */
++      ev_job_scheduler_push_job (ev_job, EV_JOB_PRIORITY_NONE);
++      job->passwd_tried = TRUE;
++    } else {
+       pdf_load_job_complete_error (job, (ev_job->error != NULL) ? 
+                                    g_error_copy (ev_job->error) :
+                                    g_error_new_literal (G_IO_ERROR,
+                                                         G_IO_ERROR_FAILED,
+                                                         _("Unable to load the document")));
++    }
+ 
+     g_clear_object (&ev_job);
+     return;
+@@ -255,6 +271,9 @@ pdf_load_job_from_pdf (PdfLoadJob *job)
+   }
+ 
+   ev_job = ev_job_load_new ((uri != NULL) ? (uri) : (job->uri));
++  if (job->passwd != NULL)
++    ev_job_load_set_password (EV_JOB_LOAD (ev_job), job->passwd);
++
+   g_signal_connect (ev_job, "finished",
+                     G_CALLBACK (ev_load_job_done), job);
+ 
+@@ -1015,8 +1034,17 @@ pdf_load_job_start (PdfLoadJob *job)
+     pdf_load_job_from_regular_file (job);
+ }
+ 
++/**
++ * gd_pdf_loader_load_uri_async:
++ * @uri:
++ * @passwd: (allow-none):
++ * @cancellable: (allow-none):
++ * @callback:
++ * @user_data:
++ */
+ void
+ gd_pdf_loader_load_uri_async (const gchar *uri,
++                              const gchar *passwd,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+@@ -1027,7 +1055,7 @@ gd_pdf_loader_load_uri_async (const gchar *uri,
+   result = g_simple_async_result_new (NULL, callback, user_data,
+                                       gd_pdf_loader_load_uri_async);
+ 
+-  job = pdf_load_job_new (result, uri, NULL, NULL, cancellable);
++  job = pdf_load_job_new (result, uri, NULL, NULL, passwd, cancellable);
+ 
+   pdf_load_job_start (job);
+ 
+@@ -1068,7 +1096,7 @@ gd_pdf_loader_load_gdata_entry_async (GDataEntry *entry,
+   result = g_simple_async_result_new (NULL, callback, user_data,
+                                       gd_pdf_loader_load_gdata_entry_async);
+ 
+-  job = pdf_load_job_new (result, NULL, entry, NULL, cancellable);
++  job = pdf_load_job_new (result, NULL, entry, NULL, NULL, cancellable);
+   job->gdata_service = g_object_ref (service);
+ 
+   pdf_load_job_start (job);
+@@ -1110,7 +1138,7 @@ gd_pdf_loader_load_zpj_entry_async (ZpjSkydriveEntry *entry,
+   result = g_simple_async_result_new (NULL, callback, user_data,
+                                       gd_pdf_loader_load_zpj_entry_async);
+ 
+-  job = pdf_load_job_new (result, NULL, NULL, entry, cancellable);
++  job = pdf_load_job_new (result, NULL, NULL, entry, NULL, cancellable);
+   job->zpj_service = g_object_ref (service);
+ 
+   pdf_load_job_start (job);
+diff --git a/src/lib/gd-pdf-loader.h b/src/lib/gd-pdf-loader.h
+index 22b05b4..9e5ffb7 100644
+--- a/src/lib/gd-pdf-loader.h
++++ b/src/lib/gd-pdf-loader.h
+@@ -33,6 +33,7 @@
+ G_BEGIN_DECLS
+ 
+ void gd_pdf_loader_load_uri_async (const gchar *uri,
++                                   const gchar *passwd,
+                                    GCancellable *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data);
+diff --git a/src/mainToolbar.js b/src/mainToolbar.js
+index 25e6b94..a1b1c52 100644
+--- a/src/mainToolbar.js
++++ b/src/mainToolbar.js
+@@ -1,5 +1,5 @@
+ /*
+- * Copyright (c) 2011 Red Hat, Inc.
++ * Copyright (c) 2011, 2013 Red Hat, Inc.
+  *
+  * Gnome Documents is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by the
+@@ -58,6 +58,7 @@ const MainToolbar = new Lang.Class({
+             }));
+ 
+         Application.documentManager.connect('load-error', Lang.bind(this, this._onLoadErrorOrPassword));
++        Application.documentManager.connect('password-needed', Lang.bind(this, this._onLoadErrorOrPassword));
+     },
+ 
+     _onLoadErrorOrPassword: function() {
+diff --git a/src/password.js b/src/password.js
+new file mode 100644
+index 0000000..3a09b10
+--- /dev/null
++++ b/src/password.js
+@@ -0,0 +1,105 @@
++/*
++ * Copyright (c) 2013 Red Hat, Inc.
++ *
++ * Gnome Documents 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.
++ *
++ * Gnome Documents 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 Gnome Documents; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++ *
++ * Author: Debarshi Ray <debarshir at gnome.org>
++ *
++ */
++
++const Gio = imports.gi.Gio;
++const GLib = imports.gi.GLib;
++const Gtk = imports.gi.Gtk;
++const _ = imports.gettext.gettext;
++const C_ = imports.gettext.pgettext;
++
++const Application = imports.application;
++const Documents = imports.documents;
++const Mainloop = imports.mainloop;
++
++const Lang = imports.lang;
++
++const PasswordDialog = new Lang.Class({
++    Name: 'PasswordDialog',
++
++    _init: function(doc) {
++        let toplevel = Application.application.get_windows()[0];
++        this.widget = new Gtk.Dialog({ resizable: false,
++                                       transient_for: toplevel,
++                                       modal: true,
++                                       destroy_with_parent: true,
++                                       default_width: 400,
++                                       border_width: 6,
++                                       title: _("Password Required"),
++                                       hexpand: true });
++        this.widget.add_button('gtk-cancel', Gtk.ResponseType.CANCEL);
++        this.widget.add_button(_("_Unlock"), Gtk.ResponseType.OK);
++        this.widget.set_default_response(Gtk.ResponseType.OK);
++        this.widget.set_response_sensitive(Gtk.ResponseType.OK, false);
++
++        let grid = new Gtk.Grid({ column_spacing: 12,
++                                  row_spacing: 18,
++                                  border_width: 5,
++                                  margin_bottom: 6,
++                                  hexpand: true,
++                                  vexpand: true });
++
++        let contentArea = this.widget.get_content_area();
++        contentArea.pack_start(grid, true, true, 2);
++
++        let label;
++
++        let msg = _("Document %s is locked and requires a password to be opened."
++                   ).format(doc.name);
++        // Doesn't respect halign and hexpand.
++        label = new Gtk.Label({ label: msg,
++                                max_width_chars: 56,
++                                use_markup: true,
++                                wrap: true });
++        label.set_alignment(0.0, 0.5);
++        grid.attach(label, 0, 0, 2, 1);
++
++        let entry = new Gtk.Entry({ activates_default: true,
++                                    can_focus: true,
++                                    visibility: false,
++                                    hexpand: true });
++        label = new Gtk.Label({ label: _("_Password"),
++                                mnemonic_widget: entry,
++                                use_underline: true });
++        label.get_style_context().add_class('dim-label');
++        grid.attach(label, 0, 1, 1, 1);
++        grid.attach(entry, 1, 1, 1, 1);
++
++        entry.connect('realize', Lang.bind(this,
++            function() {
++                entry.grab_focus();
++            }));
++        entry.connect('changed', Lang.bind(this,
++            function() {
++                let length = entry.get_text_length();
++                this.widget.set_response_sensitive(Gtk.ResponseType.OK, (length != 0));
++            }));
++
++        this.widget.connect('response', Lang.bind(this,
++            function(widget, response) {
++                if (response != Gtk.ResponseType.OK)
++                    return;
++                let passwd = entry.get_text();
++                Application.documentManager.reloadActiveItem(passwd);
++            }));
++
++        this.widget.show_all();
++    }
++});
+-- 
+1.8.3.1
+
diff --git a/gnome-documents.spec b/gnome-documents.spec
index d736973..6333216 100644
--- a/gnome-documents.spec
+++ b/gnome-documents.spec
@@ -2,13 +2,17 @@
 
 Name:           gnome-documents
 Version:        3.8.3.1
-Release:        1%{?dist}
+Release:        2%{?dist}
 Summary:        A document manager application for GNOME
 
 License:        GPLv2+
 URL:            https://live.gnome.org/Design/Apps/Documents
 Source0:        http://ftp.acc.umu.se/pub/GNOME/sources/%{name}/3.8/%{name}-%{version}.tar.xz
 
+# https://bugzilla.gnome.org/show_bug.cgi?id=700716
+Patch0:         0001-Support-previewing-of-password-protected-PDFs.patch
+
+BuildRequires:  autoconf
 BuildRequires:  intltool
 BuildRequires:  libgdata-devel
 BuildRequires:  gnome-desktop3-devel
@@ -32,8 +36,12 @@ the Documents directory.
 
 %prep
 %setup -q
+%patch0 -p1
 
 %build
+autoreconf -fi
+intltoolize --automake -f
+
 %configure --disable-static --enable-getting-started
 make %{?_smp_mflags}
 
@@ -78,6 +86,9 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor >&/dev/null || :
 %{_mandir}/man1/gnome-documents.1.gz
 
 %changelog
+* Thu Jul 11 2013 Debarshi Ray <rishi at fedoraproject.org> - 3.8.3.1-2
+- Backport support for previewing password protected PDFs (GNOME #700716)
+
 * Fri Jun 14 2013 Debarshi Ray <rishi at fedoraproject.org> - 3.8.3.1-1
 - Update to 3.8.3.1
 


More information about the scm-commits mailing list