Package: mcomix
Version: 1.2.1mcomix3+git20190616-1
Severity: critical
Tags: patch upstream
Justification: causes serious data loss

Dear Maintainer,

User bookmarks for the MComix application are stored in a pickle file at
~/.local/share/mcomix/bookmarks.pickle .  The Python 3 fork of MComix which
Debian has switched to is unable to read this file when it has been written by
the earlier, Python 2 version of the application.  When this happens, the
application prints an error message to the console (which most users will not
see since they won't be opening the application from a terminal), and then
proceeds to start up.  Since no bookmarks have been loaded, when the application
saves the current bookmarks back to the file, the contents of the file are reset
and the user's existing bookmark collection is destroyed.

I reported this problem to upstream, here:

  https://github.com/multiSnow/mcomix3/issues/98

I also wrote a pull request to fix the problem, here:

  https://github.com/multiSnow/mcomix3/pull/99

The patch from that PR is attached.  The behavior of the application after
patching is that now it will be able to read a user bookmarks file produced by
the Python 2 version of the applicaton, and if any error is encountered, it will
make a backup of the bookmarks pickle file before proceeding, in order to avoid
data loss.

Thanks,
    Keshav

-- System Information:
Debian Release: bullseye/sid
  APT prefers unstable
  APT policy: (990, 'unstable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 5.3.0-3-amd64 (SMP w/4 CPU cores)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_OOT_MODULE, 
TAINT_UNSIGNED_MODULE
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE= 
(charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash
Init: systemd (via /run/systemd/system)

Versions of packages mcomix depends on:
ii  gir1.2-gtk-3.0    3.24.13-1
ii  python3           3.7.5-3
ii  python3-cairo     1.16.2-2
ii  python3-gi        3.34.0-3
ii  python3-gi-cairo  3.34.0-3
ii  python3-pil       6.2.1-2+b1

Versions of packages mcomix recommends:
ii  python3-chardet  3.0.4-4

Versions of packages mcomix suggests:
pn  mupdf-tools  <none>
ii  p7zip        16.02+dfsg-7
ii  unrar        1:5.6.6-2

-- no debconf information
>From 8a344788e18a31d34ae1681e1ebe75cd6a02f716 Mon Sep 17 00:00:00 2001
From: Keshav Kini <keshav.k...@gmail.com>
Date: Thu, 26 Dec 2019 19:45:23 -0800
Subject: [PATCH] Try to read bookmarks pickled by Python 2

When a pickle file is saved by Python 2, it can be unreadable from Python 3 if
certain kinds of objects are stored in it, such as non-Unicode strings.  To
reliably unpickle these objects, a fallback string encoding must be supplied to
`pickle.load()`.

It turns out that `datetime` objects, which MComix saves as part of each
bookmark, are serialized as non-Unicode binary strings (using the pickle
format's opcode `SHORT_BINSTRING`).  According to the [Python
documentation][1],

> Using `encoding='latin1'` is required for unpickling NumPy arrays and
> instances of *datetime*, *date* and *time* pickled by Python 2.

This commit does as recommended by the above sentence from the documentation.

Furthermore, this commit makes it so that if MComix fails to unpickle the
bookmarks file, it will back up the existing file before proceeding (because
otherwise it will be overwritten with an empty set of bookmarks later).  If the
backup file cannot be saved, the program will crash to avoid destroying the
user's data.

[1]: https://docs.python.org/3/library/pickle.html#pickle.Unpickler "pickle"
---
 mcomix/mcomix/bookmark_backend.py | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/mcomix/mcomix/bookmark_backend.py 
b/mcomix/mcomix/bookmark_backend.py
index 0492600..8293ee2 100644
--- a/mcomix/mcomix/bookmark_backend.py
+++ b/mcomix/mcomix/bookmark_backend.py
@@ -6,6 +6,7 @@ from gi.repository import Gtk
 import operator
 import datetime
 import time
+import shutil
 
 from mcomix import constants
 from mcomix import log
@@ -141,8 +142,8 @@ class __BookmarksStore(object):
             try:
                 mtime = os.stat(path).st_mtime
                 with open(path, 'rb') as fd:
-                    version = pickle.load(fd)
-                    packs = pickle.load(fd)
+                    version = pickle.load(fd, encoding='latin1')
+                    packs = pickle.load(fd, encoding='latin1')
 
                     for pack in packs:
                         # Handle old bookmarks without date_added attribute
@@ -155,6 +156,13 @@ class __BookmarksStore(object):
 
             except Exception:
                 log.error(_('! Could not parse bookmarks file %s'), path)
+                backup_path = path + '.bak'
+                if not os.path.exists(backup_path):
+                    log.warning(_('! Backing up bookmarks file to %s'), 
backup_path)
+                    shutil.copyfile(path, backup_path)
+                else:
+                    log.error(_('! Cannot create backup bookmarks file at 
%s'), backup_path)
+                    raise
 
         return bookmarks, mtime
 
-- 
2.24.1

Reply via email to