summaryrefslogtreecommitdiff
path: root/examples/shred.py
blob: d445cee444b6256ce01b3d9639a7c19117eea2d4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# Examples: https://github.com/mate-desktop/python-caja/tree/master/examples
#
# This script adds a new menu's to Caja file browser:
# - Shred: when right-mouse clicking on one or more selected files
#   or directories.
# - Wipe freespace: when right-mouse clicking on a white area without
#   selected files or directories.
# The executed command is shown in the statusbar.
#
# WARNING: THIS IS AN EXAMPLE SCRIPT AND THE USER IS RESPONSIBLE
# FOR SHREDDING THE FULL SSD OR HARD DRIVE WHEN ABSOLUTE SECURITY IS
# REQUIRED!
#
# The Linux tool `shred` is used to shred files by filling files with
# zero's or random data and removes filenames. Overwriting files
# multiple times has no effect on SSD's and decreases lifetime. It
# should be used for mechanical hard drives only.
#
# Copy this script to a path defined in $XDG_DATA_DIRS, for example:
#   ~/.local/share/caja-python/extensions

import os
import subprocess

import gi
from gi.repository import Caja, GObject, Gio, Gtk


class ShredMenuProvider(GObject.GObject, Caja.MenuProvider):
    SHRED_ICON = '/usr/share/pixmaps/caja/erase.png'
    WIPE_CMD = 'dd if=/dev/{} of={} bs=1M'
    WIPE_FILENAME = 'tmp'

    def __init__(self):
        pass

    def shred_menu_activate_cb(self, menu, data):
        dialog = Gtk.MessageDialog(
            parent=None,
            flags=0,
            message_type=Gtk.MessageType.QUESTION,
            buttons=Gtk.ButtonsType.YES_NO,
            title="Caja shred",
            text="Are you sure you want to shred selected files?",
        )
        dialog.format_secondary_text("WARNING: This cannot be undone!")
        response = dialog.run()
        dialog.destroy()
        if response == Gtk.ResponseType.YES:
            files_skipped = False
            shred_files = ''
            for shred_file in data['files']:
                file_path = shred_file.get_location().get_path()
                if os.access(file_path, os.W_OK):
                    shred_files += ' "{}"'.format(file_path)
                else:
                    print('Skip shred file: {}'.format(file_path))
                    files_skipped = True

            # Start Shred command
            if shred_files:
                cmd = data['cmd'] + shred_files
                print('Running: {}'.format(cmd))
                subprocess.check_call(cmd, shell=True)

            dialog = Gtk.MessageDialog(
                parent=None,
                flags=0,
                message_type=Gtk.MessageType.INFO,
                buttons=Gtk.ButtonsType.OK,
                title="Caja shred",
                text='Shred completed.',
            )
            if files_skipped:
                dialog.format_secondary_text('WARNING: Some files could not be shredded.\nPlease check manually.')

            response = dialog.run()
            dialog.destroy()

    def wipe_freepsace_menu_activate_cb(self, menu, data):
        wipe_path = data['file'].get_location().get_path()
        wipe_cmd = data['cmd'].replace('of=', 'of={}/'.format(wipe_path))
        clean_cmd = 'rm {}/{}'.format(wipe_path, self.WIPE_FILENAME)

        # Check if wipe path is writable
        if not os.access(wipe_path, os.W_OK):
            dialog = Gtk.MessageDialog(
                parent=None,
                flags=0,
                message_type=Gtk.MessageType.ERROR,
                buttons=Gtk.ButtonsType.OK,
                title="Caja wipe freespace",
                text="Error: Directory is not writable",
            )
            response = dialog.run()
            dialog.destroy()
            return

        # Start wipe freespace command
        print('Running: {}'.format(wipe_cmd))
        try:
            subprocess.check_call(wipe_cmd, shell=True)
        except:
            # Ignore disk full error
            pass

        print('Running: sync')
        subprocess.check_call('sync', shell=True)

        print('Running: {}'.format(clean_cmd))
        subprocess.check_call(clean_cmd, shell=True)

        dialog = Gtk.MessageDialog(
            parent=None,
            flags=0,
            message_type=Gtk.MessageType.INFO,
            buttons=Gtk.ButtonsType.OK,
            title="Caja wipe freespace",
            text="Wipe freespace completed!",
        )
        response = dialog.run()
        dialog.destroy()

    def get_file_items(self, window, files):
        top_menuitem = Caja.MenuItem(name='ShredMenuProvider::Shred',
                                     label='Shred',
                                     tip='Delete files with the Linux "shred" tool',
                                     icon=self.SHRED_ICON)

        shred_submenu = Caja.Menu()
        top_menuitem.set_submenu(shred_submenu)

        # Shred fill zero's
        shred_cmd = 'shred -uz -n 0'
        shred_1x_zero_menuitem = Caja.MenuItem(name='ShredMenuProvider::Shred1xZero',
                                               label='Fill zero\'s',
                                               tip=shred_cmd + ' FILE... (NOT SECURE!)',
                                               icon=self.SHRED_ICON)
        shred_1x_zero_menuitem.connect('activate', self.shred_menu_activate_cb, {'cmd': shred_cmd, 'files': files})
        shred_submenu.append_item(shred_1x_zero_menuitem)

        # Shred fill random
        shred_cmd = 'shred -u -n 1'
        shred_1x_random_menuitem = Caja.MenuItem(name='ShredMenuProvider::Shred1xRandom',
                                                 label='Fill random',
                                                 tip=shred_cmd + ' FILE... (NOT SECURE!)',
                                                 icon=self.SHRED_ICON)
        shred_1x_random_menuitem.connect('activate', self.shred_menu_activate_cb, {'cmd': shred_cmd, 'files': files})
        shred_submenu.append_item(shred_1x_random_menuitem)

        # Shred 3x overwrite, last zero's
        shred_cmd = 'shred -uz'
        shred_3x_zero_menuitem = Caja.MenuItem(name='ShredMenuProvider::Shred3xLastZero',
                                               label='3x overwrite, last zero\'s',
                                               tip=shred_cmd + ' FILE... (HDD\'s only!)',
                                               icon=self.SHRED_ICON)
        shred_3x_zero_menuitem.connect('activate', self.shred_menu_activate_cb, {'cmd': shred_cmd, 'files': files})
        shred_submenu.append_item(shred_3x_zero_menuitem)

        # Shred 3x overwrite (default)
        shred_cmd = 'shred -u'
        shred_3x_random_menuitem = Caja.MenuItem(name='ShredMenuProvider::Shred3x',
                                                 label='3x overwrite (Default)',
                                                 tip=shred_cmd + ' FILE... (HDD\'s only!)',
                                                 icon=self.SHRED_ICON)
        shred_3x_random_menuitem.connect('activate', self.shred_menu_activate_cb, {'cmd': shred_cmd, 'files': files})
        shred_submenu.append_item(shred_3x_random_menuitem)

        return top_menuitem,

    def get_background_items(self, window, file):
        wipe_zero_cmd = self.WIPE_CMD.format('zero', self.WIPE_FILENAME)
        wipe_freespace_zero_menuitem = Caja.MenuItem(name='ShredMenuProvider::WipeFreespaceZero',
                                                     label='Wipe freespace zero\'s',
                                                     tip='Wipe freespace with command: "{}"'.format(wipe_zero_cmd),
                                                     icon=self.SHRED_ICON)
        wipe_freespace_zero_menuitem.connect('activate',
                                             self.wipe_freepsace_menu_activate_cb,
                                             {'cmd': wipe_zero_cmd, 'file': file})

        wipe_random_cmd = self.WIPE_CMD.format('random', self.WIPE_FILENAME)
        wipe_freespace_random_menuitem = Caja.MenuItem(name='ShredMenuProvider::WipeFreespaceRandom',
                                                       label='Wipe freespace random',
                                                       tip='Wipe freespace with command: "{}"'.format(wipe_random_cmd),
                                                       icon=self.SHRED_ICON)
        wipe_freespace_random_menuitem.connect('activate',
                                               self.wipe_freepsace_menu_activate_cb,
                                               {'cmd': wipe_random_cmd, 'file': file})

        return wipe_freespace_zero_menuitem, wipe_freespace_random_menuitem,