[gnome-documents] Support previewing of password protected PDFs
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-documents] Support previewing of password protected PDFs
- Date: Mon, 8 Jul 2013 21:04:51 +0000 (UTC)
commit cde99212000c2f2b0beb29d66956aad7b791b44e
Author: Debarshi Ray <debarshir gnome org>
Date: Mon May 27 16:48:49 2013 +0200
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(-)
---
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 96516a2..8b37c17 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 668de42..7bb747e 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;
@@ -268,6 +269,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();
@@ -353,15 +356,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() {
@@ -384,6 +385,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);
@@ -396,11 +399,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 f98fed9..5d3437f 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")));
+ }
return;
}
@@ -252,6 +268,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);
@@ -1013,8 +1032,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)
@@ -1025,7 +1053,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);
@@ -1066,7 +1094,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);
@@ -1108,7 +1136,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 c83d399..c14f80d 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 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();
+ }
+});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]