From 30a19f89ca4db25c3f2669283b39e837c4b4438d Mon Sep 17 00:00:00 2001
From: jvoisin
Date: Wed, 17 Aug 2011 18:26:57 +0200
Subject: Correct "binaries" names, and fix the setup.py according to the
changes
---
MANIFEST.in | 2 +-
mat-cli | 152 ++++++++++++++++
mat-cli.py | 152 ----------------
mat-gui | 571 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
mat-gui.py | 571 ------------------------------------------------------------
setup.py | 1 +
6 files changed, 725 insertions(+), 724 deletions(-)
create mode 100755 mat-cli
delete mode 100755 mat-cli.py
create mode 100755 mat-gui
delete mode 100755 mat-gui.py
diff --git a/MANIFEST.in b/MANIFEST.in
index 3cdc6b9..b525485 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,3 +1,3 @@
-include README cli.py gui.py
+include README mat-cli mat-gui
graft mat
recursive-include test *
diff --git a/mat-cli b/mat-cli
new file mode 100755
index 0000000..804c4cf
--- /dev/null
+++ b/mat-cli
@@ -0,0 +1,152 @@
+#!/usr/bin/python
+'''
+ Metadata anonymisation toolkit - CLI edition
+'''
+
+import sys
+import xml.sax
+import optparse
+
+import hachoir_core
+
+from mat import mat
+
+__version__ = '0.1'
+
+
+def parse():
+ '''
+ Get, and parse options passed to the program
+ '''
+ parser = optparse.OptionParser(usage='%prog [options] files')
+ parser.add_option('--add2archive', '-a', action='store_true',
+ default=False, help='Add to outputed archive non-supported filetypes')
+ parser.add_option('--backup', '-b', action='store_true', default=False,
+ help='Keep a backup copy')
+ parser.add_option('--check', '-c', action='store_true', default=False,
+ help='Check if a file is free of harmfull metadatas')
+ parser.add_option('--display', '-d', action='store_true', default=False,
+ help='List all the meta of a file without removing them')
+ parser.add_option('--force', '-f', action='store_true', default=False,
+ help='Don\'t check if files are clean before cleaning')
+ parser.add_option('--list', '-l', action='store_true', default=False,
+ help='List all supported fileformat')
+ parser.add_option('--ugly', '-u', action='store_true', default=False,
+ help='Remove harmful meta, but loss can occure')
+ parser.add_option('--version', '-v', action='callback',
+ callback=display_version, help='Display version and exit')
+
+ values, arguments = parser.parse_args()
+ if not arguments and values.list is False:
+ parser.print_help()
+ sys.exit(0)
+ return values, arguments
+
+
+def display_version(*_):
+ '''
+ Display the program's version, and exit
+ '''
+ print('Metadata Anonymisation Toolkit version %s') % mat.__version__
+ print('CLI version %s') % __version__
+ print('Hachoir version %s') % hachoir_core.__version__
+ sys.exit(0)
+
+
+def list_meta(class_file, filename, force):
+ '''
+ Print all the meta of 'filename' on stdout
+ '''
+ print('[+] File %s :' % filename)
+ if force is False and class_file.is_clean():
+ print('No harmful meta found')
+ else:
+ meta = class_file.get_meta()
+ if meta is None:
+ print('No harmful meta found')
+ else:
+ for key, value in class_file.get_meta().iteritems():
+ print(key + ' : ' + str(value))
+
+
+def is_clean(class_file, filename, force):
+ '''
+ Say if 'filename' is clean or not
+ '''
+ if class_file.is_clean():
+ print('[+] %s is clean' % filename)
+ else:
+ print('[+] %s is not clean' % filename)
+
+
+def clean_meta(class_file, filename, force):
+ '''
+ Clean the file 'filename'
+ '''
+ print('[+] Cleaning %s' % filename)
+ if force is False and class_file.is_clean():
+ print('%s is already clean' % filename)
+ else:
+ class_file.remove_all()
+ print('%s cleaned !' % filename)
+
+
+def clean_meta_ugly(class_file, filename, force):
+ '''
+ Clean the file 'filename', ugly way
+ '''
+ print('[+] Cleaning %s' % filename)
+ if force is False and class_file.is_clean():
+ print('%s is already clean' % filename)
+ else:
+ class_file.remove_all_ugly()
+ print('%s cleaned' % filename)
+
+
+def list_supported():
+ '''
+ Print all supported fileformat, and exit
+ '''
+ handler = mat.XMLParser()
+ parser = xml.sax.make_parser()
+ parser.setContentHandler(handler)
+ with open('FORMATS', 'r') as xmlfile:
+ parser.parse(xmlfile)
+
+ for item in handler.list:
+ print('%s (%s)' % (item['name'], item['extension']))
+ print('\tsupport : ' + item['support'])
+ print('\tmetadata : ' + item['metadata'])
+ print('\tmethod : ' + item['method'])
+ if item['support'] == 'partial':
+ print('\tremaining : ' + item['remaining'])
+ print('\n')
+ sys.exit(0)
+
+
+def main():
+ '''
+ main function : get args, and launch the appropriate function
+ '''
+ args, filenames = parse()
+
+ #func receive the function correponding to the options given as parameters
+ if args.display is True: # only print metadatas
+ func = list_meta
+ elif args.check is True: # only check if the file is clean
+ func = is_clean
+ elif args.ugly is True: # destructive anonymisation method
+ func = clean_meta_ugly
+ elif args.list is True: # print the list of all supported format
+ list_supported()
+ else: # clean the file
+ func = clean_meta
+
+ for filename in filenames:
+ class_file = mat.create_class_file(filename, args.backup,
+ args.add2archive)
+ if class_file is not None:
+ func(class_file, filename, args.force)
+
+if __name__ == '__main__':
+ main()
diff --git a/mat-cli.py b/mat-cli.py
deleted file mode 100755
index 804c4cf..0000000
--- a/mat-cli.py
+++ /dev/null
@@ -1,152 +0,0 @@
-#!/usr/bin/python
-'''
- Metadata anonymisation toolkit - CLI edition
-'''
-
-import sys
-import xml.sax
-import optparse
-
-import hachoir_core
-
-from mat import mat
-
-__version__ = '0.1'
-
-
-def parse():
- '''
- Get, and parse options passed to the program
- '''
- parser = optparse.OptionParser(usage='%prog [options] files')
- parser.add_option('--add2archive', '-a', action='store_true',
- default=False, help='Add to outputed archive non-supported filetypes')
- parser.add_option('--backup', '-b', action='store_true', default=False,
- help='Keep a backup copy')
- parser.add_option('--check', '-c', action='store_true', default=False,
- help='Check if a file is free of harmfull metadatas')
- parser.add_option('--display', '-d', action='store_true', default=False,
- help='List all the meta of a file without removing them')
- parser.add_option('--force', '-f', action='store_true', default=False,
- help='Don\'t check if files are clean before cleaning')
- parser.add_option('--list', '-l', action='store_true', default=False,
- help='List all supported fileformat')
- parser.add_option('--ugly', '-u', action='store_true', default=False,
- help='Remove harmful meta, but loss can occure')
- parser.add_option('--version', '-v', action='callback',
- callback=display_version, help='Display version and exit')
-
- values, arguments = parser.parse_args()
- if not arguments and values.list is False:
- parser.print_help()
- sys.exit(0)
- return values, arguments
-
-
-def display_version(*_):
- '''
- Display the program's version, and exit
- '''
- print('Metadata Anonymisation Toolkit version %s') % mat.__version__
- print('CLI version %s') % __version__
- print('Hachoir version %s') % hachoir_core.__version__
- sys.exit(0)
-
-
-def list_meta(class_file, filename, force):
- '''
- Print all the meta of 'filename' on stdout
- '''
- print('[+] File %s :' % filename)
- if force is False and class_file.is_clean():
- print('No harmful meta found')
- else:
- meta = class_file.get_meta()
- if meta is None:
- print('No harmful meta found')
- else:
- for key, value in class_file.get_meta().iteritems():
- print(key + ' : ' + str(value))
-
-
-def is_clean(class_file, filename, force):
- '''
- Say if 'filename' is clean or not
- '''
- if class_file.is_clean():
- print('[+] %s is clean' % filename)
- else:
- print('[+] %s is not clean' % filename)
-
-
-def clean_meta(class_file, filename, force):
- '''
- Clean the file 'filename'
- '''
- print('[+] Cleaning %s' % filename)
- if force is False and class_file.is_clean():
- print('%s is already clean' % filename)
- else:
- class_file.remove_all()
- print('%s cleaned !' % filename)
-
-
-def clean_meta_ugly(class_file, filename, force):
- '''
- Clean the file 'filename', ugly way
- '''
- print('[+] Cleaning %s' % filename)
- if force is False and class_file.is_clean():
- print('%s is already clean' % filename)
- else:
- class_file.remove_all_ugly()
- print('%s cleaned' % filename)
-
-
-def list_supported():
- '''
- Print all supported fileformat, and exit
- '''
- handler = mat.XMLParser()
- parser = xml.sax.make_parser()
- parser.setContentHandler(handler)
- with open('FORMATS', 'r') as xmlfile:
- parser.parse(xmlfile)
-
- for item in handler.list:
- print('%s (%s)' % (item['name'], item['extension']))
- print('\tsupport : ' + item['support'])
- print('\tmetadata : ' + item['metadata'])
- print('\tmethod : ' + item['method'])
- if item['support'] == 'partial':
- print('\tremaining : ' + item['remaining'])
- print('\n')
- sys.exit(0)
-
-
-def main():
- '''
- main function : get args, and launch the appropriate function
- '''
- args, filenames = parse()
-
- #func receive the function correponding to the options given as parameters
- if args.display is True: # only print metadatas
- func = list_meta
- elif args.check is True: # only check if the file is clean
- func = is_clean
- elif args.ugly is True: # destructive anonymisation method
- func = clean_meta_ugly
- elif args.list is True: # print the list of all supported format
- list_supported()
- else: # clean the file
- func = clean_meta
-
- for filename in filenames:
- class_file = mat.create_class_file(filename, args.backup,
- args.add2archive)
- if class_file is not None:
- func(class_file, filename, args.force)
-
-if __name__ == '__main__':
- main()
diff --git a/mat-gui b/mat-gui
new file mode 100755
index 0000000..07b8d6c
--- /dev/null
+++ b/mat-gui
@@ -0,0 +1,571 @@
+#!/usr/bin/env python
+#charset utf-8
+
+'''
+ Metadata anonymisation toolkit - GUI edition
+'''
+
+import gtk
+import gobject
+
+import gettext
+import locale
+import logging
+import os
+import xml.sax
+
+from mat import mat
+
+__version__ = '0.1'
+__author__ = 'jvoisin'
+
+
+
+logging.basicConfig(level=mat.LOGGING_LEVEL)
+
+
+class CFile(object):
+ '''
+ Contain the "parser" class of the file "filename"
+ This class exist just to be "around" my parser.Generic_parser class,
+ since the gtk.ListStore does not accept it.
+ '''
+ def __init__(self, filename, backup, add2archive):
+ try:
+ self.file = mat.create_class_file(filename, backup, add2archive)
+ except:
+ self.file = None
+
+
+class GUI:
+ '''
+ Main GUI class
+ '''
+ def __init__(self):
+ # Preferences
+ self.force = False
+ self.backup = True
+ self.add2archive = True
+
+ # Main window
+ self.window = gtk.Window()
+ self.window.set_title('Metadata Anonymisation Toolkit %s' %
+ __version__)
+ self.window.connect('destroy', gtk.main_quit)
+ self.window.set_default_size(800, 600)
+
+ vbox = gtk.VBox()
+ self.window.add(vbox)
+
+ menubar = self.create_menu()
+ toolbar = self.create_toolbar()
+ content = gtk.ScrolledWindow()
+ vbox.pack_start(menubar, False, True, 0)
+ vbox.pack_start(toolbar, False, True, 0)
+ vbox.pack_start(content, True, True, 0)
+
+ # parser.class - name - type - cleaned
+ self.liststore = gtk.ListStore(object, str, str, str)
+
+ treeview = gtk.TreeView(model=self.liststore)
+ treeview.set_search_column(1) # name column is searchable
+ treeview.set_rules_hint(True) # alternate colors for rows
+ treeview.set_rubber_banding(True) # mouse selection
+ self.add_columns(treeview)
+ self.selection = treeview.get_selection()
+ self.selection.set_mode(gtk.SELECTION_MULTIPLE)
+
+ content.add(treeview)
+
+ self.statusbar = gtk.Statusbar()
+ self.statusbar.push(1, _('Ready'))
+ vbox.pack_start(self.statusbar, False, False, 0)
+
+ self.window.show_all()
+
+ def create_toolbar(self):
+ '''
+ Returns a vbox object, which contains a toolbar with buttons
+ '''
+ toolbar = gtk.Toolbar()
+
+ toolbutton = gtk.ToolButton(gtk.STOCK_ADD)
+ toolbutton.set_label(_('Add'))
+ toolbutton.connect('clicked', self.add_files)
+ toolbutton.set_tooltip_text(_('Add files'))
+ toolbar.add(toolbutton)
+
+ toolbutton = gtk.ToolButton(gtk.STOCK_PRINT_REPORT)
+ toolbutton.set_label(_('Clean'))
+ toolbutton.connect('clicked', self.process_files, self.mat_clean)
+ toolbutton.set_tooltip_text(_('Clean selected files without data loss'))
+ toolbar.add(toolbutton)
+
+ toolbutton = gtk.ToolButton(gtk.STOCK_PRINT_WARNING)
+ toolbutton.set_label(_('Brute Clean'))
+ toolbutton.connect('clicked', self.mat_clean_dirty)
+ toolbutton.set_tooltip_text(_('Clean selected files with possible \
+data loss'))
+ toolbar.add(toolbutton)
+
+ toolbutton = gtk.ToolButton(gtk.STOCK_FIND)
+ toolbutton.set_label(_('Check'))
+ toolbutton.connect('clicked', self.process_files, self.mat_check)
+ toolbutton.set_tooltip_text(_('Check selected files for harmful meta'))
+ toolbar.add(toolbutton)
+
+ toolbutton = gtk.ToolButton(stock_id=gtk.STOCK_QUIT)
+ toolbutton.set_label(_('Quit'))
+ toolbutton.connect('clicked', gtk.main_quit)
+ toolbar.add(toolbutton)
+
+ vbox = gtk.VBox(spacing=3)
+ vbox.pack_start(toolbar, False, False, 0)
+ return vbox
+
+ def add_columns(self, treeview):
+ '''
+ Create the columns, and add them to the treeview
+ '''
+ colname = [_('Filename'), _('Mimetype'), _('State')]
+
+ for i, j in enumerate(colname):
+ filename_column = gtk.CellRendererText()
+ column = gtk.TreeViewColumn(j, filename_column, text=i + 1)
+ column.set_sort_column_id(i + 1)
+ if i is 0: # place tooltip on this column
+ tips = TreeViewTooltips(column)
+ tips.add_view(treeview)
+ treeview.append_column(column)
+
+ def create_menu_item(self, name, func, menu, pix):
+ '''
+ Create a MenuItem() like Preferences, Quit, Add, Clean, ...
+ '''
+ item = gtk.ImageMenuItem()
+ picture = gtk.Image()
+ picture.set_from_stock(pix, gtk.ICON_SIZE_MENU)
+ item.set_image(picture)
+ item.set_label(name)
+ item.connect('activate', func)
+ menu.append(item)
+
+ def create_sub_menu(self, name, menubar):
+ '''
+ Create a submenu like File, Edit, Clean, ...
+ '''
+ submenu = gtk.Menu()
+ menuitem = gtk.MenuItem()
+ menuitem.set_submenu(submenu)
+ menuitem.set_label(name)
+ menubar.append(menuitem)
+ return submenu
+
+ def create_menu(self):
+ '''
+ Return a MenuBar
+ '''
+ menubar = gtk.MenuBar()
+
+ file_menu = self.create_sub_menu(_('Files'), menubar)
+ self.create_menu_item(_('Add files'), self.add_files, file_menu,
+ gtk.STOCK_ADD)
+ self.create_menu_item(_('Quit'), gtk.main_quit, file_menu,
+ gtk.STOCK_QUIT)
+
+ edit_menu = self.create_sub_menu(_('Edit'), menubar)
+ self.create_menu_item(_('Clear the filelist'),
+ lambda x: self.liststore.clear(), edit_menu, gtk.STOCK_REMOVE)
+ self.create_menu_item(_('Preferences'), self.preferences, edit_menu,
+ gtk.STOCK_PREFERENCES)
+
+ process_menu = self.create_sub_menu(_('Process'), menubar)
+ item = gtk.ImageMenuItem()
+ picture = gtk.Image()
+ picture.set_from_stock(gtk.STOCK_PRINT_REPORT, gtk.ICON_SIZE_MENU)
+ item.set_image(picture)
+ item.set_label(_('Clean'))
+ item.connect('activate', self.process_files, self.mat_clean)
+ process_menu.append(item)
+
+ item = gtk.ImageMenuItem()
+ picture = gtk.Image()
+ picture.set_from_stock(gtk.STOCK_PRINT_WARNING, gtk.ICON_SIZE_MENU)
+ item.set_image(picture)
+ item.set_label(_('Clean (lossy way)'))
+ item.connect('activate', self.process_files, self.mat_clean_dirty)
+ process_menu.append(item)
+
+ item = gtk.ImageMenuItem()
+ picture = gtk.Image()
+ picture.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
+ item.set_image(picture)
+ item.set_label(_('Check'))
+ item.connect('activate', self.process_files, self.mat_check)
+ process_menu.append(item)
+
+
+ help_menu = self.create_sub_menu(_('Help'), menubar)
+ self.create_menu_item(_('Supported formats'), self.supported, help_menu,
+ gtk.STOCK_INFO)
+ self.create_menu_item(_('About'), self.about, help_menu, gtk.STOCK_ABOUT)
+
+ return menubar
+
+ def add_files(self, button):
+ '''
+ Add the files chosed by the filechoser ("Add" button)
+ '''
+ chooser = gtk.FileChooserDialog(title=_('Choose files'),
+ parent=self.window, action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(gtk.STOCK_OK, 0, gtk.STOCK_CANCEL, 1))
+ chooser.set_default_response(0)
+ chooser.set_select_multiple(True)
+
+ all_filter = gtk.FileFilter() # filter that shows all files
+ all_filter.set_name(_('All files'))
+ all_filter.add_pattern('*')
+ chooser.add_filter(all_filter)
+
+ supported_filter = gtk.FileFilter()
+ # filter that shows only supported formats
+ [supported_filter.add_mime_type(i) for i in mat.STRIPPERS.keys()]
+ supported_filter.set_name(_('Supported files'))
+ chooser.add_filter(supported_filter)
+
+ response = chooser.run()
+
+ if response is 0: # gtk.STOCK_OK
+ filenames = chooser.get_filenames()
+ task = self.populate(filenames)
+ gobject.idle_add(task.next) # asynchrone processing
+ chooser.destroy()
+
+ def populate(self, filenames):
+ '''
+ Append selected files by add_file to the self.liststore
+ '''
+ for filename in filenames: # filenames : all selected files/folders
+ if os.path.isdir(filename): # if "filename" is a directory
+ for root, dirs, files in os.walk(filename):
+ for item in files:
+ path_to_file = os.path.join(root, item)
+ self.add_file_to_treeview(path_to_file)
+ else: # filename is a regular file
+ self.add_file_to_treeview(filename)
+ yield True
+ yield False
+
+ def add_file_to_treeview(self, filename):
+ '''
+ Add a file to the list if his format is supported
+ '''
+ cf = CFile(filename, self.backup, self.add2archive)
+ if cf.file is not None:
+ self.liststore.append([cf, cf.file.basename,
+ cf.file.mime, _('unknow')])
+
+
+ def about(self, button):
+ '''
+ About popup
+ '''
+ w = gtk.AboutDialog()
+ w.set_version(__version__)
+ w.set_copyright('GNU Public License v2')
+ w.set_comments(_('This software was coded during the GSoC 2011'))
+ w.set_website('https://gitweb.torproject.org/user/jvoisin/mat.git')
+ w.set_website_label(_('Website'))
+ w.set_authors(['Julien (jvoisin) Voisin', ])
+ w.set_program_name('Metadata Anonymistion Toolkit')
+ click = w.run()
+ if click:
+ w.destroy()
+
+ def supported(self, button):
+ '''
+ List the supported formats
+ '''
+ dialog = gtk.Dialog(_('Supported formats'), self.window, 0,
+ (gtk.STOCK_CLOSE, 0))
+ vbox = gtk.VBox(spacing=5)
+ dialog.get_content_area().pack_start(vbox, True, True, 0)
+
+ label = gtk.Label()
+ label.set_markup('Supported fileformats')
+ vbox.pack_start(label, True, True, 0)
+
+ #parsing xml
+ handler = mat.XMLParser()
+ parser = xml.sax.make_parser()
+ parser.setContentHandler(handler)
+ with open('FORMATS', 'r') as xmlfile:
+ parser.parse(xmlfile)
+
+ for item in handler.list: # list of dict : one dict per format
+ #create one expander per format
+ title = '%s (%s)' % (item['name'], item['extension'])
+ support = ('\t%s : %s' % ('support', item['support']))
+ metadata = '\n\tmetadata : ' + item['metadata']
+ method = '\n\tmethod : ' + item['method']
+ content = support + metadata + method
+ if item['support'] == 'partial':
+ content += '\n\tremaining : ' + item['remaining']
+
+ expander = gtk.Expander(title)
+ vbox.pack_start(expander, False, False, 0)
+ label = gtk.Label()
+ label.set_markup(content)
+ expander.add(label)
+
+ dialog.show_all()
+ click = dialog.run()
+ if click is 0: # Close
+ dialog.destroy()
+
+ def preferences(self, button):
+ '''
+ Preferences popup
+ '''
+ dialog = gtk.Dialog(_('Preferences'), self.window, 0, (gtk.STOCK_OK, 0))
+ hbox = gtk.HBox()
+ dialog.get_content_area().pack_start(hbox, False, False, 0)
+
+ icon = gtk.Image()
+ icon.set_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_DIALOG)
+ hbox.pack_start(icon, False, False, 0)
+
+ table = gtk.Table(3, 2, False) # nb rows, nb lines
+ hbox.pack_start(table, True, True, 0)
+
+ force = gtk.CheckButton(_('Force Clean'), False)
+ force.set_active(self.force)
+ force.connect('toggled', self.invert, 'force')
+ force.set_tooltip_text(_('Do not check if already clean before \
+cleaning'))
+ table.attach(force, 0, 1, 0, 1)
+
+ backup = gtk.CheckButton(_('Backup'), False)
+ backup.set_active(self.backup)
+ backup.connect('toggled', self.invert, 'backup')
+ backup.set_tooltip_text(_('Keep a backup copy'))
+ table.attach(backup, 0, 1, 1, 2)
+
+ add2archive = gtk.CheckButton(_('Add unsupported file to archives'),
+ False)
+ add2archive.set_active(self.add2archive)
+ add2archive.connect('toggled', self.invert, 'add2archive')
+ add2archive.set_tooltip_text(_('Add non-supported (and so \
+non-anonymised) file to outputed archive'))
+ table.attach(add2archive, 0, 1, 2, 3)
+
+ hbox.show_all()
+ response = dialog.run()
+ if response is 0: # gtk.STOCK_OK
+ dialog.destroy()
+
+ def invert(self, button, name):
+ '''
+ Invert a preference state
+ '''
+ if name == 'force':
+ self.force = not self.force
+ elif name == 'backup':
+ self.backup = not self.backup
+ elif name == 'add2archive':
+ self.add2archive = not self.add2archive
+
+ def process_files(self, button, func):
+ '''
+ Launch the function "function" in a asynchrone way
+ '''
+ iterator = self.selection.get_selected_rows()[1]
+ if not iterator: # if nothing is selected : select everything
+ iterator = xrange(len(self.liststore))
+ task = func(iterator) # launch func() in an asynchrone way
+ gobject.idle_add(task.next)
+
+ def mat_check(self, iterator):
+ '''
+ Check if selected elements are clean
+ '''
+ for line in iterator: # for each file in selection
+ if self.liststore[line][0].file.is_clean():
+ string = _('clean')
+ else:
+ string = _('dirty')
+ logging.info('%s is %s' % (self.liststore[line][1], string))
+ self.liststore[line][3] = string
+ yield True
+ yield False
+
+ def mat_clean(self, iterator):
+ '''
+ Clean selected elements
+ '''
+ for line in iterator: # for each file in selection
+ logging.info('Cleaning %s' % self.liststore[line][1])
+ if self.liststore[line][3] is not _('clean'):
+ if self.force or not self.liststore[line][0].file.is_clean():
+ self.liststore[line][0].file.remove_all()
+ self.liststore[line][3] = _('clean')
+ yield True
+ yield False
+
+ def mat_clean_dirty(self, iterator):
+ '''
+ Clean selected elements (ugly way)
+ '''
+ for line in iterator: # for each file in selection
+ logging.info('Cleaning (lossy way) %s' % self.liststore[line][1])
+ if self.liststore[line][3] is not _('clean'):
+ if self.force or not self.liststore[line][0].file.is_clean():
+ self.liststore[line][0].file.remove_all_ugly()
+ self.liststore[line][3] = _('clean')
+ yield True
+ yield False
+
+
+class TreeViewTooltips(object):
+ '''
+ A dirty hack losely based on treeviewtooltip from Daniel J. Popowich
+ (dpopowich AT astro dot umass dot edu), to display differents tooltips
+ for each cell of the first row of the GUI.
+ '''
+ # Copyright (c) 2006, Daniel J. Popowich
+ #
+ # Permission is hereby granted, free of charge, to any person
+ # obtaining a copy of this software and associated documentation files
+ # (the "Software"), to deal in the Software without restriction,
+ # including without limitation the rights to use, copy, modify, merge,
+ # publish, distribute, sublicense, and/or sell copies of the Software,
+ # and to permit persons to whom the Software is furnished to do so,
+ # subject to the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be
+ # included in all copies or substantial portions of the Software.
+ def __init__(self, namecol):
+ '''
+ Initialize the tooltip.
+ window: the popup window that holds the tooltip text, an
+ instance of gtk.Window.
+ label: a gtk.Label that is packed into the window. The
+ tooltip text is set in the label with the
+ set_label() method, so the text can be plain or
+ markup text.
+ '''
+ # create the window
+ self.window = window = gtk.Window(gtk.WINDOW_POPUP)
+ window.set_name('gtk-tooltips')
+ window.set_resizable(False)
+ window.set_border_width(4)
+ window.set_app_paintable(True)
+ window.connect("expose-event", self.__on_expose_event)
+
+ # create the label
+ self.label = label = gtk.Label()
+ label.set_line_wrap(True)
+ label.set_alignment(0.5, 0.5)
+ label.show()
+ window.add(label)
+
+ self.namecol = namecol
+ self.__save = None # saves the current cell
+ self.__next = None # the timer id for the next tooltip to be shown
+ self.__shown = False # flag on whether the tooltip window is shown
+
+ def __show(self, tooltip, x, y):
+ '''
+ show the tooltip
+ '''
+ self.label.set_label(tooltip) # set label
+ self.window.move(x, y) # move the window
+ self.window.show() # show it
+ self.__shown = True
+
+ def __hide(self):
+ '''
+ hide the tooltip
+ '''
+ self.__queue_next()
+ self.window.hide()
+ self.__shown = False
+
+ def __motion_handler(self, view, event):
+ '''
+ as the pointer moves across the view, show a tooltip
+ '''
+ path_at_pos = view.get_path_at_pos(int(event.x), int(event.y))
+ if path_at_pos:
+ path, col, x, y = path_at_pos
+ tooltip = self.get_tooltip(view, col, path)
+ if tooltip:
+ tooltip = str(tooltip).strip()
+ self.__queue_next((path, col), tooltip, int(event.x_root),
+ int(event.y_root))
+ return
+ self.__hide()
+
+ def __queue_next(self, *args):
+ '''
+ queue next request to show a tooltip
+ if args is non-empty it means a request was made to show a
+ tooltip. if empty, no request is being made, but any
+ pending requests should be cancelled anyway
+ '''
+ cell = None
+
+ if args: # if called with args, break them out
+ cell, tooltip, x, y = args
+
+ # if it's the same cell as previously shown, just return
+ if self.__save == cell:
+ return
+
+ if self.__next: # if we have something queued up, cancel it
+ gobject.source_remove(self.__next)
+ self.__next = None
+
+ if cell: # if there was a request
+ if self.__shown: # if tooltip is already shown show the new one
+ self.__show(tooltip, x, y)
+ else: # else queue it up in 1/2 second
+ self.__next = gobject.timeout_add(500, self.__show,
+ tooltip, x, y)
+ self.__save = cell # save this cell
+
+ def __on_expose_event(self, window, event):
+ '''
+ this magic is required so the window appears with a 1-pixel
+ black border (default gtk Style).
+ '''
+ w, h = window.size_request()
+ window.style.paint_flat_box(window.window, gtk.STATE_NORMAL,
+ gtk.SHADOW_OUT, None, window, 'tooltip', 0, 0, w, h)
+
+ def add_view(self, view):
+ '''
+ add a gtk.TreeView to the tooltip
+ '''
+ view.connect('motion-notify-event', self.__motion_handler)
+ view.connect('leave-notify-event', lambda i, j: self.__hide())
+
+ def get_tooltip(self, view, column, path):
+ '''
+ See the module doc string for a description of this method
+ '''
+ if column is self.namecol:
+ model = view.get_model()
+ name = model[path][0]
+ return name.file.filename
+
+
+if __name__ == '__main__':
+ #Translations
+ t = gettext.translation('gui', 'locale', fallback=True)
+ _ = t.ugettext
+ t.install()
+
+ #Main
+ GUI()
+ gtk.main()
diff --git a/mat-gui.py b/mat-gui.py
deleted file mode 100755
index 07b8d6c..0000000
--- a/mat-gui.py
+++ /dev/null
@@ -1,571 +0,0 @@
-#!/usr/bin/env python
-#charset utf-8
-
-'''
- Metadata anonymisation toolkit - GUI edition
-'''
-
-import gtk
-import gobject
-
-import gettext
-import locale
-import logging
-import os
-import xml.sax
-
-from mat import mat
-
-__version__ = '0.1'
-__author__ = 'jvoisin'
-
-
-
-logging.basicConfig(level=mat.LOGGING_LEVEL)
-
-
-class CFile(object):
- '''
- Contain the "parser" class of the file "filename"
- This class exist just to be "around" my parser.Generic_parser class,
- since the gtk.ListStore does not accept it.
- '''
- def __init__(self, filename, backup, add2archive):
- try:
- self.file = mat.create_class_file(filename, backup, add2archive)
- except:
- self.file = None
-
-
-class GUI:
- '''
- Main GUI class
- '''
- def __init__(self):
- # Preferences
- self.force = False
- self.backup = True
- self.add2archive = True
-
- # Main window
- self.window = gtk.Window()
- self.window.set_title('Metadata Anonymisation Toolkit %s' %
- __version__)
- self.window.connect('destroy', gtk.main_quit)
- self.window.set_default_size(800, 600)
-
- vbox = gtk.VBox()
- self.window.add(vbox)
-
- menubar = self.create_menu()
- toolbar = self.create_toolbar()
- content = gtk.ScrolledWindow()
- vbox.pack_start(menubar, False, True, 0)
- vbox.pack_start(toolbar, False, True, 0)
- vbox.pack_start(content, True, True, 0)
-
- # parser.class - name - type - cleaned
- self.liststore = gtk.ListStore(object, str, str, str)
-
- treeview = gtk.TreeView(model=self.liststore)
- treeview.set_search_column(1) # name column is searchable
- treeview.set_rules_hint(True) # alternate colors for rows
- treeview.set_rubber_banding(True) # mouse selection
- self.add_columns(treeview)
- self.selection = treeview.get_selection()
- self.selection.set_mode(gtk.SELECTION_MULTIPLE)
-
- content.add(treeview)
-
- self.statusbar = gtk.Statusbar()
- self.statusbar.push(1, _('Ready'))
- vbox.pack_start(self.statusbar, False, False, 0)
-
- self.window.show_all()
-
- def create_toolbar(self):
- '''
- Returns a vbox object, which contains a toolbar with buttons
- '''
- toolbar = gtk.Toolbar()
-
- toolbutton = gtk.ToolButton(gtk.STOCK_ADD)
- toolbutton.set_label(_('Add'))
- toolbutton.connect('clicked', self.add_files)
- toolbutton.set_tooltip_text(_('Add files'))
- toolbar.add(toolbutton)
-
- toolbutton = gtk.ToolButton(gtk.STOCK_PRINT_REPORT)
- toolbutton.set_label(_('Clean'))
- toolbutton.connect('clicked', self.process_files, self.mat_clean)
- toolbutton.set_tooltip_text(_('Clean selected files without data loss'))
- toolbar.add(toolbutton)
-
- toolbutton = gtk.ToolButton(gtk.STOCK_PRINT_WARNING)
- toolbutton.set_label(_('Brute Clean'))
- toolbutton.connect('clicked', self.mat_clean_dirty)
- toolbutton.set_tooltip_text(_('Clean selected files with possible \
-data loss'))
- toolbar.add(toolbutton)
-
- toolbutton = gtk.ToolButton(gtk.STOCK_FIND)
- toolbutton.set_label(_('Check'))
- toolbutton.connect('clicked', self.process_files, self.mat_check)
- toolbutton.set_tooltip_text(_('Check selected files for harmful meta'))
- toolbar.add(toolbutton)
-
- toolbutton = gtk.ToolButton(stock_id=gtk.STOCK_QUIT)
- toolbutton.set_label(_('Quit'))
- toolbutton.connect('clicked', gtk.main_quit)
- toolbar.add(toolbutton)
-
- vbox = gtk.VBox(spacing=3)
- vbox.pack_start(toolbar, False, False, 0)
- return vbox
-
- def add_columns(self, treeview):
- '''
- Create the columns, and add them to the treeview
- '''
- colname = [_('Filename'), _('Mimetype'), _('State')]
-
- for i, j in enumerate(colname):
- filename_column = gtk.CellRendererText()
- column = gtk.TreeViewColumn(j, filename_column, text=i + 1)
- column.set_sort_column_id(i + 1)
- if i is 0: # place tooltip on this column
- tips = TreeViewTooltips(column)
- tips.add_view(treeview)
- treeview.append_column(column)
-
- def create_menu_item(self, name, func, menu, pix):
- '''
- Create a MenuItem() like Preferences, Quit, Add, Clean, ...
- '''
- item = gtk.ImageMenuItem()
- picture = gtk.Image()
- picture.set_from_stock(pix, gtk.ICON_SIZE_MENU)
- item.set_image(picture)
- item.set_label(name)
- item.connect('activate', func)
- menu.append(item)
-
- def create_sub_menu(self, name, menubar):
- '''
- Create a submenu like File, Edit, Clean, ...
- '''
- submenu = gtk.Menu()
- menuitem = gtk.MenuItem()
- menuitem.set_submenu(submenu)
- menuitem.set_label(name)
- menubar.append(menuitem)
- return submenu
-
- def create_menu(self):
- '''
- Return a MenuBar
- '''
- menubar = gtk.MenuBar()
-
- file_menu = self.create_sub_menu(_('Files'), menubar)
- self.create_menu_item(_('Add files'), self.add_files, file_menu,
- gtk.STOCK_ADD)
- self.create_menu_item(_('Quit'), gtk.main_quit, file_menu,
- gtk.STOCK_QUIT)
-
- edit_menu = self.create_sub_menu(_('Edit'), menubar)
- self.create_menu_item(_('Clear the filelist'),
- lambda x: self.liststore.clear(), edit_menu, gtk.STOCK_REMOVE)
- self.create_menu_item(_('Preferences'), self.preferences, edit_menu,
- gtk.STOCK_PREFERENCES)
-
- process_menu = self.create_sub_menu(_('Process'), menubar)
- item = gtk.ImageMenuItem()
- picture = gtk.Image()
- picture.set_from_stock(gtk.STOCK_PRINT_REPORT, gtk.ICON_SIZE_MENU)
- item.set_image(picture)
- item.set_label(_('Clean'))
- item.connect('activate', self.process_files, self.mat_clean)
- process_menu.append(item)
-
- item = gtk.ImageMenuItem()
- picture = gtk.Image()
- picture.set_from_stock(gtk.STOCK_PRINT_WARNING, gtk.ICON_SIZE_MENU)
- item.set_image(picture)
- item.set_label(_('Clean (lossy way)'))
- item.connect('activate', self.process_files, self.mat_clean_dirty)
- process_menu.append(item)
-
- item = gtk.ImageMenuItem()
- picture = gtk.Image()
- picture.set_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU)
- item.set_image(picture)
- item.set_label(_('Check'))
- item.connect('activate', self.process_files, self.mat_check)
- process_menu.append(item)
-
-
- help_menu = self.create_sub_menu(_('Help'), menubar)
- self.create_menu_item(_('Supported formats'), self.supported, help_menu,
- gtk.STOCK_INFO)
- self.create_menu_item(_('About'), self.about, help_menu, gtk.STOCK_ABOUT)
-
- return menubar
-
- def add_files(self, button):
- '''
- Add the files chosed by the filechoser ("Add" button)
- '''
- chooser = gtk.FileChooserDialog(title=_('Choose files'),
- parent=self.window, action=gtk.FILE_CHOOSER_ACTION_OPEN,
- buttons=(gtk.STOCK_OK, 0, gtk.STOCK_CANCEL, 1))
- chooser.set_default_response(0)
- chooser.set_select_multiple(True)
-
- all_filter = gtk.FileFilter() # filter that shows all files
- all_filter.set_name(_('All files'))
- all_filter.add_pattern('*')
- chooser.add_filter(all_filter)
-
- supported_filter = gtk.FileFilter()
- # filter that shows only supported formats
- [supported_filter.add_mime_type(i) for i in mat.STRIPPERS.keys()]
- supported_filter.set_name(_('Supported files'))
- chooser.add_filter(supported_filter)
-
- response = chooser.run()
-
- if response is 0: # gtk.STOCK_OK
- filenames = chooser.get_filenames()
- task = self.populate(filenames)
- gobject.idle_add(task.next) # asynchrone processing
- chooser.destroy()
-
- def populate(self, filenames):
- '''
- Append selected files by add_file to the self.liststore
- '''
- for filename in filenames: # filenames : all selected files/folders
- if os.path.isdir(filename): # if "filename" is a directory
- for root, dirs, files in os.walk(filename):
- for item in files:
- path_to_file = os.path.join(root, item)
- self.add_file_to_treeview(path_to_file)
- else: # filename is a regular file
- self.add_file_to_treeview(filename)
- yield True
- yield False
-
- def add_file_to_treeview(self, filename):
- '''
- Add a file to the list if his format is supported
- '''
- cf = CFile(filename, self.backup, self.add2archive)
- if cf.file is not None:
- self.liststore.append([cf, cf.file.basename,
- cf.file.mime, _('unknow')])
-
-
- def about(self, button):
- '''
- About popup
- '''
- w = gtk.AboutDialog()
- w.set_version(__version__)
- w.set_copyright('GNU Public License v2')
- w.set_comments(_('This software was coded during the GSoC 2011'))
- w.set_website('https://gitweb.torproject.org/user/jvoisin/mat.git')
- w.set_website_label(_('Website'))
- w.set_authors(['Julien (jvoisin) Voisin', ])
- w.set_program_name('Metadata Anonymistion Toolkit')
- click = w.run()
- if click:
- w.destroy()
-
- def supported(self, button):
- '''
- List the supported formats
- '''
- dialog = gtk.Dialog(_('Supported formats'), self.window, 0,
- (gtk.STOCK_CLOSE, 0))
- vbox = gtk.VBox(spacing=5)
- dialog.get_content_area().pack_start(vbox, True, True, 0)
-
- label = gtk.Label()
- label.set_markup('Supported fileformats')
- vbox.pack_start(label, True, True, 0)
-
- #parsing xml
- handler = mat.XMLParser()
- parser = xml.sax.make_parser()
- parser.setContentHandler(handler)
- with open('FORMATS', 'r') as xmlfile:
- parser.parse(xmlfile)
-
- for item in handler.list: # list of dict : one dict per format
- #create one expander per format
- title = '%s (%s)' % (item['name'], item['extension'])
- support = ('\t%s : %s' % ('support', item['support']))
- metadata = '\n\tmetadata : ' + item['metadata']
- method = '\n\tmethod : ' + item['method']
- content = support + metadata + method
- if item['support'] == 'partial':
- content += '\n\tremaining : ' + item['remaining']
-
- expander = gtk.Expander(title)
- vbox.pack_start(expander, False, False, 0)
- label = gtk.Label()
- label.set_markup(content)
- expander.add(label)
-
- dialog.show_all()
- click = dialog.run()
- if click is 0: # Close
- dialog.destroy()
-
- def preferences(self, button):
- '''
- Preferences popup
- '''
- dialog = gtk.Dialog(_('Preferences'), self.window, 0, (gtk.STOCK_OK, 0))
- hbox = gtk.HBox()
- dialog.get_content_area().pack_start(hbox, False, False, 0)
-
- icon = gtk.Image()
- icon.set_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_DIALOG)
- hbox.pack_start(icon, False, False, 0)
-
- table = gtk.Table(3, 2, False) # nb rows, nb lines
- hbox.pack_start(table, True, True, 0)
-
- force = gtk.CheckButton(_('Force Clean'), False)
- force.set_active(self.force)
- force.connect('toggled', self.invert, 'force')
- force.set_tooltip_text(_('Do not check if already clean before \
-cleaning'))
- table.attach(force, 0, 1, 0, 1)
-
- backup = gtk.CheckButton(_('Backup'), False)
- backup.set_active(self.backup)
- backup.connect('toggled', self.invert, 'backup')
- backup.set_tooltip_text(_('Keep a backup copy'))
- table.attach(backup, 0, 1, 1, 2)
-
- add2archive = gtk.CheckButton(_('Add unsupported file to archives'),
- False)
- add2archive.set_active(self.add2archive)
- add2archive.connect('toggled', self.invert, 'add2archive')
- add2archive.set_tooltip_text(_('Add non-supported (and so \
-non-anonymised) file to outputed archive'))
- table.attach(add2archive, 0, 1, 2, 3)
-
- hbox.show_all()
- response = dialog.run()
- if response is 0: # gtk.STOCK_OK
- dialog.destroy()
-
- def invert(self, button, name):
- '''
- Invert a preference state
- '''
- if name == 'force':
- self.force = not self.force
- elif name == 'backup':
- self.backup = not self.backup
- elif name == 'add2archive':
- self.add2archive = not self.add2archive
-
- def process_files(self, button, func):
- '''
- Launch the function "function" in a asynchrone way
- '''
- iterator = self.selection.get_selected_rows()[1]
- if not iterator: # if nothing is selected : select everything
- iterator = xrange(len(self.liststore))
- task = func(iterator) # launch func() in an asynchrone way
- gobject.idle_add(task.next)
-
- def mat_check(self, iterator):
- '''
- Check if selected elements are clean
- '''
- for line in iterator: # for each file in selection
- if self.liststore[line][0].file.is_clean():
- string = _('clean')
- else:
- string = _('dirty')
- logging.info('%s is %s' % (self.liststore[line][1], string))
- self.liststore[line][3] = string
- yield True
- yield False
-
- def mat_clean(self, iterator):
- '''
- Clean selected elements
- '''
- for line in iterator: # for each file in selection
- logging.info('Cleaning %s' % self.liststore[line][1])
- if self.liststore[line][3] is not _('clean'):
- if self.force or not self.liststore[line][0].file.is_clean():
- self.liststore[line][0].file.remove_all()
- self.liststore[line][3] = _('clean')
- yield True
- yield False
-
- def mat_clean_dirty(self, iterator):
- '''
- Clean selected elements (ugly way)
- '''
- for line in iterator: # for each file in selection
- logging.info('Cleaning (lossy way) %s' % self.liststore[line][1])
- if self.liststore[line][3] is not _('clean'):
- if self.force or not self.liststore[line][0].file.is_clean():
- self.liststore[line][0].file.remove_all_ugly()
- self.liststore[line][3] = _('clean')
- yield True
- yield False
-
-
-class TreeViewTooltips(object):
- '''
- A dirty hack losely based on treeviewtooltip from Daniel J. Popowich
- (dpopowich AT astro dot umass dot edu), to display differents tooltips
- for each cell of the first row of the GUI.
- '''
- # Copyright (c) 2006, Daniel J. Popowich
- #
- # Permission is hereby granted, free of charge, to any person
- # obtaining a copy of this software and associated documentation files
- # (the "Software"), to deal in the Software without restriction,
- # including without limitation the rights to use, copy, modify, merge,
- # publish, distribute, sublicense, and/or sell copies of the Software,
- # and to permit persons to whom the Software is furnished to do so,
- # subject to the following conditions:
- #
- # The above copyright notice and this permission notice shall be
- # included in all copies or substantial portions of the Software.
- def __init__(self, namecol):
- '''
- Initialize the tooltip.
- window: the popup window that holds the tooltip text, an
- instance of gtk.Window.
- label: a gtk.Label that is packed into the window. The
- tooltip text is set in the label with the
- set_label() method, so the text can be plain or
- markup text.
- '''
- # create the window
- self.window = window = gtk.Window(gtk.WINDOW_POPUP)
- window.set_name('gtk-tooltips')
- window.set_resizable(False)
- window.set_border_width(4)
- window.set_app_paintable(True)
- window.connect("expose-event", self.__on_expose_event)
-
- # create the label
- self.label = label = gtk.Label()
- label.set_line_wrap(True)
- label.set_alignment(0.5, 0.5)
- label.show()
- window.add(label)
-
- self.namecol = namecol
- self.__save = None # saves the current cell
- self.__next = None # the timer id for the next tooltip to be shown
- self.__shown = False # flag on whether the tooltip window is shown
-
- def __show(self, tooltip, x, y):
- '''
- show the tooltip
- '''
- self.label.set_label(tooltip) # set label
- self.window.move(x, y) # move the window
- self.window.show() # show it
- self.__shown = True
-
- def __hide(self):
- '''
- hide the tooltip
- '''
- self.__queue_next()
- self.window.hide()
- self.__shown = False
-
- def __motion_handler(self, view, event):
- '''
- as the pointer moves across the view, show a tooltip
- '''
- path_at_pos = view.get_path_at_pos(int(event.x), int(event.y))
- if path_at_pos:
- path, col, x, y = path_at_pos
- tooltip = self.get_tooltip(view, col, path)
- if tooltip:
- tooltip = str(tooltip).strip()
- self.__queue_next((path, col), tooltip, int(event.x_root),
- int(event.y_root))
- return
- self.__hide()
-
- def __queue_next(self, *args):
- '''
- queue next request to show a tooltip
- if args is non-empty it means a request was made to show a
- tooltip. if empty, no request is being made, but any
- pending requests should be cancelled anyway
- '''
- cell = None
-
- if args: # if called with args, break them out
- cell, tooltip, x, y = args
-
- # if it's the same cell as previously shown, just return
- if self.__save == cell:
- return
-
- if self.__next: # if we have something queued up, cancel it
- gobject.source_remove(self.__next)
- self.__next = None
-
- if cell: # if there was a request
- if self.__shown: # if tooltip is already shown show the new one
- self.__show(tooltip, x, y)
- else: # else queue it up in 1/2 second
- self.__next = gobject.timeout_add(500, self.__show,
- tooltip, x, y)
- self.__save = cell # save this cell
-
- def __on_expose_event(self, window, event):
- '''
- this magic is required so the window appears with a 1-pixel
- black border (default gtk Style).
- '''
- w, h = window.size_request()
- window.style.paint_flat_box(window.window, gtk.STATE_NORMAL,
- gtk.SHADOW_OUT, None, window, 'tooltip', 0, 0, w, h)
-
- def add_view(self, view):
- '''
- add a gtk.TreeView to the tooltip
- '''
- view.connect('motion-notify-event', self.__motion_handler)
- view.connect('leave-notify-event', lambda i, j: self.__hide())
-
- def get_tooltip(self, view, column, path):
- '''
- See the module doc string for a description of this method
- '''
- if column is self.namecol:
- model = view.get_model()
- name = model[path][0]
- return name.file.filename
-
-
-if __name__ == '__main__':
- #Translations
- t = gettext.translation('gui', 'locale', fallback=True)
- _ = t.ugettext
- t.install()
-
- #Main
- GUI()
- gtk.main()
diff --git a/setup.py b/setup.py
index 35d282e..8fdd923 100755
--- a/setup.py
+++ b/setup.py
@@ -10,4 +10,5 @@ setup(name='MAT',
license='GPLv2',
url='https://gitweb.torproject.org/user/jvoisin/mat.git',
packages=['mat'],
+ script=['mat-cli', 'mat-gui']
)
--
cgit v1.3