1import os
2
3from django.conf import settings
4from django.core.management.base import BaseCommand, CommandError
5from termcolor import colored as coloured
6
7from documents.models import Document
8from paperless.db import GnuPG
9
10
11class Command(BaseCommand):
12
13    help = (
14        "This is how you migrate your stored documents from an encrypted "
15        "state to an unencrypted one (or vice-versa)"
16    )
17
18    def add_arguments(self, parser):
19
20        parser.add_argument(
21            "from",
22            choices=("gpg", "unencrypted"),
23            help="The state you want to change your documents from"
24        )
25        parser.add_argument(
26            "to",
27            choices=("gpg", "unencrypted"),
28            help="The state you want to change your documents to"
29        )
30        parser.add_argument(
31            "--passphrase",
32            help="If PAPERLESS_PASSPHRASE isn't set already, you need to "
33                 "specify it here"
34        )
35
36    def handle(self, *args, **options):
37
38        try:
39            print(coloured(
40                "\n\nWARNING: This script is going to work directly on your "
41                "document originals, so\nWARNING: you probably shouldn't run "
42                "this unless you've got a recent backup\nWARNING: handy.  It "
43                "*should* work without a hitch, but be safe and backup your\n"
44                "WARNING: stuff first.\n\nHit Ctrl+C to exit now, or Enter to "
45                "continue.\n\n",
46                "yellow",
47                attrs=("bold",)
48            ))
49            __ = input()
50        except KeyboardInterrupt:
51            return
52
53        if options["from"] == options["to"]:
54            raise CommandError(
55                'The "from" and "to" values can\'t be the same.'
56            )
57
58        passphrase = options["passphrase"] or settings.PASSPHRASE
59        if not passphrase:
60            raise CommandError(
61                "Passphrase not defined.  Please set it with --passphrase or "
62                "by declaring it in your environment or your config."
63            )
64
65        if options["from"] == "gpg" and options["to"] == "unencrypted":
66            self.__gpg_to_unencrypted(passphrase)
67        elif options["from"] == "unencrypted" and options["to"] == "gpg":
68            self.__unencrypted_to_gpg(passphrase)
69
70    @staticmethod
71    def __gpg_to_unencrypted(passphrase):
72
73        encrypted_files = Document.objects.filter(
74            storage_type=Document.STORAGE_TYPE_GPG)
75
76        for document in encrypted_files:
77
78            print(coloured("Decrypting {}".format(document), "green"))
79
80            old_paths = [document.source_path, document.thumbnail_path]
81            raw_document = GnuPG.decrypted(document.source_file, passphrase)
82            raw_thumb = GnuPG.decrypted(document.thumbnail_file, passphrase)
83
84            document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
85
86            with open(document.source_path, "wb") as f:
87                f.write(raw_document)
88
89            with open(document.thumbnail_path, "wb") as f:
90                f.write(raw_thumb)
91
92            document.save(update_fields=("storage_type",))
93
94            for path in old_paths:
95                os.unlink(path)
96
97    @staticmethod
98    def __unencrypted_to_gpg(passphrase):
99
100        unencrypted_files = Document.objects.filter(
101            storage_type=Document.STORAGE_TYPE_UNENCRYPTED)
102
103        for document in unencrypted_files:
104
105            print(coloured("Encrypting {}".format(document), "green"))
106
107            old_paths = [document.source_path, document.thumbnail_path]
108            with open(document.source_path, "rb") as raw_document:
109                with open(document.thumbnail_path, "rb") as raw_thumb:
110                    document.storage_type = Document.STORAGE_TYPE_GPG
111                    with open(document.source_path, "wb") as f:
112                        f.write(GnuPG.encrypted(raw_document, passphrase))
113                    with open(document.thumbnail_path, "wb") as f:
114                        f.write(GnuPG.encrypted(raw_thumb, passphrase))
115
116            document.save(update_fields=("storage_type",))
117
118            for path in old_paths:
119                os.unlink(path)
120