From 9eae571eb285409ddd46cf9ea7f36465e11bc623 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 15:22:49 +0300 Subject: [PATCH 01/34] migrate to python3 --- modules/core.py | 12 ++++++------ modules/prefs.py | 6 +++--- pyrite.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/modules/core.py b/modules/core.py index 2d69fa4..eadfbcb 100644 --- a/modules/core.py +++ b/modules/core.py @@ -33,15 +33,15 @@ from pango import FontDescription from os import access, R_OK, read, close, pipe from os.path import isfile -from urllib import url2pathname +from urllib.request import url2pathname from shlex import split from subprocess import check_output from time import sleep # Custom Modules: -import cfg -import prefs -import crypt_interface -from messages import MESSAGE_DICT +from . import cfg +from . import prefs +from . import crypt_interface +from .messages import MESSAGE_DICT # Important variables SIGSTOP, SIGCONT = 19, 18 @@ -748,7 +748,7 @@ def action_open(self, w): # Called when direct-file-mode FileChooserButton gets a new file set, # either because of dnd or manual selection def action_chooserbtn_file_set(self, w): - print "[on_file-set] FileChooserButton.get_filename() output:\n{!r}\n".format(w.get_filename()) + print("[on_file-set] FileChooserButton.get_filename() output:\n{!r}\n".format(w.get_filename())) self.initiate_filemode() diff --git a/modules/prefs.py b/modules/prefs.py index 4074209..8541ae2 100644 --- a/modules/prefs.py +++ b/modules/prefs.py @@ -26,12 +26,12 @@ # StdLib: import gtk import glib -import cPickle as pickle +import pickle as pickle from sys import stderr from os import access, R_OK # Custom Modules: -import cfg -from messages import PREFS_MESSAGE_DICT as MESSAGE_DICT +from . import cfg +from .messages import PREFS_MESSAGE_DICT as MESSAGE_DICT diff --git a/pyrite.py b/pyrite.py index 8c33798..c601a51 100755 --- a/pyrite.py +++ b/pyrite.py @@ -86,6 +86,6 @@ try: FeS2.main() except KeyboardInterrupt: - print + print() exit() From 72f73f331ab515571847b62535c6c8dd25a58fb4 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 15:39:58 +0300 Subject: [PATCH 02/34] migrate to GTK3 --- modules/cfg.py | 22 +++++----- modules/core.py | 112 ++++++++++++++++++++++++----------------------- modules/prefs.py | 20 +++++---- ui/main.glade | 20 ++++----- 4 files changed, 90 insertions(+), 84 deletions(-) diff --git a/modules/cfg.py b/modules/cfg.py index 4b9daf7..ece8562 100644 --- a/modules/cfg.py +++ b/modules/cfg.py @@ -23,7 +23,9 @@ # #------------------------------------------------------------------------------ -import gtk +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk from os import getenv # Important variables @@ -34,14 +36,14 @@ # List of possible Infobar message types MSGTYPES = [0, - gtk.MESSAGE_INFO, # 1 - gtk.MESSAGE_QUESTION, # 2 - gtk.MESSAGE_WARNING, # 3 - gtk.MESSAGE_ERROR] # 4 + Gtk.MessageType.INFO, # 1 + Gtk.MessageType.QUESTION, # 2 + Gtk.MessageType.WARNING, # 3 + Gtk.MessageType.ERROR] # 4 # List of possible images to show in Infobar -IMGTYPES = [gtk.STOCK_APPLY, # 0 - gtk.STOCK_DIALOG_INFO, # 1 - gtk.STOCK_DIALOG_QUESTION, # 2 - gtk.STOCK_DIALOG_WARNING, # 3 - gtk.STOCK_DIALOG_ERROR] # 4 +IMGTYPES = [Gtk.STOCK_APPLY, # 0 + Gtk.STOCK_DIALOG_INFO, # 1 + Gtk.STOCK_DIALOG_QUESTION, # 2 + Gtk.STOCK_DIALOG_WARNING, # 3 + Gtk.STOCK_DIALOG_ERROR] # 4 diff --git a/modules/core.py b/modules/core.py index eadfbcb..5d65c32 100644 --- a/modules/core.py +++ b/modules/core.py @@ -24,13 +24,15 @@ #------------------------------------------------------------------------------ # StdLib: -import gtk -gtk.gdk.threads_init() +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +# gtk.gdk.threads_init() import glib -glib.threads_init() +# glib.threads_init() from threading import Thread from sys import stderr -from pango import FontDescription from os import access, R_OK, read, close, pipe from os.path import isfile from urllib.request import url2pathname @@ -53,11 +55,11 @@ class Pyrite: """Display GTK+ window to interact with gpg or openssl via Xface object.""" - def show_errmsg(self, msg, dialog=gtk.MESSAGE_ERROR, parent=None): + def show_errmsg(self, msg, dialog=Gtk.MessageType.ERROR, parent=None): """Display msg with GtkMessageDialog.""" - d = gtk.MessageDialog( - parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, dialog, - gtk.BUTTONS_OK, msg) + d = Gtk.MessageDialog( + parent, Gtk.DIALOG_MODAL | Gtk.DIALOG_DESTROY_WITH_PARENT, dialog, + Gtk.BUTTONS_OK, msg) d.run() d.destroy() @@ -66,7 +68,7 @@ def __init__(self, cmdlineargs): """Build GUI interface from XML, etc.""" # Use GtkBuilder to build our GUI from the XML file - builder = gtk.Builder() + builder = Gtk.Builder() try: builder.add_from_file(cfg.ASSETDIR + 'ui/main.glade') except: self.show_errmsg( @@ -143,7 +145,7 @@ def __init__(self, cmdlineargs): self.g_activityspin = builder.get_object('spinner1') # Set app icon to something halfway-decent - gtk.window_set_default_icon_name(gtk.STOCK_DIALOG_AUTHENTICATION) + Gtk.window_set_default_icon_name(Gtk.STOCK_DIALOG_AUTHENTICATION) # Connect signals builder.connect_signals(self) @@ -187,8 +189,8 @@ def __init__(self, cmdlineargs): #------------------------------------------------ DRAG AND DROP FUNNESS dnd_list = [ ( 'text/uri-list', 0, TARGET_TYPE_URI_LIST ) ] self.g_msgtxtview.drag_dest_set( - gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT, - dnd_list, gtk.gdk.ACTION_COPY) + Gtk.DEST_DEFAULT_MOTION | Gtk.DEST_DEFAULT_HIGHLIGHT, + dnd_list, Gtk.gdk.ACTION_COPY) #---------------------------------------------------- CMDLINE ARGUMENTS if cmdlineargs: @@ -223,7 +225,7 @@ def infobar(self, id, filename=None, customtext=None, vbox=None): # Find the needed dictionary inside our message dict, by id MSG = MESSAGE_DICT[id] - # Use value from MSG type & icon to lookup Gtk constant, e.g. gtk.MESSAGE_INFO + # Use value from MSG type & icon to lookup Gtk constant, e.g. Gtk.MessageType.INFO msgtype = cfg.MSGTYPES[ MSG['type'] ] imgtype = cfg.IMGTYPES[ MSG['icon'] ] # Replace variables in message text & change text color @@ -232,24 +234,24 @@ def infobar(self, id, filename=None, customtext=None, vbox=None): "") # Now that we have all the data we need, START creating! - ibar = gtk.InfoBar() + ibar = Gtk.InfoBar() ibar.set_message_type(msgtype) if vbox: # If specific vbox requested: assume ibar for filemode, add cancel button - ibar.add_button (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) + ibar.add_button (Gtk.STOCK_CANCEL, Gtk.RESPONSE_CANCEL) ibar.connect ('response', self.cleanup_filemode) else: # If no specific vbox requested: do normal ibar at the top of message area vbox = self.vbox_ibar - ibar.add_button (gtk.STOCK_OK, gtk.RESPONSE_OK) + ibar.add_button (Gtk.STOCK_OK, Gtk.RESPONSE_OK) ibar.connect ('response', lambda *args: ibar.destroy()) vbox.pack_end (ibar, False, False) content = ibar.get_content_area() - img = gtk.Image() - img.set_from_stock (imgtype, gtk.ICON_SIZE_LARGE_TOOLBAR) + img = Gtk.Image() + img.set_from_stock (imgtype, Gtk.ICON_SIZE_LARGE_TOOLBAR) content.pack_start (img, False, False) img.show () - label = gtk.Label() + label = Gtk.Label() label.set_markup (message) content.pack_start (label, False, False) label.show () @@ -376,9 +378,9 @@ def set_defaults_from_prefs(self, startup=False): self.g_errtxtview.modify_font( FontDescription("normal {}".format(self.p['errfntsize']))) self.g_msgtxtview.modify_base( - gtk.STATE_NORMAL, gtk.gdk.color_parse(self.p['color_bg'])) + Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_bg'])) self.g_msgtxtview.modify_text( - gtk.STATE_NORMAL, gtk.gdk.color_parse(self.p['color_fg'])) + Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_fg'])) if self.p['opc_slider']: self.g_slider.set_range (0, 100) @@ -421,10 +423,10 @@ def fix_msgtxtviewcolor(self, sensitive): """Change Message area text to black when TextView insensitive.""" if sensitive: self.g_msgtxtview.modify_text( - gtk.STATE_NORMAL, gtk.gdk.color_parse(self.p['color_fg'])) + Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_fg'])) else: self.g_msgtxtview.modify_text( - gtk.STATE_NORMAL, gtk.gdk.color_parse('black')) + Gtk.STATE_NORMAL, Gtk.gdk.color_parse('black')) def get_file_path_from_dnd_dropped_uri(self, uri): @@ -494,9 +496,9 @@ def confirm_overwrite_callback(self, chooser): self.show_errmsg( "Simultaneously reading from & writing to a file is a baaad idea. " "Choose a different output filename.", parent=chooser) - return gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN + return Gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN else: - return gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM + return Gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM # Generic file chooser for opening or saving @@ -506,15 +508,15 @@ def chooser_grab_filename(self, mode, save_suggestion=None): filename = None if mode in 'open': title = "Choose text file to open as input..." elif mode in 'save': title = "Choose output filename..." - cmd = ("gtk.FileChooserDialog('{0}', None, gtk.FILE_CHOOSER_ACTION_{1}, " - "(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))" + cmd = ("Gtk.FileChooserDialog('{0}', None, Gtk.FILE_CHOOSER_ACTION_{1}, " + "(Gtk.STOCK_CANCEL, Gtk.RESPONSE_CANCEL, Gtk.STOCK_OPEN, Gtk.RESPONSE_OK))" .format(title, mode.upper())) chooser = eval(cmd) if mode in 'open': # Setup file filters - t = gtk.FileFilter() ; t.set_name("Text Files") ; t.add_mime_type("text/*") - a = gtk.FileFilter() ; a.set_name("All Files") ; a.add_pattern("*") + t = Gtk.FileFilter() ; t.set_name("Text Files") ; t.add_mime_type("text/*") + a = Gtk.FileFilter() ; a.set_name("All Files") ; a.add_pattern("*") chooser.add_filter(t) chooser.add_filter(a) elif mode in 'save': @@ -523,7 +525,7 @@ def chooser_grab_filename(self, mode, save_suggestion=None): chooser.connect('confirm-overwrite', self.confirm_overwrite_callback) if save_suggestion: chooser.set_current_name(save_suggestion) - if chooser.run() == gtk.RESPONSE_OK: + if chooser.run() == Gtk.RESPONSE_OK: filename = chooser.get_filename() chooser.destroy() return filename @@ -621,8 +623,8 @@ def cleanup_filemode(self, *args): self.g_sigmode.set_active (0) # Set statusbar self.set_stdstatus() - #while gtk.events_pending(): - gtk.main_iteration() + #while Gtk.events_pending(): + Gtk.main_iteration() # Reset filenames self.x.io['infile'] = 0 self.x.io['outfile'] = 0 @@ -643,7 +645,7 @@ def action_quit(self, w): self.x.childprocess.terminate() stderr.write("\n") #sleep(0.2) - gtk.main_quit() + Gtk.main_quit() # Called when dnd occurs on Message TextView @@ -678,10 +680,10 @@ def action_switch_engine(self, w): # Called by About menu item def action_about(self, w): """Launch About dialog.""" - builder = gtk.Builder() + builder = Gtk.Builder() builder.add_from_file(cfg.ASSETDIR + 'ui/about.glade') about = builder.get_object('aboutdialog') - about.set_logo_icon_name(gtk.STOCK_DIALOG_AUTHENTICATION) + about.set_logo_icon_name(Gtk.STOCK_DIALOG_AUTHENTICATION) about.set_transient_for(self.g_window) about.set_version(cfg.VERSION) about.connect('response', lambda *args: about.destroy()) @@ -767,8 +769,8 @@ def action_save(self, w): # Set saving status self.g_statusbar.push(self.status, "Saving {}".format(filename)) - #while gtk.events_pending(): - gtk.main_iteration() + #while Gtk.events_pending(): + Gtk.main_iteration() # Grab text from buffer buffertext = self.buff.get_text(self.buff.get_start_iter(), @@ -796,19 +798,19 @@ def action_redo(self, w): # Called by Cut toolbar btn or menu item def action_cut(self, w): """Cut msg TextBuffer selection.""" - self.buff.cut_clipboard(gtk.clipboard_get(), True) + self.buff.cut_clipboard(Gtk.clipboard_get(), True) # Called by Copy toolbar btn or menu item def action_copy(self, w): """Copy msg TextBuffer selection.""" - self.buff.copy_clipboard(gtk.clipboard_get()) + self.buff.copy_clipboard(Gtk.clipboard_get()) # Called by Paste toolbar btn or menu item def action_paste(self, w): """Paste clipboard into msg TextBuffer at selection.""" - self.buff.paste_clipboard(gtk.clipboard_get(), None, True) + self.buff.paste_clipboard(Gtk.clipboard_get(), None, True) # Called by Copyall toolbar btn @@ -818,7 +820,7 @@ def action_copyall(self, w): return self.buff.select_range(self.buff.get_start_iter(), self.buff.get_end_iter()) - self.buff.copy_clipboard(gtk.clipboard_get()) + self.buff.copy_clipboard(Gtk.clipboard_get()) self.infobar('txtview_copyall_success') @@ -859,7 +861,7 @@ def action_encrypt(self, w): # DEBUG #self.g_chooserbtn.select_filename('/etc/passwd') #self.g_expander.set_expanded(True) - #gtk.main_iteration() + #Gtk.main_iteration() #return if self.g_signverify.get_active(): # If in sign-only mode, figure out which sig-type @@ -1048,10 +1050,10 @@ def action_toggle_wordwrap(self, w): """Toggle word wrapping for main message TextView.""" if w.get_active(): # If entering toggled state, enable word wrapping - self.g_msgtxtview.set_wrap_mode(gtk.WRAP_WORD) + self.g_msgtxtview.set_wrap_mode(Gtk.WRAP_WORD) else: # If leaving toggled state, disable word wrapping - self.g_msgtxtview.set_wrap_mode(gtk.WRAP_NONE) + self.g_msgtxtview.set_wrap_mode(Gtk.WRAP_NONE) # Called by [processing progbar] Cancel button @@ -1064,9 +1066,9 @@ def action_cancel_child_process(self, btn): w.set_sensitive (False) self.g_progbar.set_text ("Canceling Operation...") self.g_activityspin.stop() - gtk.main_iteration() + Gtk.main_iteration() while not self.x.childprocess: - gtk.main_iteration() + Gtk.main_iteration() if self.paused: self.x.childprocess.send_signal(SIGCONT) self.x.childprocess.terminate() @@ -1079,13 +1081,13 @@ def action_pause_child_process(self, btn): # We can't pause childprocess until it actually starts while not self.x.childprocess: - gtk.main_iteration() + Gtk.main_iteration() if self.paused: # Already paused, so, time to unpause stderr.write ("\n") self.paused = False - btn.set_relief (gtk.RELIEF_NONE) + btn.set_relief (Gtk.RELIEF_NONE) self.g_progbar.set_text ("{} working...".format(self.engine)) self.g_activityspin.start() self.x.childprocess.send_signal(SIGCONT) @@ -1093,7 +1095,7 @@ def action_pause_child_process(self, btn): # Time to pause stderr.write ("\n") self.paused = True - btn.set_relief (gtk.RELIEF_NORMAL) + btn.set_relief (Gtk.RELIEF_NORMAL) self.g_progbar.set_text ("Operation PAUSED") self.g_activityspin.stop() self.x.childprocess.send_signal(SIGSTOP) @@ -1243,7 +1245,7 @@ def launchxface(self, action): if self.canceled: break if c % 15 == 0 and not self.paused: self.g_progbar.pulse() - gtk.main_iteration() + Gtk.main_iteration() c += 1 if self.quiting: # If application is shutting down @@ -1390,13 +1392,13 @@ def show_working_progress(self, show=True, action=None): self.g_statusbar.push(self.status, status) self.g_activityspin.set_visible(True) self.g_activityspin.start() - gtk.main_iteration() + Gtk.main_iteration() else: # If finished processing: ensure progbar buttons are normal, reset status, stop spinner for w in self.g_cancel, self.g_pause: w.set_sensitive(True) - w.set_relief(gtk.RELIEF_NONE) + w.set_relief(Gtk.RELIEF_NONE) self.g_activityspin.stop() self.g_activityspin.set_visible(False) self.g_statusbar.pop(self.status) @@ -1406,10 +1408,10 @@ def show_working_progress(self, show=True, action=None): def main(self): """Show main window, tweak some GTK+ settings and start GTK+ main loop.""" self.g_window.show() - settings = gtk.settings_get_default() + settings = Gtk.settings_get_default() settings.props.gtk_button_images = True - with gtk.gdk.lock: - gtk.main() + with Gtk.gdk.lock: + Gtk.main() diff --git a/modules/prefs.py b/modules/prefs.py index 8541ae2..d0fdb2c 100644 --- a/modules/prefs.py +++ b/modules/prefs.py @@ -24,7 +24,9 @@ #------------------------------------------------------------------------------ # StdLib: -import gtk +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk import glib import pickle as pickle from sys import stderr @@ -107,7 +109,7 @@ def destroy_ibar(): # Find the needed dictionary inside our message dict, by id MSG = MESSAGE_DICT[id] - # Use value from MSG type & icon to lookup Gtk constant, e.g. gtk.MESSAGE_INFO + # Use value from MSG type & icon to lookup Gtk constant, e.g. Gtk.MessageType.INFO msgtype = cfg.MSGTYPES[ MSG['type'] ] imgtype = cfg.IMGTYPES[ MSG['icon'] ] # Replace variables in message text & change text color @@ -116,12 +118,12 @@ def destroy_ibar(): "") # Now that we have all the data we need, START creating! - self.ibar = gtk.InfoBar() + self.ibar = Gtk.InfoBar() self.ibar.set_message_type(msgtype) self.vbox_ib.pack_end (self.ibar, False, False) - img = gtk.Image() - img.set_from_stock (imgtype, gtk.ICON_SIZE_LARGE_TOOLBAR) - label = gtk.Label() + img = Gtk.Image() + img.set_from_stock (imgtype, Gtk.ICON_SIZE_LARGE_TOOLBAR) + label = Gtk.Label() label.set_markup (message) content = self.ibar.get_content_area() content.pack_start (img, False, False) @@ -135,7 +137,7 @@ def destroy_ibar(): def open_preferences_window(self, parentwindow): """Show the preferences window. Duh.""" self.ibar_timeout = 0 - builder = gtk.Builder() + builder = Gtk.Builder() builder.add_from_file(cfg.ASSETDIR + 'ui/preferences.glade') # Main window self.window = builder.get_object('window1') @@ -214,8 +216,8 @@ def populate_pref_window_prefs(self): self.sp_opacity.set_value (self.p['opacity']) self.sp_msgfntsize.set_value (self.p['msgfntsize']) self.sp_errfntsize.set_value (self.p['errfntsize']) - self.btn_color_fg.set_color (gtk.gdk.color_parse(self.p['color_fg'])) - self.btn_color_bg.set_color (gtk.gdk.color_parse(self.p['color_bg'])) + self.btn_color_fg.set_color (Gtk.gdk.color_parse(self.p['color_fg'])) + self.btn_color_bg.set_color (Gtk.gdk.color_parse(self.p['color_bg'])) def capture_current_prefs(self): diff --git a/ui/main.glade b/ui/main.glade index ea88f4c..fd8fb73 100644 --- a/ui/main.glade +++ b/ui/main.glade @@ -178,7 +178,7 @@ image6 False True - + @@ -194,7 +194,7 @@ img_open False True - + @@ -209,7 +209,7 @@ img_saveas False True - + @@ -232,7 +232,7 @@ image9 False True - + @@ -252,7 +252,7 @@ True True True - + @@ -357,7 +357,7 @@ True True True - + @@ -431,7 +431,7 @@ This will not add newline characters to Message image7 False True - + @@ -445,7 +445,7 @@ This will not add newline characters to Message image8 False True - + @@ -522,7 +522,7 @@ This will not add newline characters to Message none True False - + @@ -544,7 +544,7 @@ This will not add newline characters to Message none True False - + From 88df00448235e5c394a45d710c686f623170671d Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 15:44:55 +0300 Subject: [PATCH 03/34] core.py migrate dialog to GTK3 --- modules/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/core.py b/modules/core.py index 5d65c32..1f45725 100644 --- a/modules/core.py +++ b/modules/core.py @@ -58,8 +58,11 @@ class Pyrite: def show_errmsg(self, msg, dialog=Gtk.MessageType.ERROR, parent=None): """Display msg with GtkMessageDialog.""" d = Gtk.MessageDialog( - parent, Gtk.DIALOG_MODAL | Gtk.DIALOG_DESTROY_WITH_PARENT, dialog, - Gtk.BUTTONS_OK, msg) + flags=0, + message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.OK, + text=msg + ) d.run() d.destroy() From 52f075c66ae931e5ad3be59cb3c9edda35a3c0ef Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 15:54:34 +0300 Subject: [PATCH 04/34] main.glade rollback control mask --- ui/main.glade | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ui/main.glade b/ui/main.glade index fd8fb73..ea88f4c 100644 --- a/ui/main.glade +++ b/ui/main.glade @@ -178,7 +178,7 @@ image6 False True - + @@ -194,7 +194,7 @@ img_open False True - + @@ -209,7 +209,7 @@ img_saveas False True - + @@ -232,7 +232,7 @@ image9 False True - + @@ -252,7 +252,7 @@ True True True - + @@ -357,7 +357,7 @@ True True True - + @@ -431,7 +431,7 @@ This will not add newline characters to Message image7 False True - + @@ -445,7 +445,7 @@ This will not add newline characters to Message image8 False True - + @@ -522,7 +522,7 @@ This will not add newline characters to Message none True False - + @@ -544,7 +544,7 @@ This will not add newline characters to Message none True False - + From 73a3459473db3f1bbdea4245a6ee2ffb76389a6f Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 16:10:29 +0300 Subject: [PATCH 05/34] core.py migrate window --- modules/core.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/core.py b/modules/core.py index 1f45725..6f3dacf 100644 --- a/modules/core.py +++ b/modules/core.py @@ -148,7 +148,7 @@ def __init__(self, cmdlineargs): self.g_activityspin = builder.get_object('spinner1') # Set app icon to something halfway-decent - Gtk.window_set_default_icon_name(Gtk.STOCK_DIALOG_AUTHENTICATION) + Gtk.Window.set_default_icon_name("dialog-password") # Connect signals builder.connect_signals(self) @@ -1409,12 +1409,11 @@ def show_working_progress(self, show=True, action=None): #---------------------------------------------- RUN MAIN APPLICATION WINDOW def main(self): - """Show main window, tweak some GTK+ settings and start GTK+ main loop.""" + """Show main window, and start GTK+ main loop.""" self.g_window.show() - settings = Gtk.settings_get_default() - settings.props.gtk_button_images = True - with Gtk.gdk.lock: - Gtk.main() + self.g_window.connect("destroy", Gtk.main_quit) + self.g_window.show_all() + Gtk.main() From 1b250701d410cc3e1c8f44db29329e1edd84c37a Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 16:12:04 +0300 Subject: [PATCH 06/34] core.py disable fonts --- modules/core.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/core.py b/modules/core.py index 6f3dacf..9ba4c2c 100644 --- a/modules/core.py +++ b/modules/core.py @@ -191,9 +191,9 @@ def __init__(self, cmdlineargs): #------------------------------------------------ DRAG AND DROP FUNNESS dnd_list = [ ( 'text/uri-list', 0, TARGET_TYPE_URI_LIST ) ] - self.g_msgtxtview.drag_dest_set( - Gtk.DEST_DEFAULT_MOTION | Gtk.DEST_DEFAULT_HIGHLIGHT, - dnd_list, Gtk.gdk.ACTION_COPY) + # self.g_msgtxtview.drag_dest_set( + # Gtk.DEST_DEFAULT_MOTION | Gtk.DEST_DEFAULT_HIGHLIGHT, + # dnd_list, Gtk.gdk.ACTION_COPY) #---------------------------------------------------- CMDLINE ARGUMENTS if cmdlineargs: @@ -376,14 +376,14 @@ def set_defaults_from_prefs(self, startup=False): self.g_wrap.set_active (self.p['wrap']) # Set TextView fonts, sizes, and colors - self.g_msgtxtview.modify_font( - FontDescription("monospace {}".format(self.p['msgfntsize']))) - self.g_errtxtview.modify_font( - FontDescription("normal {}".format(self.p['errfntsize']))) - self.g_msgtxtview.modify_base( - Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_bg'])) - self.g_msgtxtview.modify_text( - Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_fg'])) + # self.g_msgtxtview.modify_font( + # FontDescription("monospace {}".format(self.p['msgfntsize']))) + # self.g_errtxtview.modify_font( + # FontDescription("normal {}".format(self.p['errfntsize']))) + # self.g_msgtxtview.modify_base( + # Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_bg'])) + # self.g_msgtxtview.modify_text( + # Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_fg'])) if self.p['opc_slider']: self.g_slider.set_range (0, 100) From 608782a8fb2ce28f382b277ea2b1e9e191f66942 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 16:12:25 +0300 Subject: [PATCH 07/34] cfg.py use assets from the current dir --- modules/cfg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cfg.py b/modules/cfg.py index ece8562..d1fc353 100644 --- a/modules/cfg.py +++ b/modules/cfg.py @@ -30,7 +30,7 @@ # Important variables VERSION = 'v1.0.2' -ASSETDIR = '/usr/share/pyrite/' +ASSETDIR = './' USERPREF_FILE = getenv('HOME') + '/.pyrite' USERPREF_FORMAT_INFO = {'version':'Must6fa'} From 92ce2fdfe8b98bbf2fe5008d03ebe16f169b783a Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 19:41:34 +0300 Subject: [PATCH 08/34] shebang --- modules/cfg.py | 2 +- modules/core.py | 2 +- modules/crypt_interface.py | 2 +- modules/messages.py | 2 +- modules/prefs.py | 2 +- pyrite.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/cfg.py b/modules/cfg.py index d1fc353..befd1e8 100644 --- a/modules/cfg.py +++ b/modules/cfg.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of Pyrite. diff --git a/modules/core.py b/modules/core.py index 9ba4c2c..66f4147 100644 --- a/modules/core.py +++ b/modules/core.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of Pyrite. diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index b553809..a2ccbeb 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of Pyrite. diff --git a/modules/messages.py b/modules/messages.py index c79fa61..7154233 100644 --- a/modules/messages.py +++ b/modules/messages.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of Pyrite. diff --git a/modules/prefs.py b/modules/prefs.py index d0fdb2c..5cb5248 100644 --- a/modules/prefs.py +++ b/modules/prefs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of Pyrite. diff --git a/pyrite.py b/pyrite.py index c601a51..7a46331 100755 --- a/pyrite.py +++ b/pyrite.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of Pyrite. From 015f6adcf0c8496b44db5f91e666964f85468946 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 19:46:01 +0300 Subject: [PATCH 09/34] migrate colors --- modules/core.py | 84 ++++++++++++++++++++++++++++-------------------- modules/prefs.py | 33 ++++++++++++------- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/modules/core.py b/modules/core.py index 66f4147..71a0735 100644 --- a/modules/core.py +++ b/modules/core.py @@ -25,7 +25,11 @@ # StdLib: import gi -gi.require_version("Gtk", "3.0") +gi.require_version('GLib', '2.0') +gi.require_version('Gdk', '3.0') +gi.require_version('Gtk', '3.0') +from gi.repository import GLib +from gi.repository import Gdk from gi.repository import Gtk # gtk.gdk.threads_init() @@ -192,8 +196,8 @@ def __init__(self, cmdlineargs): #------------------------------------------------ DRAG AND DROP FUNNESS dnd_list = [ ( 'text/uri-list', 0, TARGET_TYPE_URI_LIST ) ] # self.g_msgtxtview.drag_dest_set( - # Gtk.DEST_DEFAULT_MOTION | Gtk.DEST_DEFAULT_HIGHLIGHT, - # dnd_list, Gtk.gdk.ACTION_COPY) + # Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT, + # dnd_list, Gdk.DragAction.COPY) #---------------------------------------------------- CMDLINE ARGUMENTS if cmdlineargs: @@ -241,28 +245,28 @@ def infobar(self, id, filename=None, customtext=None, vbox=None): ibar.set_message_type(msgtype) if vbox: # If specific vbox requested: assume ibar for filemode, add cancel button - ibar.add_button (Gtk.STOCK_CANCEL, Gtk.RESPONSE_CANCEL) + ibar.add_button (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) ibar.connect ('response', self.cleanup_filemode) else: # If no specific vbox requested: do normal ibar at the top of message area vbox = self.vbox_ibar - ibar.add_button (Gtk.STOCK_OK, Gtk.RESPONSE_OK) + ibar.add_button (Gtk.STOCK_OK, Gtk.ResponseType.OK) ibar.connect ('response', lambda *args: ibar.destroy()) - vbox.pack_end (ibar, False, False) + vbox.pack_end (ibar, False, False, 0) content = ibar.get_content_area() img = Gtk.Image() - img.set_from_stock (imgtype, Gtk.ICON_SIZE_LARGE_TOOLBAR) - content.pack_start (img, False, False) + img.set_from_stock (imgtype, Gtk.IconSize.LARGE_TOOLBAR) + content.pack_start (img, False, False, 0) img.show () label = Gtk.Label() label.set_markup (message) - content.pack_start (label, False, False) + content.pack_start (label, False, False, 0) label.show () # FIXME: Why doesn't Esc trigger this close signal? ibar.connect ('close', lambda *args: ibar.destroy()) ibar.show() if MSG['timeout'] > 0: - glib.timeout_add_seconds(MSG['timeout'], ibar.destroy) + GLib.timeout_add_seconds(MSG['timeout'], ibar.destroy) return ibar @@ -380,10 +384,15 @@ def set_defaults_from_prefs(self, startup=False): # FontDescription("monospace {}".format(self.p['msgfntsize']))) # self.g_errtxtview.modify_font( # FontDescription("normal {}".format(self.p['errfntsize']))) - # self.g_msgtxtview.modify_base( - # Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_bg'])) - # self.g_msgtxtview.modify_text( - # Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_fg'])) + + bg_color = Gdk.Color(0, 0, 0) + bg_color.parse(self.p['color_bg']) + fg_color = Gdk.Color(0, 0, 0) + fg_color.parse(self.p['color_fg']) + self.g_msgtxtview.modify_base( + Gtk.StateType.NORMAL, bg_color) + self.g_msgtxtview.modify_text( + Gtk.StateType.NORMAL, fg_color) if self.p['opc_slider']: self.g_slider.set_range (0, 100) @@ -425,11 +434,15 @@ def setsensitive_gpgwidgets(x=True): def fix_msgtxtviewcolor(self, sensitive): """Change Message area text to black when TextView insensitive.""" if sensitive: + fg_color = Gdk.Color(0, 0, 0) + fg_color.parse(self.p['color_fg']) self.g_msgtxtview.modify_text( - Gtk.STATE_NORMAL, Gtk.gdk.color_parse(self.p['color_fg'])) + Gtk.StateType.NORMAL, fg_color) else: + fg_color = Gdk.Color(0, 0, 0) + fg_color.parse('black') self.g_msgtxtview.modify_text( - Gtk.STATE_NORMAL, Gtk.gdk.color_parse('black')) + Gtk.StateType.NORMAL, fg_color) def get_file_path_from_dnd_dropped_uri(self, uri): @@ -511,8 +524,8 @@ def chooser_grab_filename(self, mode, save_suggestion=None): filename = None if mode in 'open': title = "Choose text file to open as input..." elif mode in 'save': title = "Choose output filename..." - cmd = ("Gtk.FileChooserDialog('{0}', None, Gtk.FILE_CHOOSER_ACTION_{1}, " - "(Gtk.STOCK_CANCEL, Gtk.RESPONSE_CANCEL, Gtk.STOCK_OPEN, Gtk.RESPONSE_OK))" + cmd = ("Gtk.FileChooserDialog('{0}', None, Gtk.FileChooserAction.{1}, " + "(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))" .format(title, mode.upper())) chooser = eval(cmd) @@ -528,7 +541,7 @@ def chooser_grab_filename(self, mode, save_suggestion=None): chooser.connect('confirm-overwrite', self.confirm_overwrite_callback) if save_suggestion: chooser.set_current_name(save_suggestion) - if chooser.run() == Gtk.RESPONSE_OK: + if chooser.run() == Gtk.ResponseType.OK: filename = chooser.get_filename() chooser.destroy() return filename @@ -597,7 +610,8 @@ def initiate_filemode(self): else: # Otherwise, save TextView buffer for later and then blow it away self.filemode_saved_buff = self.buff.get_text(self.buff.get_start_iter(), - self.buff.get_end_iter()) + self.buff.get_end_iter(), + False) self.buff.set_text('') self.filemode_enablewidgets(False) @@ -777,7 +791,8 @@ def action_save(self, w): # Grab text from buffer buffertext = self.buff.get_text(self.buff.get_start_iter(), - self.buff.get_end_iter()) + self.buff.get_end_iter(), + False) try: # If can open file for writing, show success infobar with open(filename, 'w') as f: f.write(buffertext) @@ -1053,10 +1068,10 @@ def action_toggle_wordwrap(self, w): """Toggle word wrapping for main message TextView.""" if w.get_active(): # If entering toggled state, enable word wrapping - self.g_msgtxtview.set_wrap_mode(Gtk.WRAP_WORD) + self.g_msgtxtview.set_wrap_mode(Gtk.WrapMode.WORD) else: # If leaving toggled state, disable word wrapping - self.g_msgtxtview.set_wrap_mode(Gtk.WRAP_NONE) + self.g_msgtxtview.set_wrap_mode(Gtk.WrapMode.NONE) # Called by [processing progbar] Cancel button @@ -1090,7 +1105,7 @@ def action_pause_child_process(self, btn): # Already paused, so, time to unpause stderr.write ("\n") self.paused = False - btn.set_relief (Gtk.RELIEF_NONE) + btn.set_relief (Gtk.ReliefStyle.NONE) self.g_progbar.set_text ("{} working...".format(self.engine)) self.g_activityspin.start() self.x.childprocess.send_signal(SIGCONT) @@ -1098,7 +1113,7 @@ def action_pause_child_process(self, btn): # Time to pause stderr.write ("\n") self.paused = True - btn.set_relief (Gtk.RELIEF_NORMAL) + btn.set_relief (Gtk.ReliefStyle.NORMAL) self.g_progbar.set_text ("Operation PAUSED") self.g_activityspin.stop() self.x.childprocess.send_signal(SIGSTOP) @@ -1205,7 +1220,8 @@ def launchxface(self, action): # Save textview buffer to Xface stdin self.x.io['stdin'] = self.buff.get_text(self.buff.get_start_iter(), - self.buff.get_end_iter()) + self.buff.get_end_iter(), + False) # Set working status + spinner + progress bar self.show_working_progress(True, action) @@ -1214,9 +1230,9 @@ def launchxface(self, action): # Setup stderr file descriptors & update task status while processing self.x.io['stderr'] = pipe() - glib.io_add_watch( + GLib.io_add_watch( self.x.io['stderr'][0], - glib.IO_IN | glib.IO_HUP, + GLib.IO_IN | GLib.IO_HUP, self.update_task_status) if self.engine in 'OpenSSL': @@ -1231,9 +1247,9 @@ def launchxface(self, action): if verbose: # Setup gpg-status file descriptors & update terminal while processing self.x.io['gstatus'] = pipe() - glib.io_add_watch( + GLib.io_add_watch( self.x.io['gstatus'][0], - glib.IO_IN | glib.IO_HUP, + GLib.IO_IN | GLib.IO_HUP, self.update_task_status, 'term') # ATTEMPT EN-/DECRYPTION w/GPG Thread( @@ -1353,12 +1369,12 @@ def launchxface(self, action): #------------------------------------------ HELPERS FOR MAIN XFACE FUNCTION - # CB for glib.io_add_watch() + # CB for GLib.io_add_watch() def update_task_status(self, fd, condition, output='task'): """Read data waiting in file descriptor; close fd if other end hangs up.""" # If there's data to be read, let's read it - if condition == glib.IO_IN: + if condition == GLib.IO_IN: if output in 'task': # Output to Task Status self.buff2.insert(self.buff2.get_end_iter(), read(fd, 1024)) @@ -1368,7 +1384,7 @@ def update_task_status(self, fd, condition, output='task'): return True # If other end of pipe hangs up, close our fd and destroy the watcher - elif condition == glib.IO_HUP: + elif condition == GLib.IO_HUP: if output in 'term': stderr.write("\n") close(fd) @@ -1401,7 +1417,7 @@ def show_working_progress(self, show=True, action=None): # If finished processing: ensure progbar buttons are normal, reset status, stop spinner for w in self.g_cancel, self.g_pause: w.set_sensitive(True) - w.set_relief(Gtk.RELIEF_NONE) + w.set_relief(Gtk.ReliefStyle.NONE) self.g_activityspin.stop() self.g_activityspin.set_visible(False) self.g_statusbar.pop(self.status) diff --git a/modules/prefs.py b/modules/prefs.py index 5cb5248..d9abafe 100644 --- a/modules/prefs.py +++ b/modules/prefs.py @@ -25,9 +25,13 @@ # StdLib: import gi -gi.require_version("Gtk", "3.0") +gi.require_version('GLib', '2.0') +gi.require_version('Gdk', '3.0') +gi.require_version('Gtk', '3.0') +from gi.repository import GLib from gi.repository import Gtk -import glib +from gi.repository import Gdk +from gi.repository import GObject import pickle as pickle from sys import stderr from os import access, R_OK @@ -104,7 +108,7 @@ def destroy_ibar(): # If infobar already active: delete old timeout, destroy old ibar if self.ibar_timeout > 0: - glib.source_remove(self.ibar_timeout) + GObject.source_remove(self.ibar_timeout) destroy_ibar() # Find the needed dictionary inside our message dict, by id @@ -120,18 +124,18 @@ def destroy_ibar(): # Now that we have all the data we need, START creating! self.ibar = Gtk.InfoBar() self.ibar.set_message_type(msgtype) - self.vbox_ib.pack_end (self.ibar, False, False) + self.vbox_ib.pack_end (self.ibar, False, False, 0) img = Gtk.Image() - img.set_from_stock (imgtype, Gtk.ICON_SIZE_LARGE_TOOLBAR) + img.set_from_stock (imgtype, Gtk.IconSize.LARGE_TOOLBAR) label = Gtk.Label() label.set_markup (message) content = self.ibar.get_content_area() - content.pack_start (img, False, False) - content.pack_start (label, False, False) + content.pack_start (img, False, False, 0) + content.pack_start (label, False, False, 0) img.show () label.show () self.ibar.show () - self.ibar_timeout = glib.timeout_add_seconds(MSG['timeout'], destroy_ibar) + self.ibar_timeout = GLib.timeout_add_seconds(MSG['timeout'], destroy_ibar) def open_preferences_window(self, parentwindow): @@ -177,7 +181,7 @@ def open_preferences_window(self, parentwindow): # TODO: Advanced tab #self.tg_args_gpg_e = builder.get_object('tg_args_gpg_e') #self.en_args_gpg_e = builder.get_object('en_args_gpg_e') - self.window.set_transient_for(parentwindow) + self.set_transient_for(parentwindow) if access(cfg.USERPREF_FILE, R_OK): btn_revert = builder.get_object('btn_revert') btn_revert.set_sensitive(True) @@ -216,8 +220,15 @@ def populate_pref_window_prefs(self): self.sp_opacity.set_value (self.p['opacity']) self.sp_msgfntsize.set_value (self.p['msgfntsize']) self.sp_errfntsize.set_value (self.p['errfntsize']) - self.btn_color_fg.set_color (Gtk.gdk.color_parse(self.p['color_fg'])) - self.btn_color_bg.set_color (Gtk.gdk.color_parse(self.p['color_bg'])) + + fg_color = Gdk.Color(0, 0, 0) + fg_color.parse(self.p['color_fg']) + + bg_color = Gdk.Color(0, 0, 0) + bg_color.parse(self.p['color_bg']) + + self.btn_color_fg.set_color (fg_color) + self.btn_color_bg.set_color (bg_color) def capture_current_prefs(self): From 2ccf4544e2f669c45d07f8e06e305fcbc4a6d3d1 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 20:42:29 +0300 Subject: [PATCH 10/34] fix bytes to str conversion --- modules/core.py | 8 +++++--- modules/crypt_interface.py | 9 ++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/core.py b/modules/core.py index 71a0735..3d51c3d 100644 --- a/modules/core.py +++ b/modules/core.py @@ -1347,7 +1347,7 @@ def launchxface(self, action): self.infobar('x_verify_success') else: # Set TextBuffer to gpg stdout - self.buff.set_text(self.x.io['stdout']) + self.buff.set_text(self.x.io['stdout'].decode('utf-8')) self.x.io['stdout'] = 0 if self.engine in 'OpenSSL' and action in 'enc': self.infobar('x_opensslenc_success_textmode', customtext=cipher) @@ -1377,10 +1377,12 @@ def update_task_status(self, fd, condition, output='task'): if condition == GLib.IO_IN: if output in 'task': # Output to Task Status - self.buff2.insert(self.buff2.get_end_iter(), read(fd, 1024)) + b = read(fd, 1024).decode('utf-8') + self.buff2.insert(self.buff2.get_end_iter(), b) else: # Output to stderr (will show if run from terminal) - stderr.write(read(fd, 1024)) + b = read(fd, 1024).decode('utf-8') + stderr.write(b) return True # If other end of pipe hangs up, close our fd and destroy the watcher diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index a2ccbeb..736852d 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -258,10 +258,12 @@ def gpg( self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][1]) # Otherwise, only difference for Popen is we need the stdin pipe else: - self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=self.io['stderr'][1]) + b = self.io['stderr'] + self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=b[1]) # Time to communicate! Save output for later - self.io['stdout'] = self.childprocess.communicate(input=self.io['stdin'])[0] + b = self.io['stdin'].encode('utf-8') + self.io['stdout'] = self.childprocess.communicate(input=b)[0] # Clear stdin from our dictionary asap, in case it's huge self.io['stdin'] = '' @@ -399,7 +401,8 @@ def openssl( self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=self.io['stderr'][1]) # Time to communicate! Save output for later - self.io['stdout'] = self.childprocess.communicate(input=self.io['stdin'])[0] + b = self.io['stdin'].encode('utf-8') + self.io['stdout'] = self.childprocess.communicate(input=b)[0] # Clear stdin from our dictionary asap, in case it's huge self.io['stdin'] = '' From 4505dc1fbb2d61de13f15fc6ff4806d6ac90bacd Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 20:52:27 +0300 Subject: [PATCH 11/34] Fix Font --- modules/core.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/core.py b/modules/core.py index 3d51c3d..f051e6a 100644 --- a/modules/core.py +++ b/modules/core.py @@ -31,6 +31,7 @@ from gi.repository import GLib from gi.repository import Gdk from gi.repository import Gtk +from gi.repository import Pango # gtk.gdk.threads_init() import glib @@ -380,10 +381,10 @@ def set_defaults_from_prefs(self, startup=False): self.g_wrap.set_active (self.p['wrap']) # Set TextView fonts, sizes, and colors - # self.g_msgtxtview.modify_font( - # FontDescription("monospace {}".format(self.p['msgfntsize']))) - # self.g_errtxtview.modify_font( - # FontDescription("normal {}".format(self.p['errfntsize']))) + self.g_msgtxtview.modify_font( + Pango.FontDescription("monospace {}".format(self.p['msgfntsize']))) + self.g_errtxtview.modify_font( + Pango.FontDescription("normal {}".format(self.p['errfntsize']))) bg_color = Gdk.Color(0, 0, 0) bg_color.parse(self.p['color_bg']) @@ -853,9 +854,9 @@ def action_zoom(self, w): self.p['msgfntsize'] -= 1 self.p['errfntsize'] -= 1 self.g_msgtxtview.modify_font( - FontDescription("monospace {}".format(self.p['msgfntsize']))) + Pango.FontDescription("monospace {}".format(self.p['msgfntsize']))) self.g_errtxtview.modify_font( - FontDescription("normal {}".format(self.p['errfntsize']))) + Pango.FontDescription("normal {}".format(self.p['errfntsize']))) # Called when Cipher combobox selection is changed From 574898763d2f566fdb8a42db62b536846112878d Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 20:55:14 +0300 Subject: [PATCH 12/34] Fix prefs window --- modules/core.py | 2 +- modules/prefs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core.py b/modules/core.py index f051e6a..9bffbe9 100644 --- a/modules/core.py +++ b/modules/core.py @@ -702,7 +702,7 @@ def action_about(self, w): builder.add_from_file(cfg.ASSETDIR + 'ui/about.glade') about = builder.get_object('aboutdialog') about.set_logo_icon_name(Gtk.STOCK_DIALOG_AUTHENTICATION) - about.set_transient_for(self.g_window) + # about.set_transient_for(self.g_window) about.set_version(cfg.VERSION) about.connect('response', lambda *args: about.destroy()) about.show() diff --git a/modules/prefs.py b/modules/prefs.py index d9abafe..fc42229 100644 --- a/modules/prefs.py +++ b/modules/prefs.py @@ -181,7 +181,7 @@ def open_preferences_window(self, parentwindow): # TODO: Advanced tab #self.tg_args_gpg_e = builder.get_object('tg_args_gpg_e') #self.en_args_gpg_e = builder.get_object('en_args_gpg_e') - self.set_transient_for(parentwindow) + # self.set_transient_for(parentwindow) if access(cfg.USERPREF_FILE, R_OK): btn_revert = builder.get_object('btn_revert') btn_revert.set_sensitive(True) From fea1d3b176b189ce476a3f7ac06e958fd38e66a5 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 21:06:59 +0300 Subject: [PATCH 13/34] glade files: migrate to Gtk3 I just opened them and saved. Can't guarantee that all was migrated correctly. --- ui/about.glade | 30 +- ui/main.glade | 876 +++++++++++++++++----------------- ui/preferences.glade | 1079 ++++++++++++++++++++---------------------- 3 files changed, 968 insertions(+), 1017 deletions(-) diff --git a/ui/about.glade b/ui/about.glade index e62dae1..dcacf58 100644 --- a/ui/about.glade +++ b/ui/about.glade @@ -1,14 +1,13 @@ - - + - False - 5 - True - dialog - True - Pyrite + False + 5 + True + dialog + True + Pyrite Copyright © 2012, 2013 Ryan Sawhill Aroha GnuPG (gpg/gpg2) encrypting, decrypting, signing, and verifying http://github.com/ryran/pyrite @@ -25,23 +24,24 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://gnu.org/licenses/gpl.html>. Ryan Sawhill Aroha <rsaw@redhat.com> - We need translators! + We need translators! We need someone to design some good icons! + image-missing - + True - False + False 2 - + True - False - end + False + end False True - end + end 0 diff --git a/ui/main.glade b/ui/main.glade index ea88f4c..8753c74 100644 --- a/ui/main.glade +++ b/ui/main.glade @@ -1,55 +1,54 @@ - - + True - False + False gtk-clear True - False + False gtk-zoom-in True - False + False gtk-zoom-out True - False + False gtk-execute True - False + False gtk-clear True - False + False gtk-revert-to-saved True - False + False gtk-apply True - False + False gtk-open True - False + False gtk-save True - False + False gtk-save-as @@ -143,117 +142,117 @@ - False + False Pyrite - center + center True - False + False True - False + False - False + False True - False + False _File - True + True True - False + False C_lear - False + False True - False - True - Clear all text/file buffers - True + False + True + Clear all text/file buffers + True image6 - False - True - + False + True + _Open Text File as Message - False + False True - False - True - Open text file in Message area - True + False + True + Open text file in Message area + True img_open - False - True - + False + True + _Save Copy of Message - False + False True - False - Save Message to text file - True + False + Save Message to text file + True img_saveas - False - True - + False + True + - False + False True - False + False Use OpenSSL as Engine - False + False True - False - True - Switch between using OpenSSL or GnuPG as the backend - True + False + True + Switch between using OpenSSL or GnuPG as the backend + True image9 - False - True - + False + True + - False + False True - False + False gtk-quit - False + False True - False - True - True - True - + False + True + True + True + @@ -262,103 +261,103 @@ - False + False True - False + False Edit - True + True True - False + False gtk-undo - False + False True False - False - True - True - True + False + True + True + True gtk-redo - False + False True False - False - True - True - True + False + True + True + True - False + False True - False + False gtk-cut - False + False True - False - True - True - True + False + True + True + True gtk-copy - False + False True - False - True - True - True + False + True + True + True gtk-paste - False + False True - False - True - True - True + False + True + True + True - False + False True - False + False gtk-preferences - False + False True - False - True - True - True - True - + False + True + True + True + True + @@ -367,86 +366,86 @@ - False + False True - False + False _View - True + True True - False + False - False + False True - False - True - Toggles line-wrapping in Message area + False + True + Toggles line-wrapping in Message area This will not add newline characters to Message Text _Wrapping - True + True True - False + False True - False - True - Show/hide side pane containing gpg/openssl cmd stderr (success/failure messages) + False + True + Show/hide side pane containing gpg/openssl cmd stderr (success/failure messages) _Task Status Side Panel - True + True True - False + False True - False - True - Enable/disable verbose status output from gpg (displayed in side panel) + False + True + Enable/disable verbose status output from gpg (displayed in side panel) _Verbose Output in Task Status - True + True - False + False True - False + False Increase Font Size - False + False True - False - True + False + True image7 - False - True - + False + True + Decrease Font Size - False + False True - False - True + False + True image8 - False - True - + False + True + @@ -455,24 +454,24 @@ This will not add newline characters to Message - False + False True - False + False Help - True + True True - False + False gtk-about - False + False True - False - True - True - True + False + True + True + True @@ -490,12 +489,12 @@ This will not add newline characters to Message True - False + False 3 True - False + False False @@ -507,23 +506,22 @@ This will not add newline characters to Message True - False - edge + False _Encrypt - False + False True - True - True - True - <i>Ctrl+E</i> + True + False + True + True + <i>Ctrl+E</i> img_encrypt none - True - False - + True + False @@ -534,18 +532,18 @@ This will not add newline characters to Message _Decrypt - False + False True - True - True - True - <i>Ctrl+D</i> + True + False + True + True + <i>Ctrl+D</i> img_decrypt none - True - False - + True + False @@ -563,7 +561,7 @@ This will not add newline characters to Message True - False + False @@ -576,16 +574,16 @@ This will not add newline characters to Message C_lear - False + False True - True - True - True - Clear all text/file buffers + True + False + True + True + Clear all text/file buffers img_clear none - True - False + True @@ -596,25 +594,25 @@ This will not add newline characters to Message - 70 - True - 0 - False - left + 70 + True + 0 + False + left False True 6 - end + end 4 True - False + False @@ -626,8 +624,8 @@ This will not add newline characters to Message - False - 0.02 + False + 0.02 True @@ -638,31 +636,31 @@ This will not add newline characters to Message True - False + False False True 2 - end + end 6 - False - True - True - True - Pause processing + False + True + False + True + True + Pause processing none - False True - False + False gtk-media-pause @@ -670,24 +668,24 @@ This will not add newline characters to Message False True - end + end 7 - False - True - True - True - Cancel processing + False + True + False + True + True + Cancel processing none - False True - False + False gtk-cancel @@ -695,7 +693,7 @@ This will not add newline characters to Message False True - end + end 8 @@ -709,12 +707,12 @@ This will not add newline characters to Message True - False + False 1 True - False + False False @@ -726,17 +724,17 @@ This will not add newline characters to Message Sign/Verif_y Mode - False + False True - True - False - Sign-only / verify-only mode + True + False + False + Sign-only / verify-only mode For adding a signature to a message/file without encrypting it or for verifying a signed message/file that isn't encrypted - True - False + True True - True + True toggle_mode_encdec @@ -750,16 +748,16 @@ For adding a signature to a message/file without encrypting it or for verifying Choose Output Filenames - False - True - False - True - When signing external files, the output filenames are automatically chosen by gpg -- output is saved to the same directory as the input file and an extension is added based on the options used + False + True + False + False + True + When signing external files, the output filenames are automatically chosen by gpg -- output is saved to the same directory as the input file and an extension is added based on the options used Check this if you wish to choose the output filename yourself (e.g., because the input file is in a directory you can't write to) - True - False - True + True + True False @@ -771,7 +769,7 @@ Check this if you wish to choose the output filename yourself (e.g., because the True - False + False False @@ -783,16 +781,16 @@ Check this if you wish to choose the output filename yourself (e.g., because the Enc/Dec _Mode - False + False True - True - False - True - Encrypt / decrypt / encrypt + sign mode - True - False + True + False + False + True + Encrypt / decrypt / encrypt + sign mode + True True - True + True False @@ -804,17 +802,17 @@ Check this if you wish to choose the output filename yourself (e.g., because the _Symmetric - False + False True - True - False - True - Symmetric encryption/decryption + True + False + False + True + Symmetric encryption/decryption Requires specifying a passphrase which is used as a shared key (for both encryption & decryption) - True - False - True + True + True @@ -827,17 +825,17 @@ Requires specifying a passphrase which is used as a shared key (for both encrypt _Asymmetric - False + False True - True - False - True - Asymmetric encryption/decryption + True + False + False + True + Asymmetric encryption/decryption Requires specifying recipients whose public keys will be used for encryption; or for decryption, it requires access to your gpg secret key - True - False - True + True + True @@ -850,17 +848,17 @@ Requires specifying recipients whose public keys will be used for encryption; or Adva_nced - False + False True - True - False - True - Allow mixing simple symmetric encryption with asymmetric encryption and/or signing + True + False + False + True + Allow mixing simple symmetric encryption with asymmetric encryption and/or signing When creating a signed + symmetrically-encrypted message, anything in the passphrase entry box will be ignored -- gpg-agent will need to ask for both the symmetric encryption key (passphrase) and [potentially] the passphrase to your secret key - True - False - True + True + True @@ -880,12 +878,12 @@ When creating a signed + symmetrically-encrypted message, anything in the passph True - False + False 1 True - False + False False @@ -898,11 +896,11 @@ When creating a signed + symmetrically-encrypted message, anything in the passph True False - False + False _Passphrase: - True - True - entry_pass + True + True + entry_pass False @@ -913,9 +911,9 @@ When creating a signed + symmetrically-encrypted message, anything in the passph True - True - True - Symmetric encryption/decryption key + True + True + Symmetric encryption/decryption key Max length limited only by available memory @@ -929,17 +927,13 @@ Example: You encrypt a message with your public key + symmetrically with a passp When you go to decrypt said message, you could leave the passphrase box empty (in which case gpg-agent will ask you for your private key passphrase); you could enter the symmetric passphrase you used, or you could enter your private key passphrase (both these options will bypass gpg-agent) False - - 10 - True - etched-in - True - gtk-dialog-authentication - True - False - True - True - Clear entry field + + 10 + True + etched-in + gtk-dialog-authentication + False + Clear entry field @@ -952,7 +946,7 @@ When you go to decrypt said message, you could leave the passphrase box empty (i True - False + False @@ -966,11 +960,11 @@ When you go to decrypt said message, you could leave the passphrase box empty (i True False - False + False _Recipients: - True - True - entry_recip + True + True + entry_recip False @@ -981,23 +975,19 @@ When you go to decrypt said message, you could leave the passphrase box empty (i True - True - True - Keys to use for asymmetric encryption + True + True + Keys to use for asymmetric encryption Use a semicolon to separate recipients - - 10 - True - etched-in - True - False - gtk-orientation-portrait - True - False - True - True - Clear entry field + + 10 + True + etched-in + False + gtk-orientation-portrait + False + Clear entry field @@ -1010,20 +1000,20 @@ Use a semicolon to separate recipients Enc. To Se_lf - False + False True False - True - False - True - Tells gpg to encrypt the message with the public key that corresponds to the first secret key in your keyring + True + False + False + True + Tells gpg to encrypt the message with the public key that corresponds to the first secret key in your keyring This is done in addition to any other recipients, or if in Advanced mode and performing symmetric encryption with a passphrase, then in addition to that Note for power users: if "Change Default Key" in the signature options toolbar is enabled, the contents of its entry box are used as self, instead of the first secret key in your keyring - True - False - True + True + True False @@ -1035,7 +1025,7 @@ Note for power users: if "Change Default Key" in the signature options toolbar i True - False + False @@ -1048,10 +1038,10 @@ Note for power users: if "Change Default Key" in the signature options toolbar i True - False + False _Cipher: - True - combobox_cipher + True + combobox_cipher False @@ -1062,9 +1052,10 @@ Note for power users: if "Change Default Key" in the signature options toolbar i True - False - True - [Not used when decrypting, verifying in gpg mode] + False + False + True + [Not used when decrypting, verifying in gpg mode] Configures symmetric encryption cipher algorithm @@ -1077,7 +1068,6 @@ This is used for both encryption and decryption in openssl-mode When decrypting, openssl needs to be told what cipher the input was encrypted with (unlike gpg) liststore_ciphers 0 - False @@ -1103,21 +1093,21 @@ When decrypting, openssl needs to be told what cipher the input was encrypted wi True - True + True 522 - True + True True - False + False True - False + False True - False + False False @@ -1127,19 +1117,19 @@ When decrypting, openssl needs to be told what cipher the input was encrypted wi - False + False True - True - True - True - Open text file in Message area + True + False + True + True + Open text file in Message area none - False True - False + False gtk-open @@ -1152,19 +1142,19 @@ When decrypting, openssl needs to be told what cipher the input was encrypted wi - False + False True - True - True - True - Save Message to text file + True + False + True + True + Save Message to text file none - False True - False + False gtk-save-as @@ -1177,19 +1167,19 @@ When decrypting, openssl needs to be told what cipher the input was encrypted wi - False + False True - True - True - True - Copy Message to clipboard + True + False + True + True + Copy Message to clipboard none - False True - False + False gtk-select-all @@ -1203,7 +1193,7 @@ When decrypting, openssl needs to be told what cipher the input was encrypted wi True - False + False False @@ -1214,21 +1204,21 @@ When decrypting, openssl needs to be told what cipher the input was encrypted wi - False + False True False - True - True - True - Undo the last action in Message area + True + False + True + True + Undo the last action in Message area none - False - top + top True - False + False gtk-undo @@ -1241,21 +1231,21 @@ When decrypting, openssl needs to be told what cipher the input was encrypted wi - False + False True False - True - True - True - Redo the last undone action in Message area + True + False + True + True + Redo the last undone action in Message area none - False - top + top True - False + False gtk-redo @@ -1277,33 +1267,33 @@ When decrypting, openssl needs to be told what cipher the input was encrypted wi True - False - 3 - 0 + False + 3 + 0 True - False + False True - True + True True - False + False True - False - True - Choose input file to pass directly to gpg/openssl instead of loading into Message area + False + False + True + Choose input file to pass directly to gpg/openssl instead of loading into Message area This is the way to go when dealing with binary or very large files Drag/drop of files onto this button is supported BUGALERT: The first drag/drop after opening Pyrite will not work! -- do it a 2nd time and all will be fine - False Choose gpg input file... @@ -1317,7 +1307,7 @@ BUGALERT: The first drag/drop after opening Pyrite will not work! -- do it a 2nd True - False + False @@ -1330,13 +1320,14 @@ BUGALERT: The first drag/drop after opening Pyrite will not work! -- do it a 2nd Generate _Text Output - False + False True False - True - False - True - [Not used when decrypting, verifying in gpg-mode] + True + False + False + True + [Not used when decrypting, verifying in gpg-mode] Tells gpg/openssl to output plaintext instead of binary data when encrypting and signing @@ -1347,10 +1338,9 @@ When operating on files directly, this is set based on whether the file is detec OpenSSL Note: This is used for both encryption and decryption in openssl-mode When decrypting, openssl needs to know if the input is base64 or binary (unlike gpg), so when decrypting files in openssl mode, just let Pyrite auto-detect the input file type and auto-set this toggle appropriately - True - False + True True - True + True False @@ -1364,10 +1354,10 @@ When decrypting, openssl needs to know if the input is base64 or binary (unlike True - False + False Inp_ut File For Direct Operation - True - expander_filemode + True + expander_filemode @@ -1377,14 +1367,14 @@ When decrypting, openssl needs to know if the input is base64 or binary (unlike False True - end + end 0 True - False + False @@ -1392,25 +1382,23 @@ When decrypting, openssl needs to know if the input is base64 or binary (unlike False True - end + end 1 True - True - automatic - automatic + True True - True - True - True - Type or paste input text here + True + True + True + Type or paste input text here Drag/drop of text files also supported - word + word @@ -1418,7 +1406,7 @@ Drag/drop of text files also supported True True - end + end 2 @@ -1427,11 +1415,11 @@ Drag/drop of text files also supported True - False + False <i>Message _Input/Output</i> - True - True - textview1 + True + True + textview1 @@ -1450,22 +1438,20 @@ Drag/drop of text files also supported True - False - 3 - 0 + False + 3 + 0 True - True - automatic - automatic + True True False - True - word - False + True + word + False @@ -1473,10 +1459,10 @@ Drag/drop of text files also supported True - False + False <i>Task Status</i> - True - True + True + True @@ -1495,12 +1481,12 @@ Drag/drop of text files also supported True - False + False 1 True - False + False False @@ -1512,20 +1498,20 @@ Drag/drop of text files also supported Add Si_gnature: - False + False True False - True - False - True - [Not used when decrypting, verifying] + True + False + False + True + [Not used when decrypting, verifying] Tells gpg to use your secret key to sign the input This will likely require you to interact with gpg-agent - True - False - True + True + True @@ -1537,9 +1523,10 @@ This will likely require you to interact with gpg-agent False - False - True - [Not used when decrypting, verifying] + False + False + True + [Not used when decrypting, verifying] This allows you to choose the signature type for signing in Sign/Verify mode @@ -1553,7 +1540,6 @@ Detached: Creates a separate signature that does not contain the message -- in order to verify a message/file that has a detached signature, you will need to ensure the message and signature are both in files (with the same name, except for the extra extension of the sig file) in the same directory liststore_sigmodes 0 - False @@ -1571,7 +1557,7 @@ Creates a separate signature that does not contain the message -- in order to ve True - False + False @@ -1583,10 +1569,10 @@ Creates a separate signature that does not contain the message -- in order to ve - False + False D_igest: - True - combobox_digest + True + combobox_digest False @@ -1596,16 +1582,16 @@ Creates a separate signature that does not contain the message -- in order to ve - False - True - [Not used when decrypting, verifying] + False + False + True + [Not used when decrypting, verifying] Configures message digest algorithm (used for hashing message, i.e., creating your signature) With "Default", gpg decides the algorithm based on local system settings, weighing them against the preferences of your secret key liststore_digest 0 - False @@ -1623,7 +1609,7 @@ With "Default", gpg decides the algorithm based on local system settings, weighi True - False + False @@ -1636,17 +1622,17 @@ With "Default", gpg decides the algorithm based on local system settings, weighi Change Default _Key: - False + False True - True - False - True - Tell gpg which secret key to use -- under common circumstances, this will only affect: (1) signing; (2) the calculation of "self" when using the "Enc. To Self" feature + True + False + False + True + Tell gpg which secret key to use -- under common circumstances, this will only affect: (1) signing; (2) the calculation of "self" when using the "Enc. To Self" feature This is only useful if you have multiple secret keys in your keyring - True - False - True + True + True @@ -1657,23 +1643,19 @@ This is only useful if you have multiple secret keys in your keyring - True - True - Key identifier of the non-default key to use for signing + True + True + Key identifier of the non-default key to use for signing Input is passed to gpg "--local-user" option - - 11 - True - etched-in - True - False - gtk-orientation-portrait - True - False - True - True - Clear entry field + + 11 + True + etched-in + False + gtk-orientation-portrait + False + Clear entry field @@ -1693,11 +1675,11 @@ Input is passed to gpg "--local-user" option True - False + False - 16 - False + 16 + False False @@ -1709,7 +1691,7 @@ Input is passed to gpg "--local-user" option True - False + False True diff --git a/ui/preferences.glade b/ui/preferences.glade index e85d77d..84ab862 100644 --- a/ui/preferences.glade +++ b/ui/preferences.glade @@ -1,34 +1,33 @@ - - + 2 50 - 1 - 4 + 1 + 4 2 100 - 1 - 4 + 1 + 4 1 100 100 - 1 - 4 + 1 + 4 True - False + False gtk-justify-fill True - False + False gtk-justify-fill @@ -187,48 +186,48 @@ - False - 12 + False + 12 Pyrite Preferences - center - True - dialog + center + True + dialog True - False + False 12 True - True + True True - False + False True - False - 8 - 5 - 6 + False + 8 + 5 + 6 True - False + False - 2 - 3 - - 10 + 2 + 3 + + 10 True - False + False liststore_major_opmodes @@ -238,32 +237,32 @@ - 4 - 5 - GTK_FILL - - 10 + 4 + 5 + GTK_FILL + + 10 True - False - 0 + False Operation Mode: - True + True + 0 - 3 - 4 - GTK_FILL - + 3 + 4 + GTK_FILL + True - False + False liststore_engines @@ -273,24 +272,24 @@ - 1 - 2 - GTK_FILL - + 1 + 2 + GTK_FILL + True - False - 0 + False Backend Engine Primary Choice: - True + True + 0 - GTK_FILL - - 10 + GTK_FILL + + 10 @@ -303,7 +302,7 @@ True - False + False False @@ -314,106 +313,106 @@ True - False + False True - False + False True - False - 8 - 5 - 2 - 6 - 6 + False + 8 + 5 + 2 + 6 + 6 Advanced - False + False True - True - False + True + False 0 - True + True - 2 - 2 - 3 - GTK_FILL - - 10 + 2 + 2 + 3 + GTK_FILL + + 10 True - False - 0 + False <b>Encrypt/Decrypt Mode Defaults</b> - True + True + 0 - 2 - GTK_FILL - + 2 + GTK_FILL + Encrypt To Self - False + False True - True - False + True + False 0 - True + True - 4 - 5 - GTK_FILL - - 10 + 4 + 5 + GTK_FILL + + 10 Add Signature - False + False True - True - False + True + False 0 - True + True - 1 - 2 - 4 - 5 - GTK_FILL - + 1 + 2 + 4 + 5 + GTK_FILL + True - False + False True - False + False True - False - 0 + False Symmetric Cipher Algorithm: + 0 False @@ -432,7 +431,7 @@ True - False + False liststore_ciphers @@ -449,26 +448,26 @@ - 2 - 3 - 4 - + 2 + 3 + 4 + True - False + False True - False + False True - False - 0 + False Initial Encryption Type: + 0 False @@ -487,7 +486,7 @@ True - False + False liststore_enctypes @@ -505,10 +504,10 @@ - 2 - 1 - 2 - + 2 + 1 + 2 + @@ -521,7 +520,7 @@ True - False + False False @@ -532,45 +531,45 @@ True - False - 8 - 5 - 2 - 6 - 6 + False + 8 + 5 + 2 + 6 + 6 True - False - 0 + False <b>Mode-Independent Defaults</b> - True + True + 0 - 2 - GTK_FILL - + 2 + GTK_FILL + True - False - 0 + False Digest Hash Algorithm: + 0 - 1 - 2 - GTK_FILL - - 10 + 1 + 2 + GTK_FILL + + 10 True - False + False liststore_digest @@ -580,75 +579,72 @@ - 1 - 2 - 1 - 2 - GTK_FILL - + 1 + 2 + 1 + 2 + GTK_FILL + Change Default Key: - False + False True - True - False + True + False 0 - True + True - 2 - 3 - GTK_FILL - - 10 + 2 + 3 + GTK_FILL + + 10 True - True - - 13 - True - etched-in - True - False - False - False - True - True + True + + 13 + True + etched-in + False + False + False - 1 - 2 - 2 - 3 - GTK_FILL - + 1 + 2 + 2 + 3 + GTK_FILL + True - False - 0 + False Text Output With Files: + 0 - 3 - 4 - GTK_FILL - - 10 + 3 + 4 + GTK_FILL + + 10 True - False + False liststore_txtoutput @@ -658,31 +654,31 @@ - 1 - 2 - 3 - 4 - GTK_FILL - + 1 + 2 + 3 + 4 + GTK_FILL + Expand Input File Expander - False + False True - True - False + True + False 0 - True + True - 2 - 4 - 5 - GTK_FILL - - 10 + 2 + 4 + 5 + GTK_FILL + + 10 @@ -702,7 +698,7 @@ True - False + False False @@ -713,82 +709,82 @@ True - False + False True - False - 8 - 4 - 2 - 6 + False + 8 + 4 + 2 + 6 True - False - 0 + False <b>Sign/Verify Mode Defaults</b> - True + True + 0 - 2 - GTK_FILL - + 2 + GTK_FILL + Choose Output Filenames - False + False True - True - False + True + False 0 - True + True - 2 - 1 - 2 - GTK_FILL - - 10 + 2 + 1 + 2 + GTK_FILL + + 10 True - False - 0 + False Initial Sign Type For Text: + 0 - 2 - 3 - GTK_FILL - - 10 + 2 + 3 + GTK_FILL + + 10 True - False - 0 + False Initial Sign Type For Files: + 0 - 3 - 4 - GTK_FILL - - 10 + 3 + 4 + GTK_FILL + + 10 True - False + False liststore_sigmodes @@ -798,18 +794,18 @@ - 1 - 2 - 2 - 3 - GTK_FILL - + 1 + 2 + 2 + 3 + GTK_FILL + True - False + False liststore_sigmodes @@ -819,12 +815,12 @@ - 1 - 2 - 3 - 4 - GTK_FILL - + 1 + 2 + 3 + 4 + GTK_FILL + @@ -837,7 +833,7 @@ True - False + False False @@ -848,62 +844,62 @@ True - False - 8 - 7 - 2 - 6 - 6 + False + 8 + 7 + 2 + 6 + 6 True - False - 0 + False <b>Display Options</b> - True + True + 0 - 2 - GTK_FILL - + 2 + GTK_FILL + Text Wrapping - False + False True - True - False + True + False 0 - True + True - 2 - 2 - 3 - GTK_FILL - - 10 + 2 + 2 + 3 + GTK_FILL + + 10 True - False + False True - False + False Task Status - False + False True - True - False + True + False 0 - True + True False @@ -922,12 +918,12 @@ Verbose Task Status - False + False True - True - False + True + False 0 - True + True False @@ -938,27 +934,27 @@ - 2 - 1 - 2 - + 2 + 1 + 2 + True - False + False True - False + False 6 True - False - 0 + False Message: + 0 False @@ -969,17 +965,14 @@ True - True - - etched-in - True - False - False - False - True - True + True + + etched-in + False + False + False adj_msgfntsz - True + True True True @@ -1000,14 +993,14 @@ True - False + False 6 True - False - 0 + False Status: + 0 False @@ -1018,17 +1011,14 @@ True - True - - etched-in - True - False - False - False - True - True + True + + etched-in + False + False + False adj_errfntsz - True + True True True @@ -1047,56 +1037,56 @@ - 2 - 5 - 6 - + 2 + 5 + 6 + True - False - 0 + False Font Sizes: + 0 - 2 - 4 - 5 - GTK_FILL - - 10 + 2 + 4 + 5 + GTK_FILL + + 10 Show Opacity Slider - False + False True - True - False + True + False 0 - True + True - 3 - 4 - GTK_FILL - - 10 + 3 + 4 + GTK_FILL + + 10 True - False + False True - False - 0 + False Opacity: + 0 False @@ -1107,17 +1097,14 @@ True - True - - etched-in - True - False - False - False - True - True + True + + etched-in + False + False + False adj_opacity - True + True True @@ -1128,22 +1115,22 @@ - 1 - 2 - 3 - 4 + 1 + 2 + 3 + 4 True - False + False True - False - 0 + False Message Area Colors: + 0 False @@ -1155,16 +1142,16 @@ True - False + False 4 - False + False True - True - True - True - Foreground (Text) + True + True + True + Foreground (Text) Pick a Foreground (Text) Color #000000000000 @@ -1176,12 +1163,12 @@ - False + False True - True - True - True - Background + True + True + True + Background Pick a Background Color #ffffffffffff @@ -1200,11 +1187,11 @@ - 2 - 6 - 7 - GTK_FILL - GTK_FILL + 2 + 6 + 7 + GTK_FILL + GTK_FILL @@ -1233,279 +1220,261 @@ True - False + False Application Startup Defaults - False + False True False - False - 8 - 8 - 2 - 4 - 6 + False + 8 + 8 + 2 + 4 + 6 True - False - 0 + False <b>Custom GnuPG Arguments</b> - True + True + 0 - 2 - GTK_FILL - + 2 + GTK_FILL + When Encrypting: - False + False True - True - False + True + False 0 - True + True - 1 - 2 - GTK_FILL - - 12 + 1 + 2 + GTK_FILL + + 12 True - True - - True - False - False - True - True + True + + False + False - 1 - 2 - 1 - 2 - GTK_FILL - + 1 + 2 + 1 + 2 + GTK_FILL + When Decrypting: - False + False True - True - False + True + False 0 - True + True - 2 - 3 - GTK_FILL - - 12 + 2 + 3 + GTK_FILL + + 12 True - True - - True - False - False - True - True + True + + False + False - 1 - 2 - 2 - 3 - GTK_FILL - + 1 + 2 + 2 + 3 + GTK_FILL + When Sign-Only: - False + False True - True - False + True + False 0 - True + True - 3 - 4 - GTK_FILL - - 12 + 3 + 4 + GTK_FILL + + 12 True - True - - True - False - False - True - True + True + + False + False - 1 - 2 - 3 - 4 - GTK_FILL - + 1 + 2 + 3 + 4 + GTK_FILL + When Verifying: - False + False True - True - False + True + False 0 - True + True - 4 - 5 - GTK_FILL - - 12 + 4 + 5 + GTK_FILL + + 12 True - True - - True - False - False - True - True + True + + False + False - 1 - 2 - 4 - 5 - GTK_FILL - + 1 + 2 + 4 + 5 + GTK_FILL + True - False - 0 + False <b>Custom OpenSSL Arguments</b> - True + True + 0 - 2 - 5 - 6 - GTK_FILL - + 2 + 5 + 6 + GTK_FILL + When Encrypting: - False + False True - True - False + True + False 0 - True + True - 6 - 7 - GTK_FILL - - 12 + 6 + 7 + GTK_FILL + + 12 True - True - - True - False - False - True - True + True + + False + False - 1 - 2 - 6 - 7 - GTK_FILL - + 1 + 2 + 6 + 7 + GTK_FILL + True - True - - True - False - False - True - True + True + + False + False - 1 - 2 - 7 - 8 - GTK_FILL - + 1 + 2 + 7 + 8 + GTK_FILL + When Decrypting: - False + False True - True - False + True + False 0 - True + True - 7 - 8 - GTK_FILL - - 12 + 7 + 8 + GTK_FILL + + 12 @@ -1516,12 +1485,12 @@ True - False + False Advanced Options 1 - False + False @@ -1534,21 +1503,21 @@ True - False + False True - False - start + False + start Defaults - False + False True - True - True - True - Reset all preferences to built-in defaults + True + True + True + Reset all preferences to built-in defaults image2 @@ -1561,14 +1530,14 @@ gtk-revert-to-saved - False + False True False - True - True - True - Revert all preferences to values set in user preferences file - True + True + True + True + Revert all preferences to values set in user preferences file + True @@ -1590,18 +1559,18 @@ True - False - end + False + end gtk-cancel - False + False True - True - True - True - Cancel without making any changes - True + True + True + True + Cancel without making any changes + True @@ -1613,13 +1582,13 @@ gtk-save - False + False True - True - True - True - Save all settings out to a pref file in your home, but don't change anything in the current session - True + True + True + True + Save all settings out to a pref file in your home, but don't change anything in the current session + True False @@ -1630,13 +1599,13 @@ gtk-apply - False + False True - True - True - True - Save settings to a user pref file and apply them to current session - True + True + True + True + Save settings to a user pref file and apply them to current session + True False @@ -1648,7 +1617,7 @@ False True - end + end 2 @@ -1662,8 +1631,8 @@ True - False - immediate + False + immediate From 2b82eed8c71e2dd3d8a132cd1f78694eb4b1bd5f Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sun, 1 Oct 2023 22:18:52 +0300 Subject: [PATCH 14/34] README.md update --- README.md | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f401e6b..6bda33b 100644 --- a/README.md +++ b/README.md @@ -6,33 +6,26 @@ Pyrite - Python/GTK+ encryption/signing frontend for GnuPG and OpenSSL FEDORA/RHEL7 INSTALLATION ------------------------- -There's an RPM (and yum repository) @ [people.redhat.com/rsawhill/rpms](http://people.redhat.com/rsawhill/rpms/). To configure it and install Pyrite, simply run the following as root: +There's an RPM (and yum repository) @ [people.redhat.com/rsawhill/rpms](https://people.redhat.com/rsawhill/rpms/). To configure it and install Pyrite, simply run the following as root: ``` -yum install http://people.redhat.com/rsawhill/rpms/latest-rsawaroha-release.rpm +yum install https://people.redhat.com/rsawhill/rpms/latest-rsawaroha-release.rpm yum install pyrite ``` Requirements and package names: -- gtk2 >= v2.24: `gtk2` -- python2 >= v2.7: `python` -- pygtk: `pygtk2` -- gpg/openssl: `gnupg2` or `gnupg` or `openssl` - -*As per above, Pyrite is not compatible with RHEL6.* +- GTK3: `gtk3` +- Python3: `python3` +- `python3-gi`, `python3-gi-cairo` +- PGP `gnupg2` or OpenSSL `openssl` DEBIAN/UBUNTU/OTHER LINUX INSTALLATION -------------------------------------- -There is a simple interactive shell installer. Before using it, ensure you have the following on your Linux system (Ubuntu package names): - -- gtk2 >= v2.24: `libgtk2.0-bin` -- python2 >= v2.7: `python` -- pygtk: `python-gtk2` -- gpg/openssl: `gnupg2` or (`gnupg` and `gnupg-agent`) or `openssl` +There is a simple interactive shell installer. -If requirements are met, clone the Pyrite repo with `git clone git://github.com/ryran/pyrite.git` **OR** [download a zip of the source](https://github.com/ryran/pyrite/archive/master.zip). +If requirements are met, clone the Pyrite repo with `git clone https://github.com/ryran/pyrite.git` **OR** [download a zip of the source](https://github.com/ryran/pyrite/archive/master.zip). From the root source folder execute the interactive `INSTALL` script. @@ -98,7 +91,7 @@ If you find yourself wondering about a particular feature, just hover your mouse BUGS ---------- -1) After launching Pyrite, the **first** drag/drop of a file onto the *Input File For Direct Operation* GtkFileChooserButton fails. After that the button works properly. I've been seeking out expertise on this weird bug but I haven't gotten anywhere. If you have any hints, hit me up, or check out [my post about it on stackoverflow](http://stackoverflow.com/questions/9047844/pygtk-troubles-with-drag-and-drop-file-to-gtkfilechooserbutton). +1) After launching Pyrite, the **first** drag/drop of a file onto the *Input File For Direct Operation* GtkFileChooserButton fails. After that the button works properly. I've been seeking out expertise on this weird bug but I haven't gotten anywhere. If you have any hints, hit me up, or check out [my post about it on stackoverflow](https://stackoverflow.com/questions/9047844/pygtk-troubles-with-drag-and-drop-file-to-gtkfilechooserbutton). 2) No undo. It wasn't a top priority at the beginning, but I think it's pretty essential for an application that basically contains a text editor to have an undo/redo stack. I'll do it eventually. @@ -138,7 +131,7 @@ the Free Software Foundation, either version 3 of the License, or This program 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 @[gnu.org/licenses/gpl.html](http://gnu.org/licenses/gpl.html>) for more details. +General Public License @[gnu.org/licenses/gpl.html](https://gnu.org/licenses/gpl.html>) for more details. @@ -148,7 +141,7 @@ General Public License @[gnu.org/licenses/gpl.html](http://gnu.org/licenses/gpl. Hmmmm. You're still here? -Oh. You must be wondering why the name [*Pyrite*](http://en.wikipedia.org/wiki/Pyrite), eh? +Oh. You must be wondering why the name [*Pyrite*](https://en.wikipedia.org/wiki/Pyrite), eh? Well, I'll let my friend River--who came up with the name--explain it to you: From 38cc10800874123e7e52ed31ff238dd920e13a47 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Tue, 24 Oct 2023 23:36:52 +0300 Subject: [PATCH 15/34] Automatically reformat Also remove long ----- lines The -*- coding: utf-8 -*- is also not needed --- modules/cfg.py | 36 +- modules/core.py | 883 ++++++++++++++++++------------------- modules/crypt_interface.py | 233 +++++----- modules/messages.py | 266 ++++++----- modules/prefs.py | 238 +++++----- pyrite.py | 10 +- 6 files changed, 800 insertions(+), 866 deletions(-) diff --git a/modules/cfg.py b/modules/cfg.py index befd1e8..d618a38 100644 --- a/modules/cfg.py +++ b/modules/cfg.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # This file is part of Pyrite. # Last file mod: 2013/09/15 @@ -20,30 +19,33 @@ # # You should have received a copy of the GNU General Public License # along with Pyrite. If not, see . -# -#------------------------------------------------------------------------------ import gi + gi.require_version("Gtk", "3.0") from gi.repository import Gtk from os import getenv # Important variables -VERSION = 'v1.0.2' -ASSETDIR = './' -USERPREF_FILE = getenv('HOME') + '/.pyrite' -USERPREF_FORMAT_INFO = {'version':'Must6fa'} +VERSION = 'v1.0.2' +ASSETDIR = './' +USERPREF_FILE = getenv('HOME') + '/.pyrite' +USERPREF_FORMAT_INFO = {'version': 'Must6fa'} # List of possible Infobar message types -MSGTYPES = [0, - Gtk.MessageType.INFO, # 1 - Gtk.MessageType.QUESTION, # 2 - Gtk.MessageType.WARNING, # 3 - Gtk.MessageType.ERROR] # 4 +MSGTYPES = [ + 0, + Gtk.MessageType.INFO, # 1 + Gtk.MessageType.QUESTION, # 2 + Gtk.MessageType.WARNING, # 3 + Gtk.MessageType.ERROR # 4 +] # List of possible images to show in Infobar -IMGTYPES = [Gtk.STOCK_APPLY, # 0 - Gtk.STOCK_DIALOG_INFO, # 1 - Gtk.STOCK_DIALOG_QUESTION, # 2 - Gtk.STOCK_DIALOG_WARNING, # 3 - Gtk.STOCK_DIALOG_ERROR] # 4 +IMGTYPES = [ + Gtk.STOCK_APPLY, # 0 + Gtk.STOCK_DIALOG_INFO, # 1 + Gtk.STOCK_DIALOG_QUESTION, # 2 + Gtk.STOCK_DIALOG_WARNING, # 3 + Gtk.STOCK_DIALOG_ERROR # 4 +] diff --git a/modules/core.py b/modules/core.py index 9bffbe9..011fc42 100644 --- a/modules/core.py +++ b/modules/core.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # This file is part of Pyrite. # Last file mod: 2013/09/15 @@ -20,11 +19,10 @@ # # You should have received a copy of the GNU General Public License # along with Pyrite. If not, see . -# -#------------------------------------------------------------------------------ # StdLib: import gi + gi.require_version('GLib', '2.0') gi.require_version('Gdk', '3.0') gi.require_version('Gtk', '3.0') @@ -51,15 +49,13 @@ from .messages import MESSAGE_DICT # Important variables -SIGSTOP, SIGCONT = 19, 18 -TARGET_TYPE_URI_LIST = 80 - +SIGSTOP, SIGCONT = 19, 18 +TARGET_TYPE_URI_LIST = 80 class Pyrite: """Display GTK+ window to interact with gpg or openssl via Xface object.""" - - + def show_errmsg(self, msg, dialog=Gtk.MessageType.ERROR, parent=None): """Display msg with GtkMessageDialog.""" d = Gtk.MessageDialog( @@ -70,98 +66,98 @@ def show_errmsg(self, msg, dialog=Gtk.MessageType.ERROR, parent=None): ) d.run() d.destroy() - - + def __init__(self, cmdlineargs): - """Build GUI interface from XML, etc.""" - + """Build GUI interface from XML, etc.""" + # Use GtkBuilder to build our GUI from the XML file builder = Gtk.Builder() - try: builder.add_from_file(cfg.ASSETDIR + 'ui/main.glade') + try: + builder.add_from_file(cfg.ASSETDIR + 'ui/main.glade') except: self.show_errmsg( "Problem loading GtkBuilder UI definition file at:\n " + cfg.ASSETDIR + "ui/main.glade\nCannot continue.") raise - - #--------------------------------------------------------- GET WIDGETS! + + # GET WIDGETS! # Main window - self.g_window = builder.get_object('window1') + self.g_window = builder.get_object('window1') # Toolbars - self.g_maintoolbar = builder.get_object('hbox1') - self.g_modetoolbar = builder.get_object('hbox2') - self.g_enctoolbar = builder.get_object('hbox3') - self.g_sigtoolbar = builder.get_object('hbox4') + self.g_maintoolbar = builder.get_object('hbox1') + self.g_modetoolbar = builder.get_object('hbox2') + self.g_enctoolbar = builder.get_object('hbox3') + self.g_sigtoolbar = builder.get_object('hbox4') # Menu items - self.g_mclear = builder.get_object('mnu_clear') - self.g_mopen = builder.get_object('mnu_open') - self.g_msave = builder.get_object('mnu_save') - self.g_mengine = builder.get_object('mnu_switchengine') - self.g_mcut = builder.get_object('mnu_cut') - self.g_mcopy = builder.get_object('mnu_copy') - self.g_mpaste = builder.get_object('mnu_paste') - self.g_mprefs = builder.get_object('mnu_preferences') - self.g_wrap = builder.get_object('toggle_wordwrap') - self.g_taskstatus = builder.get_object('toggle_taskstatus') - self.g_taskverbose = builder.get_object('toggle_gpgverbose') + self.g_mclear = builder.get_object('mnu_clear') + self.g_mopen = builder.get_object('mnu_open') + self.g_msave = builder.get_object('mnu_save') + self.g_mengine = builder.get_object('mnu_switchengine') + self.g_mcut = builder.get_object('mnu_cut') + self.g_mcopy = builder.get_object('mnu_copy') + self.g_mpaste = builder.get_object('mnu_paste') + self.g_mprefs = builder.get_object('mnu_preferences') + self.g_wrap = builder.get_object('toggle_wordwrap') + self.g_taskstatus = builder.get_object('toggle_taskstatus') + self.g_taskverbose = builder.get_object('toggle_gpgverbose') # Top action toolbar - self.g_encrypt = builder.get_object('btn_encrypt') - self.g_decrypt = builder.get_object('btn_decrypt') - self.g_bclear = builder.get_object('btn_clear') - self.g_progbar = builder.get_object('progressbar') - self.g_cancel = builder.get_object('btn_cancel') - self.g_pause = builder.get_object('btn_pause') - self.g_slider = builder.get_object('opacity_slider') + self.g_encrypt = builder.get_object('btn_encrypt') + self.g_decrypt = builder.get_object('btn_decrypt') + self.g_bclear = builder.get_object('btn_clear') + self.g_progbar = builder.get_object('progressbar') + self.g_cancel = builder.get_object('btn_cancel') + self.g_pause = builder.get_object('btn_pause') + self.g_slider = builder.get_object('opacity_slider') # Mode-setting toolbar - self.g_signverify = builder.get_object('toggle_mode_signverify') - self.g_chk_outfile = builder.get_object('toggle_sign_chooseoutput') - self.g_encdec = builder.get_object('toggle_mode_encdec') - self.g_symmetric = builder.get_object('toggle_mode_symmetric') - self.g_asymmetric = builder.get_object('toggle_mode_asymmetric') - self.g_advanced = builder.get_object('toggle_advanced') + self.g_signverify = builder.get_object('toggle_mode_signverify') + self.g_chk_outfile = builder.get_object('toggle_sign_chooseoutput') + self.g_encdec = builder.get_object('toggle_mode_encdec') + self.g_symmetric = builder.get_object('toggle_mode_symmetric') + self.g_asymmetric = builder.get_object('toggle_mode_asymmetric') + self.g_advanced = builder.get_object('toggle_advanced') # Encryption toolbar - self.g_passlabel = builder.get_object('label_entry_pass') - self.g_pass = builder.get_object('entry_pass') - self.g_reciplabel = builder.get_object('label_entry_recip') - self.g_recip = builder.get_object('entry_recip') - self.g_enctoself = builder.get_object('toggle_enctoself') - self.g_cipherlabel = builder.get_object('label_combobox_cipher') - self.g_cipher = builder.get_object('combobox_cipher') + self.g_passlabel = builder.get_object('label_entry_pass') + self.g_pass = builder.get_object('entry_pass') + self.g_reciplabel = builder.get_object('label_entry_recip') + self.g_recip = builder.get_object('entry_recip') + self.g_enctoself = builder.get_object('toggle_enctoself') + self.g_cipherlabel = builder.get_object('label_combobox_cipher') + self.g_cipher = builder.get_object('combobox_cipher') # Middle input area - self.g_bopen = builder.get_object('btn_open') - self.g_bsave = builder.get_object('btn_save') - self.g_bcopyall = builder.get_object('btn_copyall') - self.g_msgtxtview = builder.get_object('textview1') - self.buff = self.g_msgtxtview.get_buffer() - self.vbox_ibar = builder.get_object('vbox_ibar') - self.vbox_ibar2 = builder.get_object('vbox_ibar2') - self.g_expander = builder.get_object('expander_filemode') - self.g_chooserbtn = builder.get_object('btn_filechooser') - self.g_plaintext = builder.get_object('toggle_plaintext') - self.g_frame2 = builder.get_object('frame2') - self.g_errtxtview = builder.get_object('textview2') - self.buff2 = self.g_errtxtview.get_buffer() + self.g_bopen = builder.get_object('btn_open') + self.g_bsave = builder.get_object('btn_save') + self.g_bcopyall = builder.get_object('btn_copyall') + self.g_msgtxtview = builder.get_object('textview1') + self.buff = self.g_msgtxtview.get_buffer() + self.vbox_ibar = builder.get_object('vbox_ibar') + self.vbox_ibar2 = builder.get_object('vbox_ibar2') + self.g_expander = builder.get_object('expander_filemode') + self.g_chooserbtn = builder.get_object('btn_filechooser') + self.g_plaintext = builder.get_object('toggle_plaintext') + self.g_frame2 = builder.get_object('frame2') + self.g_errtxtview = builder.get_object('textview2') + self.buff2 = self.g_errtxtview.get_buffer() # Signing toolbar - self.g_signature = builder.get_object('toggle_signature') - self.g_sigmode = builder.get_object('combobox_sigmode') - self.g_digestlabel = builder.get_object('label_combobox_digest') - self.g_digest = builder.get_object('combobox_digest') - self.g_chk_defkey = builder.get_object('toggle_defaultkey') - self.g_defaultkey = builder.get_object('entry_defaultkey') + self.g_signature = builder.get_object('toggle_signature') + self.g_sigmode = builder.get_object('combobox_sigmode') + self.g_digestlabel = builder.get_object('label_combobox_digest') + self.g_digest = builder.get_object('combobox_digest') + self.g_chk_defkey = builder.get_object('toggle_defaultkey') + self.g_defaultkey = builder.get_object('entry_defaultkey') # Statusbar - self.g_statusbar = builder.get_object('statusbar') + self.g_statusbar = builder.get_object('statusbar') self.g_activityspin = builder.get_object('spinner1') - + # Set app icon to something halfway-decent Gtk.Window.set_default_icon_name("dialog-password") - + # Connect signals builder.connect_signals(self) - + # Other class attributes - self.ib_filemode = None - self.engine = 'missing_backend' - self.quiting = False + self.ib_filemode = None + self.engine = 'missing_backend' + self.quiting = False self.working_widgets_filemode = [ self.g_mclear, self.g_mprefs, self.g_encrypt, self.g_decrypt, self.g_bclear, self.g_modetoolbar, self.g_enctoolbar, self.g_expander, self.g_sigtoolbar] @@ -170,37 +166,37 @@ def __init__(self, cmdlineargs): self.g_modetoolbar, self.g_enctoolbar, self.g_expander, self.g_sigtoolbar, self.g_mengine, self.g_bcopyall, self.g_bopen, self.g_mopen, self.g_bsave, self.g_msave, self.g_mcut, self.g_mcopy, self.g_mpaste] - + # Initialize main Statusbar self.status = self.g_statusbar.get_context_id('main') self.g_statusbar.push(self.status, "Enter message to encrypt/decrypt") - + # Sensitivity for these GtkEntrys not defaulted to False in xml because # that makes their icons stay insensitive-looking forever - self.g_pass.set_sensitive (False) - self.g_recip.set_sensitive (False) + self.g_pass.set_sensitive(False) + self.g_recip.set_sensitive(False) - #------------------------------ LOAD PREFERENCES AND SET WIDGET STATES! + # ------------------------------ LOAD PREFERENCES AND SET WIDGET STATES! self.preferences = prefs.Preferences() - + # Make a clone of preferences dictionary self.p = self.preferences.p - + # Launch gpg/openssl interface if cmdlineargs and cmdlineargs.backend: backend = cmdlineargs.backend else: backend = None - + self.instantiate_xface(preferred=backend, startup=True) - - #------------------------------------------------ DRAG AND DROP FUNNESS - dnd_list = [ ( 'text/uri-list', 0, TARGET_TYPE_URI_LIST ) ] + + # DRAG AND DROP FUNNESS + dnd_list = [('text/uri-list', 0, TARGET_TYPE_URI_LIST)] # self.g_msgtxtview.drag_dest_set( # Gtk.DestDefaults.MOTION | Gtk.DestDefaults.HIGHLIGHT, # dnd_list, Gdk.DragAction.COPY) - - #---------------------------------------------------- CMDLINE ARGUMENTS + + # CMDLINE ARGUMENTS if cmdlineargs: a = cmdlineargs @@ -209,9 +205,11 @@ def __init__(self, cmdlineargs): if a.direct_file: self.g_chooserbtn.set_filename(a.input) self.g_expander.set_expanded(True) - elif a.text_input: self.buff.set_text(a.input) - else: self.open_in_txtview(a.input) - + elif a.text_input: + self.buff.set_text(a.input) + else: + self.open_in_txtview(a.input) + if self.engine not in 'OpenSSL': if a.recipients: self.g_recip.set_text(a.recipients) @@ -226,63 +224,62 @@ def __init__(self, cmdlineargs): self.g_encdec.set_active(True) elif a.signverify: self.g_signverify.set_active(True) - - #--------------------------------------------------- OUR LOVELY COMM DEVICE + + # OUR LOVELY COMM DEVICE def infobar(self, id, filename=None, customtext=None, vbox=None): """Popup a new auto-hiding InfoBar.""" - + # Find the needed dictionary inside our message dict, by id MSG = MESSAGE_DICT[id] # Use value from MSG type & icon to lookup Gtk constant, e.g. Gtk.MessageType.INFO - msgtype = cfg.MSGTYPES[ MSG['type'] ] - imgtype = cfg.IMGTYPES[ MSG['icon'] ] + msgtype = cfg.MSGTYPES[MSG['type']] + imgtype = cfg.IMGTYPES[MSG['icon']] # Replace variables in message text & change text color message = ("" + MSG['text'].format(filename=filename, customtext=customtext) + "") - + # Now that we have all the data we need, START creating! ibar = Gtk.InfoBar() ibar.set_message_type(msgtype) if vbox: # If specific vbox requested: assume ibar for filemode, add cancel button - ibar.add_button (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) - ibar.connect ('response', self.cleanup_filemode) + ibar.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL) + ibar.connect('response', self.cleanup_filemode) else: # If no specific vbox requested: do normal ibar at the top of message area - vbox = self.vbox_ibar - ibar.add_button (Gtk.STOCK_OK, Gtk.ResponseType.OK) - ibar.connect ('response', lambda *args: ibar.destroy()) - vbox.pack_end (ibar, False, False, 0) - content = ibar.get_content_area() - img = Gtk.Image() - img.set_from_stock (imgtype, Gtk.IconSize.LARGE_TOOLBAR) - content.pack_start (img, False, False, 0) - img.show () - label = Gtk.Label() - label.set_markup (message) - content.pack_start (label, False, False, 0) - label.show () + vbox = self.vbox_ibar + ibar.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK) + ibar.connect('response', lambda *args: ibar.destroy()) + vbox.pack_end(ibar, False, False, 0) + content = ibar.get_content_area() + img = Gtk.Image() + img.set_from_stock(imgtype, Gtk.IconSize.LARGE_TOOLBAR) + content.pack_start(img, False, False, 0) + img.show() + label = Gtk.Label() + label.set_markup(message) + content.pack_start(label, False, False, 0) + label.show() # FIXME: Why doesn't Esc trigger this close signal? - ibar.connect ('close', lambda *args: ibar.destroy()) + ibar.connect('close', lambda *args: ibar.destroy()) ibar.show() if MSG['timeout'] > 0: GLib.timeout_add_seconds(MSG['timeout'], ibar.destroy) return ibar - - - #----------------------------------------------------- BRING UP GPG/OPENSSL + + # BRING UP GPG/OPENSSL def instantiate_xface(self, preferred=None, startup=False): - """Instantiate Gpg or Openssl interface.""" - + """Instantiate Gpg or Openssl interface.""" + b = ['gpg2', 'gpg', 'openssl'] # self.p['backend'] contains 0, 1, or 2, corresponding to the above items in b # Desired: convert the number setting to the human-readable name and store as b b = b[self.p['backend']] - + # If we weren't passed preferred argument, set desired interface to backend pref if not preferred: preferred = b - + # Loading gpg def gpg(backend_pref=b, fallback=False): self.x = crypt_interface.Gpg(firstchoice=backend_pref) @@ -291,7 +288,7 @@ def gpg(backend_pref=b, fallback=False): if fallback: self.g_mengine.set_sensitive(False) self.infobar('engine_openssl_missing') - + # Loading openssl def openssl(fallback=False): self.x = crypt_interface.Openssl() @@ -302,16 +299,18 @@ def openssl(fallback=False): self.infobar('engine_gpg_missing') else: self.infobar('engine_openssl_notice') - + # Setup for neutered-run (when missing all backends) def err_allmissing(): self.infobar('engine_all_missing') self.g_mengine.set_sensitive(False) for w in self.g_encrypt, self.g_decrypt: w.set_sensitive(False) + class dummy: pass + self.x = dummy() self.x.io = dict(stdin='', stdout='', gstatus=0, infile=0, outfile=0) - + # Get it done! if preferred in 'openssl': # If loading openssl, try that first, then fallback to gpg @@ -331,55 +330,54 @@ class dummy: pass openssl(fallback=True) except: err_allmissing() - + self.g_window.set_title("Pyrite [{}]".format(self.engine)) - + self.buff2.set_text("Any output generated from calls to {} will be " "displayed here.\n\nIn the View menu you can change " "the verbosity level, hide this pane, or simply change " "the font size.".format(self.engine.lower())) - + self.set_defaults_from_prefs(startup) - - - #--------------------------------------------- SET OPMODES, ETC, FROM PREFS + + # SET OPMODES, ETC, FROM PREFS def set_defaults_from_prefs(self, startup=False): """Set window toggle states via preferences.""" - + if self.p['enctype'] == 0: - self.g_symmetric.set_active (True) + self.g_symmetric.set_active(True) elif self.p['enctype'] == 1: - self.g_asymmetric.set_active (True) + self.g_asymmetric.set_active(True) elif self.p['enctype'] == 2: - self.g_advanced.set_active (True) - self.g_symmetric.set_active (True) - self.g_asymmetric.set_active (True) - + self.g_advanced.set_active(True) + self.g_symmetric.set_active(True) + self.g_asymmetric.set_active(True) + if self.p['advanced']: - self.g_advanced.set_active (True) - + self.g_advanced.set_active(True) + if self.p['advanced'] or self.p['enctype'] > 0: if self.p['addsig']: - self.g_signature.set_active (True) + self.g_signature.set_active(True) if self.p['enctoself']: - self.g_enctoself.set_active (True) - + self.g_enctoself.set_active(True) + if self.p['opmode']: - self.g_signverify.set_active (True) + self.g_signverify.set_active(True) if not self.g_expander.get_expanded(): - self.g_expander.set_expanded (self.p['expander']) - - self.g_cipher.set_active (self.p['cipher']) - self.g_digest.set_active (self.p['digest']) - self.g_chk_defkey.set_active (self.p['defkey']) - self.g_defaultkey.set_text (self.p['defkeytxt']) - + self.g_expander.set_expanded(self.p['expander']) + + self.g_cipher.set_active(self.p['cipher']) + self.g_digest.set_active(self.p['digest']) + self.g_chk_defkey.set_active(self.p['defkey']) + self.g_defaultkey.set_text(self.p['defkeytxt']) + if startup: - self.g_taskstatus.set_active (self.p['taskstatus']) - self.g_taskverbose.set_active (self.p['verbose']) - self.g_wrap.set_active (self.p['wrap']) - + self.g_taskstatus.set_active(self.p['taskstatus']) + self.g_taskverbose.set_active(self.p['verbose']) + self.g_wrap.set_active(self.p['wrap']) + # Set TextView fonts, sizes, and colors self.g_msgtxtview.modify_font( Pango.FontDescription("monospace {}".format(self.p['msgfntsize']))) @@ -394,44 +392,43 @@ def set_defaults_from_prefs(self, startup=False): Gtk.StateType.NORMAL, bg_color) self.g_msgtxtview.modify_text( Gtk.StateType.NORMAL, fg_color) - + if self.p['opc_slider']: - self.g_slider.set_range (0, 100) - self.g_slider.set_value (self.p['opacity']) - self.g_slider.set_tooltip_text ("Change window opacity (current:{}%)".format(self.p['opacity'])) - self.g_slider.set_visible (True) + self.g_slider.set_range(0, 100) + self.g_slider.set_value(self.p['opacity']) + self.g_slider.set_tooltip_text("Change window opacity (current:{}%)".format(self.p['opacity'])) + self.g_slider.set_visible(True) else: - self.g_window.set_opacity(self.p['opacity']/100.0) - + self.g_window.set_opacity(self.p['opacity'] / 100.0) + # These are all the widgets that can't be used in openssl mode def setsensitive_gpgwidgets(x=True): - self.g_signverify.set_sensitive (x) - self.g_symmetric.set_sensitive (x) - self.g_asymmetric.set_sensitive (x) - self.g_advanced.set_sensitive (x) - self.g_chk_defkey.set_sensitive (x) - self.g_taskverbose.set_visible (x) # OpenSSL doesn't have verbosity - + self.g_signverify.set_sensitive(x) + self.g_symmetric.set_sensitive(x) + self.g_asymmetric.set_sensitive(x) + self.g_advanced.set_sensitive(x) + self.g_chk_defkey.set_sensitive(x) + self.g_taskverbose.set_visible(x) # OpenSSL doesn't have verbosity + if self.engine in 'OpenSSL': - self.g_encdec.set_active (True) - self.g_symmetric.set_active (True) - self.g_advanced.set_active (False) - self.g_chk_defkey.set_active (False) + self.g_encdec.set_active(True) + self.g_symmetric.set_active(True) + self.g_advanced.set_active(False) + self.g_chk_defkey.set_active(False) if startup or self.g_cipher.get_active() in {0, 2}: # If starting up, or current cipher set to 'Default' or 'Twofish' if self.p['cipher'] not in {0, 2}: # Set cipher to preference unless pref is 'Default' or 'Twofish' - self.g_cipher.set_active (self.p['cipher']) + self.g_cipher.set_active(self.p['cipher']) else: # Otherwise, set to AES - self.g_cipher.set_active (1) - setsensitive_gpgwidgets (False) + self.g_cipher.set_active(1) + setsensitive_gpgwidgets(False) else: - setsensitive_gpgwidgets (True) - - - #--------------------------------------------------------- HELPER FUNCTIONS - + setsensitive_gpgwidgets(True) + + # HELPER FUNCTIONS + def fix_msgtxtviewcolor(self, sensitive): """Change Message area text to black when TextView insensitive.""" if sensitive: @@ -444,21 +441,19 @@ def fix_msgtxtviewcolor(self, sensitive): fg_color.parse('black') self.g_msgtxtview.modify_text( Gtk.StateType.NORMAL, fg_color) - - + def get_file_path_from_dnd_dropped_uri(self, uri): path = '' - if uri.startswith('file:\\\\\\'): # windows + if uri.startswith('file:\\\\\\'): # windows path = uri[8:] # 8 is len('file:///') - elif uri.startswith('file://'): # nautilus, rox + elif uri.startswith('file://'): # nautilus, rox path = uri[7:] # 7 is len('file://') - elif uri.startswith('file:'): # xffm + elif uri.startswith('file:'): # xffm path = uri[5:] # 5 is len('file:') path = url2pathname(path) # escape special chars path = path.strip('\r\n\x00') # remove \r\n and NULL return path - - + def set_stdstatus(self): """Set a standard mode-depenedent status message.""" self.g_statusbar.pop(self.status) @@ -467,26 +462,24 @@ def set_stdstatus(self): else: s = "Enter message to encrypt or decrypt" self.g_statusbar.push(self.status, s) - - + def test_file_isbinary(self, filename): """Utilize nix file cmd to determine if filename is binary or text.""" cmd = split("file -b -e soft '{}'".format(filename)) if check_output(cmd)[:4] in {'ASCI', 'UTF-'}: return False return True - - + def open_in_txtview(self, filename): """Replace contents of msg TextView's TextBuffer with contents of file.""" try: - with open(filename) as f: self.buff.set_text(f.read()) + with open(filename) as f: + self.buff.set_text(f.read()) if self.buff.get_char_count() < 1: self.infobar('txtview_fileopen_binary_error') except: self.infobar('txtview_fileopen_error', filename) - - + # This is called when entering & exiting direct-file mode def filemode_enablewidgets(self, x=True): """Enable/disable certain widgets due to working in direct-file mode.""" @@ -496,16 +489,14 @@ def filemode_enablewidgets(self, x=True): for w in widgets: w.set_sensitive(x) self.fix_msgtxtviewcolor(x) - - + # This is called when user tries to copyall, save, or {en,de}crypt/sign/verify def test_msgbuff_isempty(self, message): """Return True + show infobar containing msg if Message area is empty.""" if self.buff.get_char_count() < 1: self.infobar('txtview_empty', customtext=message) return True - - + def confirm_overwrite_callback(self, chooser): """In filechooser, disallow output file being the input file.""" outfile = chooser.get_filename() @@ -516,24 +507,29 @@ def confirm_overwrite_callback(self, chooser): return Gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN else: return Gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM - - + # Generic file chooser for opening or saving def chooser_grab_filename(self, mode, save_suggestion=None): """Present file chooser dialog and return filename or None.""" - + filename = None - if mode in 'open': title = "Choose text file to open as input..." - elif mode in 'save': title = "Choose output filename..." + if mode in 'open': + title = "Choose text file to open as input..." + elif mode in 'save': + title = "Choose output filename..." cmd = ("Gtk.FileChooserDialog('{0}', None, Gtk.FileChooserAction.{1}, " "(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))" .format(title, mode.upper())) chooser = eval(cmd) - + if mode in 'open': # Setup file filters - t = Gtk.FileFilter() ; t.set_name("Text Files") ; t.add_mime_type("text/*") - a = Gtk.FileFilter() ; a.set_name("All Files") ; a.add_pattern("*") + t = Gtk.FileFilter() + t.set_name("Text Files") + t.add_mime_type("text/*") + a = Gtk.FileFilter() + a.set_name("All Files") + a.add_pattern("*") chooser.add_filter(t) chooser.add_filter(a) elif mode in 'save': @@ -541,13 +537,12 @@ def chooser_grab_filename(self, mode, save_suggestion=None): chooser.set_do_overwrite_confirmation(True) chooser.connect('confirm-overwrite', self.confirm_overwrite_callback) if save_suggestion: chooser.set_current_name(save_suggestion) - + if chooser.run() == Gtk.ResponseType.OK: filename = chooser.get_filename() chooser.destroy() return filename - - + def grab_activetext_combobox(self, combobox): """Return the text of active combobox selection.""" cbmodel = combobox.get_model() @@ -556,55 +551,53 @@ def grab_activetext_combobox(self, combobox): return None # If first choice is selected, i.e. 'Default' else: return cbmodel[cbindex][0] - - + # This is called by encrypt/decrypt buttons when operating in direct-file mode def filemode_get_outfile(self, mode): """Use FileChooser to get an output filename for direct enc/dec.""" - + # Prompt for output file outfile = self.chooser_grab_filename('save', self.x.io['infile']) - + # Kick off processing unless user canceled if outfile: self.x.io['outfile'] = outfile self.launchxface(mode) - - + def initiate_filemode(self): """Ensure read access of file set by chooserwidget and notify user of next steps.""" - + # Prompt for filename but err out if file can't be read infile = self.g_chooserbtn.get_filename() if not access(infile, R_OK): self.infobar('filemode_fileopen_error', infile) return - + # Tweak some widgets if in Sign/Verify mode if self.g_signverify.get_active(): - self.g_chk_outfile.set_visible (True) - self.g_chk_outfile.set_active (self.p['svoutfiles']) - self.g_sigmode.set_active (self.p['file_sigmode']) - + self.g_chk_outfile.set_visible(True) + self.g_chk_outfile.set_active(self.p['svoutfiles']) + self.g_sigmode.set_active(self.p['file_sigmode']) + # Configure state of plaintext output checkbox via user-settings - self.g_plaintext.set_sensitive (True) - + self.g_plaintext.set_sensitive(True) + if self.p['txtoutput'] == 0: # Autodetect if self.test_file_isbinary(infile): - self.g_plaintext.set_active (False) + self.g_plaintext.set_active(False) else: - self.g_plaintext.set_active (True) - + self.g_plaintext.set_active(True) + elif self.p['txtoutput'] == 1: # Always Binary - self.g_plaintext.set_active (False) - + self.g_plaintext.set_active(False) + elif self.p['txtoutput'] == 2: # Always Text - self.g_plaintext.set_active (True) - + self.g_plaintext.set_active(True) + # Set statusbar w/ filemode status self.g_statusbar.pop(self.status) self.g_statusbar.push(self.status, "Choose an action to perform on {!r}".format(infile)) - + if self.ib_filemode: # If filemode infobar already present: user picked a new file, so destroy old ibar self.ib_filemode.destroy() @@ -615,14 +608,13 @@ def initiate_filemode(self): False) self.buff.set_text('') self.filemode_enablewidgets(False) - + # Create filemode infobar with cancel button self.ib_filemode = self.infobar('filemode_blue_banner', infile, vbox=self.vbox_ibar2) - + # Set input file self.x.io['infile'] = infile - - + def cleanup_filemode(self, *args): """Revert the changes (to widgets, etc) that filemode causes.""" # Restore message buffer @@ -632,27 +624,26 @@ def cleanup_filemode(self, *args): self.ib_filemode.destroy() self.ib_filemode = None # Enable/sensitize widgets - self.filemode_enablewidgets (True) - self.g_chk_outfile.set_visible (False) + self.filemode_enablewidgets(True) + self.g_chk_outfile.set_visible(False) # Ensure sigmode combobox is back to proper 'normal' if self.g_signverify.get_active(): - self.g_sigmode.set_active (self.p['text_sigmode']) + self.g_sigmode.set_active(self.p['text_sigmode']) else: - self.g_sigmode.set_active (0) + self.g_sigmode.set_active(0) # Set statusbar self.set_stdstatus() - #while Gtk.events_pending(): + # while Gtk.events_pending(): Gtk.main_iteration() # Reset filenames self.x.io['infile'] = 0 self.x.io['outfile'] = 0 # Disable plaintext CheckButton - self.g_plaintext.set_sensitive (False) - self.g_plaintext.set_active (True) - - - #--------------------------------------------------- HERE BE GTK SIGNAL CBs - + self.g_plaintext.set_sensitive(False) + self.g_plaintext.set_active(True) + + # HERE BE GTK SIGNAL CBs + # Called by window destroy / Quit menu item def action_quit(self, w): """Shutdown application and any child process.""" @@ -662,10 +653,9 @@ def action_quit(self, w): self.x.childprocess.send_signal(SIGCONT) self.x.childprocess.terminate() stderr.write("\n") - #sleep(0.2) + # sleep(0.2) Gtk.main_quit() - - + # Called when dnd occurs on Message TextView def action_drag_data_received(self, w, context, x, y, selection, target_type, timestamp): """Read dragged text file into Message area.""" @@ -675,17 +665,15 @@ def action_drag_data_received(self, w, context, x, y, selection, target_type, ti path = self.get_file_path_from_dnd_dropped_uri(uri) if isfile(path): self.open_in_txtview(path) - - + # Called by changing opacity hscale def action_opacity_slider(self, w): """Actions to perform when opacity scale is changed.""" val = w.get_value() - self.g_window.set_opacity(val/100.0) + self.g_window.set_opacity(val / 100.0) w.set_tooltip_text( "Change window opacity (current:{:.1f}%)".format(val)) - - + # Called by SwitchEngine menu item def action_switch_engine(self, w): """Switch backend between openssl & gpg.""" @@ -693,28 +681,26 @@ def action_switch_engine(self, w): self.instantiate_xface('gpg') else: self.instantiate_xface('openssl') - - + # Called by About menu item def action_about(self, w): """Launch About dialog.""" builder = Gtk.Builder() - builder.add_from_file(cfg.ASSETDIR + 'ui/about.glade') + builder.add_from_file(cfg.ASSETDIR + 'ui/about.glade') about = builder.get_object('aboutdialog') about.set_logo_icon_name(Gtk.STOCK_DIALOG_AUTHENTICATION) # about.set_transient_for(self.g_window) about.set_version(cfg.VERSION) about.connect('response', lambda *args: about.destroy()) about.show() - - + # Called by Preferences menu item def action_preferences(self, w): """Launch preferences window.""" - + # Run preferences window method from already-open pref instance self.preferences.open_preferences_window(parentwindow=self.g_window) - + # CB for pref window's save button def savepref(*args): # Attempt to save preferences @@ -722,7 +708,7 @@ def savepref(*args): # If success: destroy pref window, show infobar self.preferences.window.destroy() self.infobar('preferences_save_success', cfg.USERPREF_FILE) - + # CB for pref window's apply button def applypref(*args): # Attempt to save preferences @@ -733,12 +719,11 @@ def applypref(*args): if self.x.io['infile']: self.cleanup_filemode() self.instantiate_xface(startup=True) self.infobar('preferences_apply_success', cfg.USERPREF_FILE) - + # Connect signals - self.preferences.btn_save.connect ('clicked', savepref) - self.preferences.btn_apply.connect ('clicked', applypref) - - + self.preferences.btn_save.connect('clicked', savepref) + self.preferences.btn_apply.connect('clicked', applypref) + # Called by Clear toolbar btn or menu item def action_clear(self, w): """Reset Statusbar, filemode stuff, TextView buffers.""" @@ -746,92 +731,83 @@ def action_clear(self, w): self.cleanup_filemode() else: self.set_stdstatus() - self.buff.set_text ('') - self.buff2.set_text ('') + self.buff.set_text('') + self.buff2.set_text('') self.x.io = dict(stdin='', stdout='', gstatus=0, infile=0, outfile=0) - - + # Called when user clicks the entry_icon in any of the entry widgets def action_clear_entry(self, entry, *args): """Clear TextEntry widget.""" entry.set_text('') - - + # Called by Open toolbar btn or menu item def action_open(self, w): """Read in a text file and push its contents to our TextView.""" filename = self.chooser_grab_filename('open') if filename: self.open_in_txtview(filename) - - + # Called when direct-file-mode FileChooserButton gets a new file set, # either because of dnd or manual selection def action_chooserbtn_file_set(self, w): print("[on_file-set] FileChooserButton.get_filename() output:\n{!r}\n".format(w.get_filename())) self.initiate_filemode() - - + # Called by Save toolbar btn or menu item def action_save(self, w): """Save contents of msg TextView's TextBuffer to file.""" - + # If Message area is empty, err out if self.test_msgbuff_isempty("No text to save."): return - + # Prompt for filename to save to; cancel if user cancels filename = self.chooser_grab_filename('save') if not filename: return - + # Set saving status self.g_statusbar.push(self.status, "Saving {}".format(filename)) - #while Gtk.events_pending(): + # while Gtk.events_pending(): Gtk.main_iteration() - + # Grab text from buffer buffertext = self.buff.get_text(self.buff.get_start_iter(), self.buff.get_end_iter(), False) try: # If can open file for writing, show success infobar - with open(filename, 'w') as f: f.write(buffertext) + with open(filename, 'w') as f: + f.write(buffertext) self.infobar('txtview_save_success', filename) except: # Otherwise, show error self.infobar('txtview_save_error', filename) - + # Clear saving status self.g_statusbar.pop(self.status) - - + def action_undo(self, w): pass - - + def action_redo(self, w): pass - - + # Called by Cut toolbar btn or menu item def action_cut(self, w): """Cut msg TextBuffer selection.""" self.buff.cut_clipboard(Gtk.clipboard_get(), True) - - + # Called by Copy toolbar btn or menu item def action_copy(self, w): """Copy msg TextBuffer selection.""" self.buff.copy_clipboard(Gtk.clipboard_get()) - - + # Called by Paste toolbar btn or menu item def action_paste(self, w): """Paste clipboard into msg TextBuffer at selection.""" self.buff.paste_clipboard(Gtk.clipboard_get(), None, True) - - + # Called by Copyall toolbar btn def action_copyall(self, w): """Select whole msg TextBuffer contents and copy it to clipboard.""" @@ -841,8 +817,7 @@ def action_copyall(self, w): self.buff.get_end_iter()) self.buff.copy_clipboard(Gtk.clipboard_get()) self.infobar('txtview_copyall_success') - - + # Called by Zoom menu items def action_zoom(self, w): """Increase/decrease font size of TextViews.""" @@ -857,8 +832,7 @@ def action_zoom(self, w): Pango.FontDescription("monospace {}".format(self.p['msgfntsize']))) self.g_errtxtview.modify_font( Pango.FontDescription("normal {}".format(self.p['errfntsize']))) - - + # Called when Cipher combobox selection is changed def action_cipher_changed(self, w): """Disallow certain cipher selections in OpenSSL mode.""" @@ -872,16 +846,15 @@ def action_cipher_changed(self, w): self.infobar('cipher_openssl_no_twofish') elif cipher in 'AES': self.infobar('cipher_openssl_aes_note') - - + # Called by Encrypt/Sign toolbar btn def action_encrypt(self, w): """Encrypt or sign input.""" # DEBUG - #self.g_chooserbtn.select_filename('/etc/passwd') - #self.g_expander.set_expanded(True) - #Gtk.main_iteration() - #return + # self.g_chooserbtn.select_filename('/etc/passwd') + # self.g_expander.set_expanded(True) + # Gtk.main_iteration() + # return if self.g_signverify.get_active(): # If in sign-only mode, figure out which sig-type if self.g_sigmode.get_active() == 0: @@ -894,8 +867,7 @@ def action_encrypt(self, w): else: # Normal enc/dec mode self.launchxface('enc') - - + # Called by Decrypt/Verify toolbar btn def action_decrypt(self, w): """Decrypt or verify input.""" @@ -905,165 +877,157 @@ def action_decrypt(self, w): else: # Normal enc/dec mode self.launchxface('dec') - - + # Called by Symmetric checkbox toggle def action_toggle_symmetric(self, w): """Toggle symmetric encryption (enable/disable certain widgets).""" - + symm_widgets = [self.g_passlabel, self.g_pass] - + if w.get_active(): # If entering toggled state, allow pass entry for widget in symm_widgets: - widget.set_sensitive (True) + widget.set_sensitive(True) if not self.g_advanced.get_active(): # If not in advanced mode, disable Asymmetric - self.g_asymmetric.set_active (False) - + self.g_asymmetric.set_active(False) + else: # If leaving toggled state, hide pass entry for widget in symm_widgets: - widget.set_sensitive (False) + widget.set_sensitive(False) if not self.g_asymmetric.get_active(): # If unchecking Symm & Asymm isn't already on, turn it on - self.g_asymmetric.set_active (True) - - + self.g_asymmetric.set_active(True) + # Called by Asymmetric checkbox toggle def action_toggle_asymmetric(self, w): """Toggle asymmetric encryption (enable/disable certain widgets).""" - + asymm_widgets = [self.g_reciplabel, self.g_recip, self.g_enctoself] - + if w.get_active(): # If entering toggled state, allow recip entry, enctoself for widget in asymm_widgets: - widget.set_sensitive (True) - self.g_signature.set_sensitive (True) + widget.set_sensitive(True) + self.g_signature.set_sensitive(True) if not self.g_advanced.get_active(): # If not in advanced mode, disable Symmetric - self.g_symmetric.set_active (False) - + self.g_symmetric.set_active(False) + else: # If leaving toggled state, hide recip entry, enctoself for widget in asymm_widgets: - widget.set_sensitive (False) - self.g_enctoself.set_active (False) + widget.set_sensitive(False) + self.g_enctoself.set_active(False) if not self.g_advanced.get_active(): # If not in advanced mode, ensure add signature is unchecked - self.g_signature.set_sensitive (False) - self.g_signature.set_active (False) + self.g_signature.set_sensitive(False) + self.g_signature.set_active(False) if not self.g_symmetric.get_active(): # If unchecking Asymm & Symm isn't already on, turn it on - self.g_symmetric.set_active (True) - - + self.g_symmetric.set_active(True) + # Called by Advanced checkbox toggle def action_toggle_advanced(self, w): """Enable/disable encryption widgets for advanced mode.""" - + if w.get_active(): # If entering the toggled state, allow adding signature - self.g_signature.set_sensitive (True) - + self.g_signature.set_sensitive(True) + else: # If leaving the toggled state... if self.g_symmetric.get_active(): # We have some things to do if Symmetric is checked... if self.g_asymmetric.get_active(): # If Asymmetric is also checked, disable it - self.g_asymmetric.set_active (False) + self.g_asymmetric.set_active(False) else: # If Asymmetric isn't checked, ensure addsig is disabled - self.g_signature.set_sensitive (False) - self.g_signature.set_active (False) - - + self.g_signature.set_sensitive(False) + self.g_signature.set_active(False) + # Called by Sign/Verify radio toggle def action_toggle_mode_signverify(self, w): """Hide/show, change some widgets when switching modes.""" - + enc_widgets = [self.g_symmetric, self.g_asymmetric, self.g_advanced, self.g_enctoolbar] - + # Change statusbar self.set_stdstatus() - + if w.get_active(): # If entering the toggled state: modify buttons, hide & show widgets # Modify Encrypt/Decrypt button labels - self.g_encrypt.set_label ("Sign") - self.g_decrypt.set_label ("Verify") + self.g_encrypt.set_label("Sign") + self.g_decrypt.set_label("Verify") # Hide encryption toolbar & Symmetric, Asymmetric, Adv toggles for widget in enc_widgets: - widget.set_visible (False) + widget.set_visible(False) # Save state of AddSignature for switching back to Enc/Dec mode self.encdec_sig_state_sensitive = self.g_signature.get_sensitive() - self.encdec_sig_state_active = self.g_signature.get_active() + self.encdec_sig_state_active = self.g_signature.get_active() # Desensitize AddSignature checkbox and turn it on - self.g_signature.set_sensitive (False) - self.g_signature.set_active (True) + self.g_signature.set_sensitive(False) + self.g_signature.set_active(True) # Sensitize sigmode combobox - self.g_sigmode.set_sensitive (True) + self.g_sigmode.set_sensitive(True) # Set sigmode combobox via user prefs if self.x.io['infile']: - self.g_sigmode.set_active (self.p['file_sigmode']) - self.g_chk_outfile.set_visible (True) + self.g_sigmode.set_active(self.p['file_sigmode']) + self.g_chk_outfile.set_visible(True) else: - self.g_sigmode.set_active (self.p['text_sigmode']) - + self.g_sigmode.set_active(self.p['text_sigmode']) + else: # If leaving the toggled state, we have some things to reverse - self.g_encrypt.set_label ("_Encrypt") - self.g_decrypt.set_label ("_Decrypt") - self.g_chk_outfile.set_visible (False) + self.g_encrypt.set_label("_Encrypt") + self.g_decrypt.set_label("_Decrypt") + self.g_chk_outfile.set_visible(False) for widget in enc_widgets: - widget.set_visible (True) - self.g_signature.set_sensitive (self.encdec_sig_state_sensitive) - self.g_signature.set_active (self.encdec_sig_state_active) - self.g_sigmode.set_sensitive (False) - self.g_sigmode.set_active (0) # Reset to 'Embedded' type for Enc/Dec mode - - + widget.set_visible(True) + self.g_signature.set_sensitive(self.encdec_sig_state_sensitive) + self.g_signature.set_active(self.encdec_sig_state_active) + self.g_sigmode.set_sensitive(False) + self.g_sigmode.set_active(0) # Reset to 'Embedded' type for Enc/Dec mode + # Called by 'Change Default Key' checkbox toggle def action_toggle_defaultkey(self, w): """Hide/show Entry widget for setting gpg 'localuser' argument.""" - + if w.get_active(): # If entering toggled state, show default key TextEntry - self.g_defaultkey.set_visible (True) + self.g_defaultkey.set_visible(True) else: # If leaving toggled state, hide default key TextEntry - self.g_defaultkey.set_visible (False) - - + self.g_defaultkey.set_visible(False) + # Called by 'Add Signature' checkbox toggle def action_toggle_signature(self, w): """Hide/show some widgets when toggling adding of a signature to input.""" sig_widgets = [self.g_sigmode, self.g_digest, self.g_digestlabel] - + if w.get_active(): # If entering toggled state, show sig toolbar widgets for widget in sig_widgets: - widget.set_visible (True) + widget.set_visible(True) else: # If leaving toggled state, hide sig toolbar widgets for widget in sig_widgets: - widget.set_visible (False) - - + widget.set_visible(False) + # Called by 'Task Status Side Panel' checkbox toggle def action_toggle_taskstatus(self, w): """Show/hide side pane containing gpg stderr output.""" if w.get_active(): # If entering toggled state, show Task Status TextView frame - self.g_frame2.set_visible (True) + self.g_frame2.set_visible(True) else: # If leaving toggled state, hide Task Status TextView frame - self.g_frame2.set_visible (False) - - + self.g_frame2.set_visible(False) + # Called by 'Text Wrapping' checkbox toggle def action_toggle_wordwrap(self, w): """Toggle word wrapping for main message TextView.""" @@ -1073,17 +1037,16 @@ def action_toggle_wordwrap(self, w): else: # If leaving toggled state, disable word wrapping self.g_msgtxtview.set_wrap_mode(Gtk.WrapMode.NONE) - - + # Called by [processing progbar] Cancel button def action_cancel_child_process(self, btn): """Terminate gpg/openssl subprocess.""" - - stderr.write ("Canceling Operation\n") - self.canceled = True + + stderr.write("Canceling Operation\n") + self.canceled = True for w in self.g_cancel, self.g_pause: - w.set_sensitive (False) - self.g_progbar.set_text ("Canceling Operation...") + w.set_sensitive(False) + self.g_progbar.set_text("Canceling Operation...") self.g_activityspin.stop() Gtk.main_iteration() while not self.x.childprocess: @@ -1092,45 +1055,43 @@ def action_cancel_child_process(self, btn): self.x.childprocess.send_signal(SIGCONT) self.x.childprocess.terminate() self.show_working_progress(False) - - + # Called by [processing progbar] Pause button def action_pause_child_process(self, btn): """Suspend/resume gpg/openssl subprocess with SIGSTOP/SIGCONT.""" - + # We can't pause childprocess until it actually starts while not self.x.childprocess: Gtk.main_iteration() - + if self.paused: # Already paused, so, time to unpause - stderr.write ("\n") - self.paused = False - btn.set_relief (Gtk.ReliefStyle.NONE) - self.g_progbar.set_text ("{} working...".format(self.engine)) + stderr.write("\n") + self.paused = False + btn.set_relief(Gtk.ReliefStyle.NONE) + self.g_progbar.set_text("{} working...".format(self.engine)) self.g_activityspin.start() self.x.childprocess.send_signal(SIGCONT) else: # Time to pause - stderr.write ("\n") - self.paused = True - btn.set_relief (Gtk.ReliefStyle.NORMAL) - self.g_progbar.set_text ("Operation PAUSED") + stderr.write("\n") + self.paused = True + btn.set_relief(Gtk.ReliefStyle.NORMAL) + self.g_progbar.set_text("Operation PAUSED") self.g_activityspin.stop() self.x.childprocess.send_signal(SIGSTOP) - - - #------------------------------------------------------ MAIN XFACE FUNCTION + + # MAIN XFACE FUNCTION def launchxface(self, action): """Manage I/O between Gtk objects and our GpgXface or OpensslXface object.""" - self.canceled = False - self.paused = False + self.canceled = False + self.paused = False self.x.childprocess = None - + ### PREPARE Xface ARGS - passwd = None - recip = None - localuser = None + passwd = None + recip = None + localuser = None # symmetric & passwd symmetric = self.g_symmetric.get_active() if symmetric: @@ -1140,7 +1101,7 @@ def launchxface(self, action): self.infobar('x_missing_passphrase') return passwd = None # If passwd was '' , set to None, which will trigger gpg-agent if necessary - + # INTERLUDE: If operating in textinput mode, check for input text if not self.x.io['infile']: # Make sure textview has a proper message in it @@ -1149,7 +1110,7 @@ def launchxface(self, action): # Make TextView immutable to changes self.g_msgtxtview.set_sensitive(False) self.fix_msgtxtviewcolor(False) - + # enctoself enctoself = self.g_enctoself.get_active() # recip @@ -1176,10 +1137,10 @@ def launchxface(self, action): if self.g_chk_defkey.get_active(): localuser = self.g_defaultkey.get_text() if not localuser: localuser = None - + # INITIAL FILE INPUT MODE PREP if self.x.io['infile'] and not self.x.io['outfile']: - + if base64 or action in 'clearsign': outfile = self.x.io['infile'] + '.asc' elif self.engine in 'OpenSSL': @@ -1188,10 +1149,10 @@ def launchxface(self, action): outfile = self.x.io['infile'] + '.sig' else: outfile = self.x.io['infile'] + '.gpg' - + if action in 'dec': outfile = self.x.io['infile'][:-4] - + if action not in 'verify': if self.g_signverify.get_active() and not self.g_chk_outfile.get_active(): pass @@ -1205,44 +1166,44 @@ def launchxface(self, action): working_widgets = self.working_widgets_filemode for w in working_widgets: w.set_sensitive(False) self.ib_filemode.hide() - + # FILE INPUT MODE PREP WHEN ALREADY HAVE OUTPUT FILE elif self.x.io['infile'] and self.x.io['outfile']: - + working_widgets = self.working_widgets_filemode for w in working_widgets: w.set_sensitive(False) self.ib_filemode.hide() - + # TEXT INPUT MODE PREP else: - + working_widgets = self.working_widgets_textmode for w in working_widgets: w.set_sensitive(False) - + # Save textview buffer to Xface stdin self.x.io['stdin'] = self.buff.get_text(self.buff.get_start_iter(), self.buff.get_end_iter(), False) - + # Set working status + spinner + progress bar self.show_working_progress(True, action) # Clear Task Status self.buff2.set_text('') - + # Setup stderr file descriptors & update task status while processing self.x.io['stderr'] = pipe() GLib.io_add_watch( self.x.io['stderr'][0], GLib.IO_IN | GLib.IO_HUP, self.update_task_status) - + if self.engine in 'OpenSSL': # ATTEMPT EN-/DECRYPTION w/OPENSSL Thread( target=self.x.openssl, args=(action, passwd, base64, cipher) - ).start() - + ).start() + else: # GPG if verbose: @@ -1257,8 +1218,8 @@ def launchxface(self, action): target=self.x.gpg, args=(action, encsign, digest, localuser, base64, symmetric, passwd, asymmetric, recip, enctoself, cipher, verbose, alwaystrust) - ).start() - + ).start() + # Wait for subprocess to finish or for Cancel button to be clicked c = 0 while not self.x.childprocess or self.x.childprocess.returncode == None: @@ -1273,23 +1234,23 @@ def launchxface(self, action): # Restore widgets to normal states for w in working_widgets: w.set_sensitive(True) self.show_working_progress(False) - + # FILE INPUT MODE CLEANUP if self.x.io['infile']: - + if self.canceled: # User Canceled! - + self.ib_filemode.show() - + if action in {'enc', 'dec'}: action = "{}rypt".format(action.title()) elif action in {'embedsign', 'clearsign', 'detachsign'}: action = "Sign" elif action in 'verify': action = action.title() - + self.infobar('x_canceled_filemode', customtext=action) - + elif self.x.childprocess.returncode == 0: # File Success! if self.engine in 'OpenSSL' and action in 'enc': @@ -1297,20 +1258,20 @@ def launchxface(self, action): elif action in {'enc', 'dec'}: self.infobar('x_crypt_success_filemode', self.x.io['outfile'], action) - + elif action in {'embedsign', 'clearsign'}: self.infobar('x_sign_success_filemode', self.x.io['outfile']) - + elif action in 'detachsign': self.infobar('x_detachsign_success_filemode', self.x.io['outfile']) - + elif action in 'verify': self.infobar('x_verify_success') - + self.cleanup_filemode() - + else: # File Fail! - + self.ib_filemode.show() if action in 'verify': self.infobar('x_verify_failed') @@ -1323,27 +1284,27 @@ def launchxface(self, action): elif action in {'embedsign', 'clearsign', 'detachsign'}: action = 'sign' self.infobar('x_generic_failed_filemode', customtext=action) - + # TEXT INPUT MODE CLEANUP else: - + self.set_stdstatus() self.g_msgtxtview.set_sensitive(True) self.fix_msgtxtviewcolor(True) - + if self.canceled: # User Canceled! - + if action in {'enc', 'dec'}: action = "{}rypt".format(action.title()) elif action in {'embedsign', 'clearsign', 'detachsign'}: action = "Sign" elif action in 'verify': action = action.title() - + self.infobar('x_canceled_textmode', customtext=action) - + elif self.x.childprocess.returncode == 0: # Text Success! - + if action in 'verify': self.infobar('x_verify_success') else: @@ -1352,9 +1313,9 @@ def launchxface(self, action): self.x.io['stdout'] = 0 if self.engine in 'OpenSSL' and action in 'enc': self.infobar('x_opensslenc_success_textmode', customtext=cipher) - + else: # Text Fail! - + if action in 'verify': self.infobar('x_verify_failed') return @@ -1366,14 +1327,13 @@ def launchxface(self, action): elif action in {'embedsign', 'clearsign', 'detachsign'}: action = 'sign' self.infobar('x_generic_failed_textmode', customtext=action) - - - #------------------------------------------ HELPERS FOR MAIN XFACE FUNCTION - + + # ------------------------------------------ HELPERS FOR MAIN XFACE FUNCTION + # CB for GLib.io_add_watch() def update_task_status(self, fd, condition, output='task'): """Read data waiting in file descriptor; close fd if other end hangs up.""" - + # If there's data to be read, let's read it if condition == GLib.IO_IN: if output in 'task': @@ -1385,26 +1345,25 @@ def update_task_status(self, fd, condition, output='task'): b = read(fd, 1024).decode('utf-8') stderr.write(b) return True - + # If other end of pipe hangs up, close our fd and destroy the watcher elif condition == GLib.IO_HUP: if output in 'term': stderr.write("\n") close(fd) return False - - + # Called when gpg/openssl begins and ends processing def show_working_progress(self, show=True, action=None): """Hide/show progress widgets; set/unset working status + activity spinner.""" - + # Show/hide progress bar & its buttons for w in self.g_progbar, self.g_cancel, self.g_pause: w.set_visible(show) - + if show: # If beginning processing: set progbar text + working status, start spinner - self.g_progbar.set_text ("{} working...".format(self.engine)) + self.g_progbar.set_text("{} working...".format(self.engine)) if action in {'embedsign', 'clearsign', 'detachsign'}: status = "Signing input ..." elif action in 'verify': @@ -1415,7 +1374,7 @@ def show_working_progress(self, show=True, action=None): self.g_activityspin.set_visible(True) self.g_activityspin.start() Gtk.main_iteration() - + else: # If finished processing: ensure progbar buttons are normal, reset status, stop spinner for w in self.g_cancel, self.g_pause: @@ -1424,15 +1383,11 @@ def show_working_progress(self, show=True, action=None): self.g_activityspin.stop() self.g_activityspin.set_visible(False) self.g_statusbar.pop(self.status) - - - #---------------------------------------------- RUN MAIN APPLICATION WINDOW + + # RUN MAIN APPLICATION WINDOW def main(self): """Show main window, and start GTK+ main loop.""" self.g_window.show() self.g_window.connect("destroy", Gtk.main_quit) self.g_window.show_all() Gtk.main() - - - diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index 736852d..f27f285 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # This file is part of Pyrite. # Last file mod: 2013/09/15 @@ -20,17 +19,14 @@ # # You should have received a copy of the GNU General Public License # along with Pyrite. If not, see . -# -#------------------------------------------------------------------------------ -from sys import stderr from os import pipe, write, close from shlex import split from subprocess import Popen, PIPE, check_output +from sys import stderr from time import sleep - def flatten_list_to_stderr(list): stderr.write("-" * 79 + "\n") for item in list: @@ -38,7 +34,6 @@ def flatten_list_to_stderr(list): stderr.write("\n\n") - class Gpg(): """GPG/GPG2 interface for encryption/decryption/signing/verifying. @@ -54,68 +49,70 @@ class Gpg(): required, gpg() invokes gpg/gpg2 with gpg-agent enabled. """ - - + def __init__(self, show_version=True, firstchoice='gpg2'): """Confirm we can run gpg or gpg2.""" - + def gpg1(): self.vers = Popen(['gpg', '--version'], stdout=PIPE).communicate()[0] self.GPG_BINARY = 'gpg' - + def gpg2(): self.vers = Popen(['gpg2', '--version'], stdout=PIPE).communicate()[0] self.GPG_BINARY = 'gpg2' - + if firstchoice == 'gpg': - try: gpg1() + try: + gpg1() except: - try: gpg2() + try: + gpg2() except: stderr.write("gpg, gpg2 not found on your system.\n\n") raise else: - try: gpg2() + try: + gpg2() except: - try: gpg1() + try: + gpg1() except: stderr.write("gpg, gpg2 not found on your system.\n\n") raise - + # To show or not to show version info if show_version: stderr.write("{}\n".format(self.vers)) - + # I/O dictionary obj self.io = dict( - stdin='', # Stores input text for subprocess + stdin='', # Stores input text for subprocess stdout='', # Stores stdout stream from subprocess - stderr=0, # Stores tuple of r/w file descriptors for stderr stream + stderr=0, # Stores tuple of r/w file descriptors for stderr stream gstatus=0, # Stores tuple of r/w file descriptors for gpg-status stream - infile=0, # Input filename for subprocess + infile=0, # Input filename for subprocess outfile=0) # Output filename for subprocess - + self.childprocess = None - - + # Main gpg interface method def gpg( - self, - action= None, # One of: enc, dec, embedsign, clearsign, detachsign, verify - encsign= False, # Add '--sign' when encrypting? - digest= None, # One of: sha256, sha1, etc; None == use gpg defaults - localuser= None, # Value passed to --local-user to set default key for signing, etc - base64= True, # Add '--armor' when encrypting/signing? - symmetric= False, # Add '--symmetric'? - passwd= None, # Passphrase for symmetric - asymmetric= False, # Add '--encrypt'? - recip= None, # Recipients for asymmetric (semicolon-delimited) - enctoself= False, # Add first id from secret keyring as recipient? - cipher= None, # One of: aes256, 3des, etc; None == use gpg defaults - verbose= False, # Add '--verbose'? - alwaystrust=False, # Add '--trust-model always'? - yes= True # Add '--yes'? (will overwrite files) - ): + self, + action=None, # One of: enc, dec, embedsign, clearsign, detachsign, verify + encsign=False, # Add '--sign' when encrypting? + digest=None, # One of: sha256, sha1, etc; None == use gpg defaults + localuser=None, # Value passed to --local-user to set default key for signing, etc + base64=True, # Add '--armor' when encrypting/signing? + symmetric=False, # Add '--symmetric'? + passwd=None, # Passphrase for symmetric + asymmetric=False, # Add '--encrypt'? + recip=None, # Recipients for asymmetric (semicolon-delimited) + enctoself=False, # Add first id from secret keyring as recipient? + cipher=None, # One of: aes256, 3des, etc; None == use gpg defaults + verbose=False, # Add '--verbose'? + alwaystrust=False, # Add '--trust-model always'? + yes=True # Add '--yes'? (will overwrite files) + ): """Build a gpg cmdline and then launch gpg/gpg2, saving output appropriately. This method inspects the contents of class attr 'io' -- a dict object that should @@ -147,32 +144,32 @@ def gpg( the caller (i.e., by examining the Popen instance's returncode attribute). """ - + if self.io['infile'] and self.io['infile'] == self.io['outfile']: stderr.write("Same file for both input and output, eh? Is it going " "to work? ... NOPE. Chuck Testa.\n") raise Exception("infile, outfile must be different") - - fd_pwd_R = None - fd_pwd_W = None - useagent = True - cmd = [self.GPG_BINARY] - + + fd_pwd_R = None + fd_pwd_W = None + useagent = True + cmd = [self.GPG_BINARY] + if self.io['gstatus']: # Status to file descriptor option cmd.append('--status-fd') cmd.append(str(self.io['gstatus'][1])) - + # Setup passphrase file descriptor for symmetric enc/dec if (action in 'enc' and symmetric and passwd and not encsign) or ( - action in 'dec' and symmetric and passwd): - useagent=False - fd_pwd_R, fd_pwd_W = pipe() - write(fd_pwd_W, passwd) - close(fd_pwd_W) - cmd.append('--passphrase-fd') - cmd.append(str(fd_pwd_R)) - + action in 'dec' and symmetric and passwd): + useagent = False + fd_pwd_R, fd_pwd_W = pipe() + write(fd_pwd_W, passwd) + close(fd_pwd_W) + cmd.append('--passphrase-fd') + cmd.append(str(fd_pwd_R)) + # Encrypt opts if action in 'enc': if encsign: @@ -201,29 +198,32 @@ def gpg( for r in recip.split(';'): cmd.append('--recipient') cmd.append(r) - + # Decrypt opts elif action in 'dec': cmd.append('--decrypt') - + # Sign opts elif action in {'embedsign', 'clearsign', 'detachsign'}: - if action in 'embedsign': cmd.append('--sign') - elif action in 'clearsign': cmd.append('--clearsign') - elif action in 'detachsign': cmd.append('--detach-sign') + if action in 'embedsign': + cmd.append('--sign') + elif action in 'clearsign': + cmd.append('--clearsign') + elif action in 'detachsign': + cmd.append('--detach-sign') if digest: cmd.append('--digest-algo') cmd.append(digest) - + # Verify opts elif action in 'verify': cmd.append('--verify') - + # Wouldn't hurt to use armor for all, but it only works with these 3 if action in {'enc', 'embedsign', 'detachsign'}: if base64: cmd.append('--armor') - + # Action-independent opts if useagent: if self.GPG_BINARY in 'gpg': @@ -249,10 +249,10 @@ def gpg( cmd.append(self.io['outfile']) if self.io['infile']: cmd.append(self.io['infile']) - + # Print a separator + the command-arguments to stderr flatten_list_to_stderr(cmd) - + # If working direct with files, setup our Popen instance with no stdin if self.io['infile']: self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][1]) @@ -260,30 +260,28 @@ def gpg( else: b = self.io['stderr'] self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=b[1]) - + # Time to communicate! Save output for later b = self.io['stdin'].encode('utf-8') self.io['stdout'] = self.childprocess.communicate(input=b)[0] - + # Clear stdin from our dictionary asap, in case it's huge self.io['stdin'] = '' - + # Close os file descriptors if fd_pwd_R: close(fd_pwd_R) sleep(0.1) # Sleep a bit to ensure everything gets read close(self.io['stderr'][1]) if self.io['gstatus']: close(self.io['gstatus'][1]) - - + def get_gpgdefaultkey(self): - """Return key id of first secret key in gpg keyring.""" + """Return key id of first secret key in gpg keyring.""" return check_output(split( "{} --list-secret-keys --with-colons --fast-list-mode" .format(self.GPG_BINARY))).split(':', 5)[4] - class Openssl(): """OpenSSL interface for encryption/decryption. @@ -298,39 +296,38 @@ class Openssl(): passed to openssl via an os file descriptor. """ - + def __init__(self, show_version=True): """Confirm we can run openssl.""" - + try: vers = Popen(['openssl', 'version'], stdout=PIPE).communicate()[0] except: stderr.write("OpenSSL not found on your system.\n\n") raise - + # To show or not to show version info if show_version: stderr.write("{}\n".format(vers)) - + # I/O dictionary obj self.io = dict( - stdin='', # Stores input text for subprocess + stdin='', # Stores input text for subprocess stdout='', # Stores stdout stream from subprocess - stderr=0, # Stores tuple of r/w file descriptors for stderr stream - infile=0, # Input filename for subprocess + stderr=0, # Stores tuple of r/w file descriptors for stderr stream + infile=0, # Input filename for subprocess outfile=0) # Output filename for subprocess - + self.childprocess = None - - + # Main openssl interface method def openssl( - self, - action, # One of: enc, dec - passwd, # Passphrase for symmetric - base64=True, # Add '-a' when encrypting/decrypting? - cipher=None, # Cipher in gpg-format; None = use aes256 - ): + self, + action, # One of: enc, dec + passwd, # Passphrase for symmetric + base64=True, # Add '-a' when encrypting/decrypting? + cipher=None, # Cipher in gpg-format; None = use aes256 + ): """Build an openssl cmdline and then launch it, saving output appropriately. This method inspects the contents of class attr 'io' -- a dict object that should @@ -349,35 +346,45 @@ def openssl( the caller (i.e., by examining the Popen instance's returncode attribute). """ - + if self.io['infile'] and self.io['infile'] == self.io['outfile']: stderr.write("Same file for both input and output, eh? Is it going " "to work? ... NOPE. Chuck Testa.\n") raise Exception("infile, outfile must be different") - + if cipher: cipher = cipher.lower() - if cipher == None: cipher = 'aes-256-cbc' - elif cipher == '3des': cipher = 'des-ede3-cbc' - elif cipher == 'cast5': cipher = 'cast5-cbc' - elif cipher == 'blowfish': cipher = 'bf-cbc' - elif cipher == 'aes': cipher = 'aes-128-cbc' - elif cipher == 'aes192': cipher = 'aes-192-cbc' - elif cipher == 'aes256': cipher = 'aes-256-cbc' - elif cipher == 'camellia128': cipher = 'camellia-128-cbc' - elif cipher == 'camellia192': cipher = 'camellia-192-cbc' - elif cipher == 'camellia256': cipher = 'camellia-256-cbc' - #else: cipher = 'aes-256-cbc' - - fd_pwd_R = None - fd_pwd_W = None - cmd = ['openssl', cipher, '-md', 'sha256', '-pass'] - + if cipher == None: + cipher = 'aes-256-cbc' + elif cipher == '3des': + cipher = 'des-ede3-cbc' + elif cipher == 'cast5': + cipher = 'cast5-cbc' + elif cipher == 'blowfish': + cipher = 'bf-cbc' + elif cipher == 'aes': + cipher = 'aes-128-cbc' + elif cipher == 'aes192': + cipher = 'aes-192-cbc' + elif cipher == 'aes256': + cipher = 'aes-256-cbc' + elif cipher == 'camellia128': + cipher = 'camellia-128-cbc' + elif cipher == 'camellia192': + cipher = 'camellia-192-cbc' + elif cipher == 'camellia256': + cipher = 'camellia-256-cbc' + # else: cipher = 'aes-256-cbc' + + fd_pwd_R = None + fd_pwd_W = None + cmd = ['openssl', cipher, '-md', 'sha256', '-pass'] + # Setup passphrase file descriptors fd_pwd_R, fd_pwd_W = pipe() write(fd_pwd_W, passwd) close(fd_pwd_W) cmd.append('fd:{}'.format(fd_pwd_R)) - + if base64: cmd.append('-a') if action in 'enc': @@ -389,27 +396,25 @@ def openssl( cmd.append(self.io['infile']) cmd.append('-out') cmd.append(self.io['outfile']) - + # Print a separator + the command-arguments to stderr flatten_list_to_stderr(cmd) - + # If working direct with files, setup our Popen instance with no stdin if self.io['infile']: self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][1]) # Otherwise, only difference for Popen is we need the stdin pipe else: self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=self.io['stderr'][1]) - + # Time to communicate! Save output for later b = self.io['stdin'].encode('utf-8') self.io['stdout'] = self.childprocess.communicate(input=b)[0] - + # Clear stdin from our dictionary asap, in case it's huge self.io['stdin'] = '' - + # Close os file descriptors close(fd_pwd_R) sleep(0.1) # Sleep a bit to ensure everything gets read close(self.io['stderr'][1]) - - diff --git a/modules/messages.py b/modules/messages.py index 7154233..3e00b83 100644 --- a/modules/messages.py +++ b/modules/messages.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # This file is part of Pyrite. # Last file mod: 2013/09/15 @@ -20,223 +19,218 @@ # # You should have received a copy of the GNU General Public License # along with Pyrite. If not, see . -# -#------------------------------------------------------------------------------ -SUCCESS = 0 -INFO = 1 -QUESTION = 2 -WARNING = 3 -ERROR = 4 +SUCCESS = 0 +INFO = 1 +QUESTION = 2 +WARNING = 3 +ERROR = 4 + def msg(text, type, icon, timeout=5): """Dictionar-ify input arguments.""" return {'text': text, 'type': type, 'icon': icon, 'timeout': timeout} -#------------------------------------------------------------------------------ - # INFOBAR MESSAGES FOR MAIN WINDOW +# INFOBAR MESSAGES FOR MAIN WINDOW MESSAGE_DICT = dict( - - #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Backend Engine - engine_openssl_missing = msg( + + # Backend Engine + engine_openssl_missing=msg( ("Shockingly, your system does not appear to have OpenSSL."), INFO, WARNING), - - engine_gpg_missing = msg( + + engine_gpg_missing=msg( ("GnuPG not found. Operating in OpenSSL fallback-mode.\n" - "To make full use of this program you need either gpg or gpg2 installed.\n" - "Without one of them, you won't have access key-based functions like\n" - "asymmetric encryption or singing."), + "To make full use of this program you need either gpg or gpg2 installed.\n" + "Without one of them, you won't have access key-based functions like\n" + "asymmetric encryption or singing."), INFO, WARNING, 20), - - engine_all_missing = msg( + + engine_all_missing=msg( ("This program requires one of: gpg, gpg2, or openssl\n" - "None of these were found on your system. You can look around\n" - "the interface, but to have real fun you'll need to install gpg or gpg2\n" - "from your linux distribution's software repository."), + "None of these were found on your system. You can look around\n" + "the interface, but to have real fun you'll need to install gpg or gpg2\n" + "from your linux distribution's software repository."), ERROR, WARNING, 0), - - engine_openssl_notice = msg( + + engine_openssl_notice=msg( ("OpenSSL only supports symmetric {{en,de}}cryption.\n" - "All key-based functions are disabled."), + "All key-based functions are disabled."), INFO, INFO, 7), - - #- - - - - - - - - - - - - - - - - - - - - Textview Message Area Operations - txtview_empty = msg( + + # Textview Message Area Operations + txtview_empty=msg( ("{customtext}"), INFO, WARNING, 2), - - txtview_fileopen_error = msg( + + txtview_fileopen_error=msg( ("Error. Could not open file:\n" - "{filename}"), + "{filename}"), WARNING, ERROR), - - txtview_fileopen_binary_error = msg( + + txtview_fileopen_binary_error=msg( ("To operate on binary files, use the\n" - "Input File For Direct Operation chooser button."), + "Input File For Direct Operation chooser button."), INFO, WARNING, 8), - - txtview_save_success = msg( + + txtview_save_success=msg( ("Saved contents of Message area to file:\n" - "{filename}"), + "{filename}"), INFO, SUCCESS), - - txtview_save_error = msg( + + txtview_save_error=msg( ("Error. Could not save to file:\n" - "{filename}"), + "{filename}"), WARNING, ERROR), - txtview_copyall_success = msg( + txtview_copyall_success=msg( ("Copied contents of Message area to clipboard."), INFO, SUCCESS, 3), - - #- - - - - - - - - - - - - - - - - - - - - - - - - - - Filemode Operations - filemode_fileopen_error = msg( + + # Filemode Operations + filemode_fileopen_error=msg( ("Error. Could not open file:\n" - "{filename}\n" - "Choose a new file."), + "{filename}\n" + "Choose a new file."), WARNING, ERROR), - - filemode_blue_banner = msg( + + filemode_blue_banner=msg( ("Encrypt, Decrypt, Sign, or Verify?\n" - "Choose an action to perform on file:\n" - "{filename}\n" - "You will be prompted for an output filename if necessary."), + "Choose an action to perform on file:\n" + "{filename}\n" + "You will be prompted for an output filename if necessary."), QUESTION, QUESTION, 0), - - #- - - - - - - - - - - - - - - Main xface (Enc/Dec/Sign/Verify) Operations - x_missing_passphrase= msg( + + # Main xface (Enc/Dec/Sign/Verify) Operations + x_missing_passphrase=msg( ("Passphrase?"), INFO, QUESTION, 3), - - x_canceled_filemode = msg( + + x_canceled_filemode=msg( ("{customtext} operation canceled.\n" - "To choose different input or output filenames, select Cancel\n" - "from the blue bar below."), + "To choose different input or output filenames, select Cancel\n" + "from the blue bar below."), INFO, WARNING, 6), - - x_canceled_textmode = msg( + + x_canceled_textmode=msg( ("{customtext} operation canceled."), INFO, WARNING, 4), - x_opensslenc_success_filemode = msg( + x_opensslenc_success_filemode=msg( ("OpenSSL encrypted input file with {customtext} cipher;\n" - "saved output to file:\n" - "{filename}\n" - "In order to decrypt that file in the future, you will need to \n" - "remember which cipher you used .. or guess until you figure it out."), + "saved output to file:\n" + "{filename}\n" + "In order to decrypt that file in the future, you will need to \n" + "remember which cipher you used .. or guess until you figure it out."), INFO, SUCCESS, 10), - - x_opensslenc_success_textmode = msg( + + x_opensslenc_success_textmode=msg( ("OpenSSL encrypted input using {customtext} cipher.\n" - "In order to decrypt the output in the future, you will need to \n" - "remember which cipher you used .. or guess until you figure it out."), + "In order to decrypt the output in the future, you will need to \n" + "remember which cipher you used .. or guess until you figure it out."), INFO, SUCCESS, 9), - - x_crypt_success_filemode = msg( + + x_crypt_success_filemode=msg( ("Saved {customtext}rypted copy of input to file:\n" - "{filename}"), + "{filename}"), INFO, SUCCESS), - - x_sign_success_filemode = msg( + + x_sign_success_filemode=msg( ("Saved signed copy of input to file:\n" - "{filename}"), + "{filename}"), INFO, SUCCESS), - - x_detachsign_success_filemode = msg( + + x_detachsign_success_filemode=msg( ("Saved detached signature of input to file:\n" - "{filename}"), + "{filename}"), INFO, SUCCESS), - - x_verify_success = msg( + + x_verify_success=msg( ("Signature verified. Data integrity intact."), INFO, SUCCESS, 4), - - x_verify_failed = msg( + + x_verify_failed=msg( ("Signature or data integrity could not be verified.\n" - "See Task Status for details."), + "See Task Status for details."), WARNING, ERROR, 7), - - x_missing_recip = msg( + + x_missing_recip=msg( ("For whom do you want to encrypt your message?\n" - "If you don't want to enter recipients and you don't want to select\n" - " Enc. To Self, you must add one of the directives\n" - "\tdefault-recipient-self\n" - "\tdefault-recipient name\n" - "to your gpg.conf file."), + "If you don't want to enter recipients and you don't want to select\n" + " Enc. To Self, you must add one of the directives\n" + "\tdefault-recipient-self\n" + "\tdefault-recipient name\n" + "to your gpg.conf file."), WARNING, QUESTION, 0), - - x_generic_failed_filemode = msg( + + x_generic_failed_filemode=msg( ("Problem {customtext}ing file.\n" - "See Task Status for details. Try a different passphrase or Cancel."), + "See Task Status for details. Try a different passphrase or Cancel."), WARNING, ERROR, 8), - - x_generic_failed_textmode = msg( + + x_generic_failed_textmode=msg( ("Problem {customtext}ing input.\n" - "See Task Status for details."), + "See Task Status for details."), WARNING, ERROR), - - #- - - - - - - - - - - - - - - - - - - - - - - - - OpenSSL Cipher Warnings - cipher_openssl_no_default = msg( + + # OpenSSL Cipher Warnings + cipher_openssl_no_default=msg( ("OpenSSL has no default cipher.\n" - "AES256 is a good choice."), + "AES256 is a good choice."), INFO, INFO, 7), - - cipher_openssl_no_twofish = msg( + + cipher_openssl_no_twofish=msg( ("OpenSSL has no support for the Twofish cipher."), INFO, INFO), - - cipher_openssl_aes_note = msg( + + cipher_openssl_aes_note=msg( ("Note for the command-line geeks:\n" - "AES translates to OpenSSL's aes-128-cbc."), + "AES translates to OpenSSL's aes-128-cbc."), INFO, INFO), - - #- - - - - - - - - - - - - - - - - - - - - - - - - - - Preferences Actions - preferences_save_success = msg( + + # Preferences Actions + preferences_save_success=msg( ("Saved preferences to {filename}\n" - "but no changes made to current session."), + "but no changes made to current session."), INFO, SUCCESS), - - preferences_apply_success = msg( + + preferences_apply_success=msg( ("Saved preferences to {filename}\n" - "and applied them to current session."), + "and applied them to current session."), INFO, SUCCESS), - - ) - +) -#------------------------------------------------------------------------------ - # INFOBAR MESSAGES FOR PREFERENCES DIALOG +# INFOBAR MESSAGES FOR PREFERENCES DIALOG PREFS_MESSAGE_DICT = dict( - - prefs_save_failed = msg( + + prefs_save_failed=msg( ("Saving preferences failed.\n" - "Unable to open config file {filename} for writing."), + "Unable to open config file {filename} for writing."), ERROR, WARNING, 10), - - prefs_reverted = msg( + + prefs_reverted=msg( ("Reverted to user-saved preferences."), INFO, SUCCESS, 3), - - prefs_reset_to_defaults = msg( + + prefs_reset_to_defaults=msg( ("Preferences reset to defaults. You still need to Save or Apply."), INFO, SUCCESS, 3), - - prefs_notice_enctoself = msg( + + prefs_notice_enctoself=msg( ("If you want Encrypt to Self on in Symmetric mode, you must set\n" - "Encryption Type to 'Both'."), + "Encryption Type to 'Both'."), INFO, INFO), - - prefs_notice_addsig = msg( + + prefs_notice_addsig=msg( ("If you want Add Signature on in Symmetric mode, you must also enable\n" - "Advanced."), + "Advanced."), INFO, INFO), - - prefs_notice_enc_both = msg( + + prefs_notice_enc_both=msg( ("In order for both encryption types to be on by default, Advanced will also be\n" - "turned on, whether or not you select it now."), + "turned on, whether or not you select it now."), INFO, INFO), - - ) + +) diff --git a/modules/prefs.py b/modules/prefs.py index fc42229..55fbd6c 100644 --- a/modules/prefs.py +++ b/modules/prefs.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # This file is part of Pyrite. # Last file mod: 2013/09/15 @@ -20,11 +19,10 @@ # # You should have received a copy of the GNU General Public License # along with Pyrite. If not, see . -# -#------------------------------------------------------------------------------ # StdLib: import gi + gi.require_version('GLib', '2.0') gi.require_version('Gdk', '3.0') gi.require_version('Gtk', '3.0') @@ -40,17 +38,15 @@ from .messages import PREFS_MESSAGE_DICT as MESSAGE_DICT - class Preferences: """Preferences system. Try to read preferences from user preferences file; failing that, initialize good defaults. This class also includes the Preferences-setting window. """ - - + def __init__(self, reset_defaults=False): - + try: if reset_defaults: raise Exception @@ -60,7 +56,7 @@ def __init__(self, reset_defaults=False): raise Exception self.p = dict(pickle.load(f)) stderr.write("Pyrite loaded preferences from file {!r}\n".format(cfg.USERPREF_FILE)) - + except: stderr.write("Pyrite loaded default preferences\n") # Default preferences @@ -95,92 +91,90 @@ def __init__(self, reset_defaults=False): errfntsize=7, color_fg='#000000000000', color_bg='#ffffffffffff') - - + def infobar(self, id, filename=None, customtext=None): """Popup a new auto-hiding InfoBar.""" - + # CB for destroy timeout def destroy_ibar(): self.ibar_timeout = 0 self.ibar.destroy() - self.window.resize(1,1) - + self.window.resize(1, 1) + # If infobar already active: delete old timeout, destroy old ibar if self.ibar_timeout > 0: GObject.source_remove(self.ibar_timeout) destroy_ibar() - + # Find the needed dictionary inside our message dict, by id MSG = MESSAGE_DICT[id] # Use value from MSG type & icon to lookup Gtk constant, e.g. Gtk.MessageType.INFO - msgtype = cfg.MSGTYPES[ MSG['type'] ] - imgtype = cfg.IMGTYPES[ MSG['icon'] ] + msgtype = cfg.MSGTYPES[MSG['type']] + imgtype = cfg.IMGTYPES[MSG['icon']] # Replace variables in message text & change text color message = ("" + MSG['text'].format(filename=filename, customtext=customtext) + "") - + # Now that we have all the data we need, START creating! - self.ibar = Gtk.InfoBar() + self.ibar = Gtk.InfoBar() self.ibar.set_message_type(msgtype) - self.vbox_ib.pack_end (self.ibar, False, False, 0) - img = Gtk.Image() - img.set_from_stock (imgtype, Gtk.IconSize.LARGE_TOOLBAR) - label = Gtk.Label() - label.set_markup (message) - content = self.ibar.get_content_area() - content.pack_start (img, False, False, 0) - content.pack_start (label, False, False, 0) - img.show () - label.show () - self.ibar.show () - self.ibar_timeout = GLib.timeout_add_seconds(MSG['timeout'], destroy_ibar) - - + self.vbox_ib.pack_end(self.ibar, False, False, 0) + img = Gtk.Image() + img.set_from_stock(imgtype, Gtk.IconSize.LARGE_TOOLBAR) + label = Gtk.Label() + label.set_markup(message) + content = self.ibar.get_content_area() + content.pack_start(img, False, False, 0) + content.pack_start(label, False, False, 0) + img.show() + label.show() + self.ibar.show() + self.ibar_timeout = GLib.timeout_add_seconds(MSG['timeout'], destroy_ibar) + def open_preferences_window(self, parentwindow): """Show the preferences window. Duh.""" self.ibar_timeout = 0 builder = Gtk.Builder() builder.add_from_file(cfg.ASSETDIR + 'ui/preferences.glade') # Main window - self.window = builder.get_object('window1') - self.btn_save = builder.get_object('btn_save') - self.btn_apply = builder.get_object('btn_apply') - self.vbox_ib = builder.get_object('vbox_ib') + self.window = builder.get_object('window1') + self.btn_save = builder.get_object('btn_save') + self.btn_apply = builder.get_object('btn_apply') + self.vbox_ib = builder.get_object('vbox_ib') # Main Operation Mode - self.cb_opmode = builder.get_object('cb_opmode') + self.cb_opmode = builder.get_object('cb_opmode') # Engine - self.cb_backend = builder.get_object('cb_backend') + self.cb_backend = builder.get_object('cb_backend') # Enc/Dec Mode - self.cb_enctype = builder.get_object('cb_enctype') - self.tg_advanced = builder.get_object('tg_advanced') - self.tg_enctoself = builder.get_object('tg_enctoself') - self.cb_cipher = builder.get_object('cb_cipher') - self.tg_addsig = builder.get_object('tg_addsig') + self.cb_enctype = builder.get_object('cb_enctype') + self.tg_advanced = builder.get_object('tg_advanced') + self.tg_enctoself = builder.get_object('tg_enctoself') + self.cb_cipher = builder.get_object('cb_cipher') + self.tg_addsig = builder.get_object('tg_addsig') # Mode-Independent - self.cb_digest = builder.get_object('cb_digest') - self.tg_defkey = builder.get_object('tg_defkey') - self.ent_defkey = builder.get_object('ent_defkey') - self.cb_txtoutput = builder.get_object('cb_txtoutput') - self.tg_expander = builder.get_object('tg_expander') + self.cb_digest = builder.get_object('cb_digest') + self.tg_defkey = builder.get_object('tg_defkey') + self.ent_defkey = builder.get_object('ent_defkey') + self.cb_txtoutput = builder.get_object('cb_txtoutput') + self.tg_expander = builder.get_object('tg_expander') # Sign/Verify Mode - self.tg_svoutfiles = builder.get_object('tg_svoutfiles') - self.cb_text_sigmode= builder.get_object('cb_text_sigmode') - self.cb_file_sigmode= builder.get_object('cb_file_sigmode') + self.tg_svoutfiles = builder.get_object('tg_svoutfiles') + self.cb_text_sigmode = builder.get_object('cb_text_sigmode') + self.cb_file_sigmode = builder.get_object('cb_file_sigmode') # Display - self.tg_taskstatus = builder.get_object('tg_taskstatus') - self.tg_verbose = builder.get_object('tg_verbose') - self.tg_wrap = builder.get_object('tg_wrap') - self.tg_opc_slider = builder.get_object('tg_opc_slider') - self.sp_opacity = builder.get_object('sp_opacity') - self.sp_msgfntsize = builder.get_object('sp_msgfntsize') - self.sp_errfntsize = builder.get_object('sp_errfntsize') - self.btn_color_fg = builder.get_object('btn_color_fg') - self.btn_color_bg = builder.get_object('btn_color_bg') + self.tg_taskstatus = builder.get_object('tg_taskstatus') + self.tg_verbose = builder.get_object('tg_verbose') + self.tg_wrap = builder.get_object('tg_wrap') + self.tg_opc_slider = builder.get_object('tg_opc_slider') + self.sp_opacity = builder.get_object('sp_opacity') + self.sp_msgfntsize = builder.get_object('sp_msgfntsize') + self.sp_errfntsize = builder.get_object('sp_errfntsize') + self.btn_color_fg = builder.get_object('btn_color_fg') + self.btn_color_bg = builder.get_object('btn_color_bg') # TODO: Advanced tab - #self.tg_args_gpg_e = builder.get_object('tg_args_gpg_e') - #self.en_args_gpg_e = builder.get_object('en_args_gpg_e') + # self.tg_args_gpg_e = builder.get_object('tg_args_gpg_e') + # self.en_args_gpg_e = builder.get_object('en_args_gpg_e') # self.set_transient_for(parentwindow) if access(cfg.USERPREF_FILE, R_OK): btn_revert = builder.get_object('btn_revert') @@ -188,38 +182,37 @@ def open_preferences_window(self, parentwindow): self.populate_pref_window_prefs() builder.connect_signals(self) self.window.show() - - + def populate_pref_window_prefs(self): """Set state of widgets in prefs window via preferences.""" # Main Operation Mode - self.cb_opmode.set_active (self.p['opmode']) + self.cb_opmode.set_active(self.p['opmode']) # Engine - self.cb_backend.set_active (self.p['backend']) + self.cb_backend.set_active(self.p['backend']) # Enc/Dec Mode - self.cb_enctype.set_active (self.p['enctype']) - self.tg_advanced.set_active (self.p['advanced']) - self.tg_enctoself.set_active (self.p['enctoself']) - self.cb_cipher.set_active (self.p['cipher']) - self.tg_addsig.set_active (self.p['addsig']) + self.cb_enctype.set_active(self.p['enctype']) + self.tg_advanced.set_active(self.p['advanced']) + self.tg_enctoself.set_active(self.p['enctoself']) + self.cb_cipher.set_active(self.p['cipher']) + self.tg_addsig.set_active(self.p['addsig']) # Mode-Independent - self.cb_digest.set_active (self.p['digest']) - self.tg_defkey.set_active (self.p['defkey']) - self.ent_defkey.set_text (self.p['defkeytxt']) - self.cb_txtoutput.set_active (self.p['txtoutput']) - self.tg_expander.set_active (self.p['expander']) + self.cb_digest.set_active(self.p['digest']) + self.tg_defkey.set_active(self.p['defkey']) + self.ent_defkey.set_text(self.p['defkeytxt']) + self.cb_txtoutput.set_active(self.p['txtoutput']) + self.tg_expander.set_active(self.p['expander']) # Sign/Verify Mode - self.tg_svoutfiles.set_active (self.p['svoutfiles']) - self.cb_text_sigmode.set_active (self.p['text_sigmode']) - self.cb_file_sigmode.set_active (self.p['file_sigmode']) + self.tg_svoutfiles.set_active(self.p['svoutfiles']) + self.cb_text_sigmode.set_active(self.p['text_sigmode']) + self.cb_file_sigmode.set_active(self.p['file_sigmode']) # Display - self.tg_taskstatus.set_active (self.p['taskstatus']) - self.tg_verbose.set_active (self.p['verbose']) - self.tg_wrap.set_active (self.p['wrap']) - self.tg_opc_slider.set_active (self.p['opc_slider']) - self.sp_opacity.set_value (self.p['opacity']) - self.sp_msgfntsize.set_value (self.p['msgfntsize']) - self.sp_errfntsize.set_value (self.p['errfntsize']) + self.tg_taskstatus.set_active(self.p['taskstatus']) + self.tg_verbose.set_active(self.p['verbose']) + self.tg_wrap.set_active(self.p['wrap']) + self.tg_opc_slider.set_active(self.p['opc_slider']) + self.sp_opacity.set_value(self.p['opacity']) + self.sp_msgfntsize.set_value(self.p['msgfntsize']) + self.sp_errfntsize.set_value(self.p['errfntsize']) fg_color = Gdk.Color(0, 0, 0) fg_color.parse(self.p['color_fg']) @@ -227,46 +220,44 @@ def populate_pref_window_prefs(self): bg_color = Gdk.Color(0, 0, 0) bg_color.parse(self.p['color_bg']) - self.btn_color_fg.set_color (fg_color) - self.btn_color_bg.set_color (bg_color) - - + self.btn_color_fg.set_color(fg_color) + self.btn_color_bg.set_color(bg_color) + def capture_current_prefs(self): """Capture current state of widgets in prefs window & save as preferences.""" self.p = { # Main Operation Mode - 'opmode' : self.cb_opmode.get_active(), + 'opmode': self.cb_opmode.get_active(), # Engine - 'backend' : self.cb_backend.get_active(), + 'backend': self.cb_backend.get_active(), # Enc/Dec Mode - 'enctype' : self.cb_enctype.get_active(), - 'advanced' : self.tg_advanced.get_active(), - 'enctoself' : self.tg_enctoself.get_active(), - 'cipher' : self.cb_cipher.get_active(), - 'addsig' : self.tg_addsig.get_active(), + 'enctype': self.cb_enctype.get_active(), + 'advanced': self.tg_advanced.get_active(), + 'enctoself': self.tg_enctoself.get_active(), + 'cipher': self.cb_cipher.get_active(), + 'addsig': self.tg_addsig.get_active(), # Mode-Independent - 'digest' : self.cb_digest.get_active(), - 'defkey' : self.tg_defkey.get_active(), - 'defkeytxt' : self.ent_defkey.get_text(), - 'txtoutput' : self.cb_txtoutput.get_active(), - 'expander' : self.tg_expander.get_active(), + 'digest': self.cb_digest.get_active(), + 'defkey': self.tg_defkey.get_active(), + 'defkeytxt': self.ent_defkey.get_text(), + 'txtoutput': self.cb_txtoutput.get_active(), + 'expander': self.tg_expander.get_active(), # Sign/Verify Mode - 'svoutfiles' : self.tg_svoutfiles.get_active(), + 'svoutfiles': self.tg_svoutfiles.get_active(), 'text_sigmode': self.cb_text_sigmode.get_active(), 'file_sigmode': self.cb_file_sigmode.get_active(), # Display - 'taskstatus' : self.tg_taskstatus.get_active(), - 'verbose' : self.tg_verbose.get_active(), - 'wrap' : self.tg_wrap.get_active(), - 'opc_slider' : self.tg_opc_slider.get_active(), - 'opacity' : self.sp_opacity.get_value(), - 'msgfntsize' : self.sp_msgfntsize.get_value(), - 'errfntsize' : self.sp_errfntsize.get_value(), - 'color_fg' : self.btn_color_fg.get_color().to_string(), - 'color_bg' : self.btn_color_bg.get_color().to_string()} + 'taskstatus': self.tg_taskstatus.get_active(), + 'verbose': self.tg_verbose.get_active(), + 'wrap': self.tg_wrap.get_active(), + 'opc_slider': self.tg_opc_slider.get_active(), + 'opacity': self.sp_opacity.get_value(), + 'msgfntsize': self.sp_msgfntsize.get_value(), + 'errfntsize': self.sp_errfntsize.get_value(), + 'color_fg': self.btn_color_fg.get_color().to_string(), + 'color_bg': self.btn_color_bg.get_color().to_string()} return self.p - - + # Called by Save button def save_prefs(self): """Attempt to save user prefs to homedir prefs file.""" @@ -279,44 +270,37 @@ def save_prefs(self): self.infobar('prefs_save_failed', cfg.USERPREF_FILE) return False return True - - + # Called by Cancel button def action_cancel_prefs(self, w): """Close prefs window without doing anything.""" self.window.destroy() - - + # Called by Revert button def action_revert_prefs(self, w): """Reset state of widgets in prefs window via external preferences file, if avail.""" self.__init__() self.populate_pref_window_prefs() self.infobar('prefs_reverted') - - + # Called by Defaults button def action_default_prefs(self, w): """Reset state of widgets in prefs window to predefined defaults.""" self.__init__(reset_defaults=True) self.populate_pref_window_prefs() self.infobar('prefs_reset_to_defaults') - - + def action_tg_enctoself(self, w): """Show some info when user enables enctoself toggle.""" if w.get_active(): self.infobar('prefs_notice_enctoself') - - + def action_tg_addsig(self, w): """Show some info when user enables addsig toggle.""" if w.get_active(): self.infobar('prefs_notice_addsig') - - + def action_cb_enctype(self, w): """Show some info when user chooses 'Both' in enctype combobox.""" if w.get_active() == 2: self.infobar('prefs_notice_enc_both') - diff --git a/pyrite.py b/pyrite.py index 7a46331..0385bdd 100755 --- a/pyrite.py +++ b/pyrite.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # This file is part of Pyrite. # Last file mod: 2013/09/15 @@ -21,19 +20,16 @@ # You should have received a copy of the GNU General Public License # along with Pyrite. If not, see . # -#------------------------------------------------------------------------------ -# # TODO: # * Icons for for encrypt, decrypt, sign, verify buttons, application # * Undo stack. Blech. Kill me. # * Update notifications # * BUG: First drag/drop onto FileChooserButton fails; 2nd, 3rd, etc succeed. # It's a GTK+ issue. Reported. bugzilla.gnome.org/show_bug.cgi?id=669718 -# -#------------------------------------------------------------------------------ import argparse from sys import argv + import modules.core # Parse command-line arguments @@ -79,13 +75,11 @@ # If no cmdline options specified, let's save some cycles later if len(argv) == 1: args = None - if __name__ == "__main__": - + FeS2 = modules.core.Pyrite(args) try: FeS2.main() except KeyboardInterrupt: print() exit() - From 84ffe879dea5ad18b4875399e68d866c7a5ba933 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Tue, 24 Oct 2023 23:57:26 +0300 Subject: [PATCH 16/34] Fix FILE_CHOOSER_CONFIRMATION_* --- modules/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core.py b/modules/core.py index 011fc42..13f490a 100644 --- a/modules/core.py +++ b/modules/core.py @@ -504,9 +504,9 @@ def confirm_overwrite_callback(self, chooser): self.show_errmsg( "Simultaneously reading from & writing to a file is a baaad idea. " "Choose a different output filename.", parent=chooser) - return Gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN + return Gtk.FileChooserConfirmation.SELECT_AGAIN else: - return Gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM + return Gtk.FileChooserConfirmation.CONFIRM # Generic file chooser for opening or saving def chooser_grab_filename(self, mode, save_suggestion=None): From 607f90bafee49fc44f64eadeb47faa84b9979e4a Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Wed, 25 Oct 2023 00:20:11 +0300 Subject: [PATCH 17/34] GPG now is anyway GPG2 --- README.md | 2 +- modules/core.py | 11 +++++----- modules/crypt_interface.py | 42 ++++++++++---------------------------- modules/messages.py | 10 ++++----- pyrite.desktop | 2 +- ui/about.glade | 2 +- ui/preferences.glade | 7 ++----- 7 files changed, 27 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index 6bda33b..126fc7a 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ FEATURES ---------- Pyrite acts as a frontend for GnuPG, doing symmetric or asymmetric encrypting/decrypting, as well as signing and verifying. Additionally, it can use OpenSSL for simple symmetric encryption/decryption. -Pyrite can operate on text input or can take input and output filenames (text or binary) to pass directly to the backend program (i.e., gpg/gpg2 or openssl). +Pyrite can operate on text input or can take input and output filenames (text or binary) to pass directly to the backend program (i.e., gpg or openssl). As you can see from the screenshots, Pyrite can utilize virtually all of the encrypting features of GnuPG -- you can mix and match passphrase & public-key encryption & signing with one file, just like gpg, which will require interacting with your gpg-agent. Or you can keep it simple and just use a passphrase as a shared key, in which case gpg-agent is bypassed and you only have to type the passphrase once. diff --git a/modules/core.py b/modules/core.py index 13f490a..eb17a99 100644 --- a/modules/core.py +++ b/modules/core.py @@ -272,17 +272,18 @@ def infobar(self, id, filename=None, customtext=None, vbox=None): def instantiate_xface(self, preferred=None, startup=False): """Instantiate Gpg or Openssl interface.""" - b = ['gpg2', 'gpg', 'openssl'] + b = ['gpg', 'openssl'] # self.p['backend'] contains 0, 1, or 2, corresponding to the above items in b # Desired: convert the number setting to the human-readable name and store as b b = b[self.p['backend']] # If we weren't passed preferred argument, set desired interface to backend pref - if not preferred: preferred = b + if not preferred: + preferred = b # Loading gpg - def gpg(backend_pref=b, fallback=False): - self.x = crypt_interface.Gpg(firstchoice=backend_pref) + def gpg(fallback=False): + self.x = crypt_interface.Gpg() self.engine = self.x.GPG_BINARY.upper() self.g_mengine.set_label("Use OpenSSL as Engine") if fallback: @@ -293,7 +294,7 @@ def gpg(backend_pref=b, fallback=False): def openssl(fallback=False): self.x = crypt_interface.Openssl() self.engine = 'OpenSSL' - self.g_mengine.set_label("Use GnuPG as Engine") + self.g_mengine.set_label("Use GnuPG2 as Engine") if fallback: self.g_mengine.set_sensitive(False) self.infobar('engine_gpg_missing') diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index f27f285..c3a0464 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -35,10 +35,10 @@ def flatten_list_to_stderr(list): class Gpg(): - """GPG/GPG2 interface for encryption/decryption/signing/verifying. + """GPG interface for encryption/decryption/signing/verifying. - First thing: use subprocess module to call a gpg or gpg2 process, ensuring - that one of them is available on the system; if not, of course we have to + First thing: use subprocess module to call a gpg process, ensuring + that it is available on the system; if not, of course we have to quit (raise exception). Either way, that's all for __init__. See the docstring for the main method -- gpg() -- for next steps. @@ -46,39 +46,19 @@ class Gpg(): Security: Xface.gpg() can take a passphrase for symmetric enc/dec as an argument, but it never stores that passphrase on disk; the passphrase is passed to gpg via an os file descriptor. If any access to your secret key is - required, gpg() invokes gpg/gpg2 with gpg-agent enabled. + required, gpg() invokes gpg with gpg-agent enabled. """ - def __init__(self, show_version=True, firstchoice='gpg2'): - """Confirm we can run gpg or gpg2.""" + def __init__(self, show_version=True): + """Confirm we can run gpg.""" - def gpg1(): + try: self.vers = Popen(['gpg', '--version'], stdout=PIPE).communicate()[0] self.GPG_BINARY = 'gpg' - - def gpg2(): - self.vers = Popen(['gpg2', '--version'], stdout=PIPE).communicate()[0] - self.GPG_BINARY = 'gpg2' - - if firstchoice == 'gpg': - try: - gpg1() - except: - try: - gpg2() - except: - stderr.write("gpg, gpg2 not found on your system.\n\n") - raise - else: - try: - gpg2() - except: - try: - gpg1() - except: - stderr.write("gpg, gpg2 not found on your system.\n\n") - raise + except: + stderr.write("gpg not found on your system.\n\n") + raise # To show or not to show version info if show_version: @@ -113,7 +93,7 @@ def gpg( alwaystrust=False, # Add '--trust-model always'? yes=True # Add '--yes'? (will overwrite files) ): - """Build a gpg cmdline and then launch gpg/gpg2, saving output appropriately. + """Build a gpg cmdline and then launch gpg, saving output appropriately. This method inspects the contents of class attr 'io' -- a dict object that should contain all of the following keys, at least initialized to 0 or '': diff --git a/modules/messages.py b/modules/messages.py index 3e00b83..5f2a6f8 100644 --- a/modules/messages.py +++ b/modules/messages.py @@ -37,20 +37,20 @@ def msg(text, type, icon, timeout=5): # Backend Engine engine_openssl_missing=msg( - ("Shockingly, your system does not appear to have OpenSSL."), + ("Your system does not appear to have OpenSSL."), INFO, WARNING), engine_gpg_missing=msg( ("GnuPG not found. Operating in OpenSSL fallback-mode.\n" - "To make full use of this program you need either gpg or gpg2 installed.\n" - "Without one of them, you won't have access key-based functions like\n" + "To make full use of this program you need gpg installed.\n" + "Without it, you won't have access key-based functions like\n" "asymmetric encryption or singing."), INFO, WARNING, 20), engine_all_missing=msg( - ("This program requires one of: gpg, gpg2, or openssl\n" + ("This program requires one of: gpg or openssl\n" "None of these were found on your system. You can look around\n" - "the interface, but to have real fun you'll need to install gpg or gpg2\n" + "the interface, but to have real fun you'll need to install gpg\n" "from your linux distribution's software repository."), ERROR, WARNING, 0), diff --git a/pyrite.desktop b/pyrite.desktop index 435173e..490d05c 100644 --- a/pyrite.desktop +++ b/pyrite.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Name=Pyrite -Comment=OpenSSL & GnuPG (gpg/gpg2) encryption/signing front-end +Comment=OpenSSL & GnuPG (gpg) encryption/signing front-end Exec=pyrite Terminal=false Type=Application diff --git a/ui/about.glade b/ui/about.glade index dcacf58..1c2fdff 100644 --- a/ui/about.glade +++ b/ui/about.glade @@ -9,7 +9,7 @@ True Pyrite Copyright © 2012, 2013 Ryan Sawhill Aroha - GnuPG (gpg/gpg2) encrypting, decrypting, signing, and verifying + GnuPG (gpg) encrypting, decrypting, signing, and verifying http://github.com/ryran/pyrite This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/ui/preferences.glade b/ui/preferences.glade index 84ab862..a86ac7a 100644 --- a/ui/preferences.glade +++ b/ui/preferences.glade @@ -127,13 +127,10 @@ - gpg2 + gpg - gpg - - - openssl + openssl From 89e3ed9b27de93252c29f8bb3a454b5058e498bb Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Wed, 25 Oct 2023 00:23:19 +0300 Subject: [PATCH 18/34] core.py optimize --- modules/core.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/core.py b/modules/core.py index eb17a99..ff15cb4 100644 --- a/modules/core.py +++ b/modules/core.py @@ -272,13 +272,12 @@ def infobar(self, id, filename=None, customtext=None, vbox=None): def instantiate_xface(self, preferred=None, startup=False): """Instantiate Gpg or Openssl interface.""" - b = ['gpg', 'openssl'] - # self.p['backend'] contains 0, 1, or 2, corresponding to the above items in b - # Desired: convert the number setting to the human-readable name and store as b - b = b[self.p['backend']] - # If we weren't passed preferred argument, set desired interface to backend pref if not preferred: + b = ['gpg', 'openssl'] + # self.p['backend'] contains 0, 1, or 2, corresponding to the above items in b + # Desired: convert the number setting to the human-readable name and store as b + b = b[self.p['backend']] preferred = b # Loading gpg From 147172f1653484fd5853a6372d4997fab4dc12d6 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Wed, 25 Oct 2023 01:03:32 +0300 Subject: [PATCH 19/34] core.py migrate GLib.IO_* --- modules/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/core.py b/modules/core.py index ff15cb4..9d9ce86 100644 --- a/modules/core.py +++ b/modules/core.py @@ -1194,7 +1194,7 @@ def launchxface(self, action): self.x.io['stderr'] = pipe() GLib.io_add_watch( self.x.io['stderr'][0], - GLib.IO_IN | GLib.IO_HUP, + GLib.IOCondition.IN | GLib.IOCondition.HUP, self.update_task_status) if self.engine in 'OpenSSL': @@ -1211,7 +1211,7 @@ def launchxface(self, action): self.x.io['gstatus'] = pipe() GLib.io_add_watch( self.x.io['gstatus'][0], - GLib.IO_IN | GLib.IO_HUP, + GLib.IOCondition.IN | GLib.IOCondition.HUP, self.update_task_status, 'term') # ATTEMPT EN-/DECRYPTION w/GPG Thread( @@ -1335,7 +1335,7 @@ def update_task_status(self, fd, condition, output='task'): """Read data waiting in file descriptor; close fd if other end hangs up.""" # If there's data to be read, let's read it - if condition == GLib.IO_IN: + if condition == GLib.IOCondition.IN: if output in 'task': # Output to Task Status b = read(fd, 1024).decode('utf-8') @@ -1347,7 +1347,7 @@ def update_task_status(self, fd, condition, output='task'): return True # If other end of pipe hangs up, close our fd and destroy the watcher - elif condition == GLib.IO_HUP: + elif condition == GLib.IOCondition.HUP: if output in 'term': stderr.write("\n") close(fd) From 25eef71918f17b259f1d2ff6e4f19bb38bd90b16 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 13:07:35 +0300 Subject: [PATCH 20/34] core.py remove ---- --- modules/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/core.py b/modules/core.py index 9d9ce86..8acc8a1 100644 --- a/modules/core.py +++ b/modules/core.py @@ -176,7 +176,7 @@ def __init__(self, cmdlineargs): self.g_pass.set_sensitive(False) self.g_recip.set_sensitive(False) - # ------------------------------ LOAD PREFERENCES AND SET WIDGET STATES! + # LOAD PREFERENCES AND SET WIDGET STATES! self.preferences = prefs.Preferences() # Make a clone of preferences dictionary @@ -1328,7 +1328,7 @@ def launchxface(self, action): action = 'sign' self.infobar('x_generic_failed_textmode', customtext=action) - # ------------------------------------------ HELPERS FOR MAIN XFACE FUNCTION + # HELPERS FOR MAIN XFACE FUNCTION # CB for GLib.io_add_watch() def update_task_status(self, fd, condition, output='task'): From 4180a28ed3e9098c48652721480d121c07773de2 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 13:08:32 +0300 Subject: [PATCH 21/34] README.md simplify --- README.md | 56 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 126fc7a..f4ddc0e 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@ Pyrite - Python/GTK+ encryption/signing frontend for GnuPG and OpenSSL ![](http://b19.org/linux/pyrite/1enc_txt.png) -FEDORA/RHEL7 INSTALLATION -------------------------- +## FEDORA/RHEL7 INSTALLATION There's an RPM (and yum repository) @ [people.redhat.com/rsawhill/rpms](https://people.redhat.com/rsawhill/rpms/). To configure it and install Pyrite, simply run the following as root: ``` @@ -21,8 +20,8 @@ Requirements and package names: - PGP `gnupg2` or OpenSSL `openssl` -DEBIAN/UBUNTU/OTHER LINUX INSTALLATION --------------------------------------- +## DEBIAN/UBUNTU/OTHER LINUX INSTALLATION + There is a simple interactive shell installer. If requirements are met, clone the Pyrite repo with `git clone https://github.com/ryran/pyrite.git` **OR** [download a zip of the source](https://github.com/ryran/pyrite/archive/master.zip). @@ -30,18 +29,18 @@ If requirements are met, clone the Pyrite repo with `git clone https://github.co From the root source folder execute the interactive `INSTALL` script. -MORE SCREENSHOTS (v1.0.1): -------------------------------- +## MORE SCREENSHOTS (v1.0.1): + ![](http://b19.org/linux/pyrite/2clearsign_txt.png) ![](http://b19.org/linux/pyrite/3enc_prog.png) ![](http://b19.org/linux/pyrite/4dec_txt.png) ![](http://b19.org/linux/pyrite/5openssl_txt.png) ![](http://b19.org/linux/pyrite/6prefs.png) -**`pyrite` command-line options:** +## `pyrite` command-line options +Type `pyrite --help`: ``` -[rsaw:~]$ pyrite --help usage: pyrite [-h] [-d | -t] [-e | -s] [-c] [-r RECIP] [-k KEYUID] [-b {gpg,openssl}] [INPUT] @@ -68,13 +67,15 @@ optional arguments: ``` -FEATURES ----------- -Pyrite acts as a frontend for GnuPG, doing symmetric or asymmetric encrypting/decrypting, as well as signing and verifying. Additionally, it can use OpenSSL for simple symmetric encryption/decryption. +## FEATURES + +Pyrite acts as a frontend for GnuPG, doing symmetric or asymmetric encrypting/decrypting, as well as signing and verifying. +Additionally, it can use OpenSSL for simple symmetric encryption/decryption. Pyrite can operate on text input or can take input and output filenames (text or binary) to pass directly to the backend program (i.e., gpg or openssl). -As you can see from the screenshots, Pyrite can utilize virtually all of the encrypting features of GnuPG -- you can mix and match passphrase & public-key encryption & signing with one file, just like gpg, which will require interacting with your gpg-agent. Or you can keep it simple and just use a passphrase as a shared key, in which case gpg-agent is bypassed and you only have to type the passphrase once. +As you can see from the screenshots, Pyrite can utilize virtually all the encrypting features of GnuPG -- you can mix and match passphrase & public-key encryption & signing with one file, just like gpg, which will require interacting with your gpg-agent. +Or you can keep it simple and just use a passphrase as a shared key, in which case gpg-agent is bypassed and you only have to type the passphrase once. Also shown in the screenshots is a Sign/Verify mode, where you can choose between the three types of signing: normal (Pyrite calls it "embedded"), where a signed copy of the message is created; clearsign, where the message is wrapped with a plaintext ASCII sig; or detached-sign, where a separate sig file is created. @@ -82,34 +83,37 @@ If you're operating directly on files (in sign or encrypt mode) instead of ASCII Not shown in the screenshots is drag & drop. You can drag text files onto the Message area and they are loaded up and you can drag text or binary files onto the *Input File For Direct Operation* button to set that. -If you end up working on very large input, you'll get a chance to *really* see the progress bar + pause/cancel buttons. At the moment the progress bar doesn't report actual progress (that's coming), but the buttons do what they advertise, pausing or canceling the backend processing. +If you end up working on very large input, you'll get a chance to *really* see the progress bar + pause/cancel buttons. +At the moment the progress bar doesn't report actual progress (that's coming), but the buttons do what they advertise, pausing or canceling the backend processing. -To top it all off, everything is configurable. There's a preferences dialog that lets you play with all the settings, from tweaking gpg verbosity to setting the default operating mode to choosing your favorite cipher to configuring font size/color and window opacity. +To top it all off, everything is configurable. +There's a preferences dialog that lets you play with all the settings, from tweaking gpg verbosity to setting the default operating mode to choosing your favorite cipher to configuring font size/color and window opacity. If you find yourself wondering about a particular feature, just hover your mouse over its widget -- there are detailed tooltips for everything. -BUGS ----------- +## BUGS + 1) After launching Pyrite, the **first** drag/drop of a file onto the *Input File For Direct Operation* GtkFileChooserButton fails. After that the button works properly. I've been seeking out expertise on this weird bug but I haven't gotten anywhere. If you have any hints, hit me up, or check out [my post about it on stackoverflow](https://stackoverflow.com/questions/9047844/pygtk-troubles-with-drag-and-drop-file-to-gtkfilechooserbutton). 2) No undo. It wasn't a top priority at the beginning, but I think it's pretty essential for an application that basically contains a text editor to have an undo/redo stack. I'll do it eventually. -BACKGROUND ----------- +## BACKGROUND -The original goal of this project was to make symmetric {en,de}cryption more accessible and easy to use. While GPG rocks if you're comfortable on the commandline (for both symmetric & public-key), and there are GUI encryption options for public-key encryption (seahorse-plugins for nautilus being the best, in my opinion), there's not much out there for people who need to do the simplest kind of encryption -- with a shared passphrase. +The original goal of this project was to make symmetric {en,de}cryption more accessible and easy to use. +While GPG rocks if you're comfortable on the commandline (for both symmetric & public-key), and there are GUI encryption options for public-key encryption (seahorse-plugins for nautilus being the best, in my opinion), there's not much out there for people who need to do the simplest kind of encryption -- with a shared passphrase. -After creating a few simple apps with BASH scripting, I decided it was time to learn Python. After the first few days I was in love. +After creating a few simple apps with BASH scripting, I decided it was time to learn Python. +After the first few days I was in love. -Long story short, after a couple weeks of learning, I released my first version of this project in January 2012, quickly added public-key encryption, signing, & verifying, and have been improving it ever since. This being my first learning experience with GTK+, I have lots more to learn, but I'm damn proud of Pyrite. +Long story short, after a couple weeks of learning, I released my first version of this project in January 2012, quickly added public-key encryption, signing, & verifying, and have been improving it ever since. +This being my first learning experience with GTK+, I have lots more to learn, but I'm damn proud of Pyrite. PLEASE contact me (or [post a new issue on the tracker](/ryran/pyrite/issues)) with any suggestions, feedback, bug reports, or questions! -AUTHORS -------- +## AUTHORS As far as direct contributions go, so far it's just me, [ryran](/ryran), aka rsaw, aka [Ryan Sawhill Aroha](http://b19.org). @@ -118,8 +122,7 @@ The project could really use a little assistance from an artist -- it doesn't ha -LICENSE -------- +## LICENSE Copyright (C) 2012, 2013 [Ryan Sawhill Aroha](http://b19.org) @@ -145,5 +148,6 @@ Oh. You must be wondering why the name [*Pyrite*](https://en.wikipedia.org/wiki/ Well, I'll let my friend River--who came up with the name--explain it to you: -*"It should be 'Pyrite', because people think they are getting your data, but really it's just gibberish to them. Fool's gold."* +> It should be 'Pyrite', because people think they are getting your data, but really it's just gibberish to them. +> Fool's gold. From a5c785b89aeed5db0c4c6deed0136b99b0a7c3ae Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 13:30:16 +0300 Subject: [PATCH 22/34] core.py fix PEP warnings --- modules/core.py | 82 +++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/modules/core.py b/modules/core.py index 8acc8a1..3f25e31 100644 --- a/modules/core.py +++ b/modules/core.py @@ -69,6 +69,12 @@ def show_errmsg(self, msg, dialog=Gtk.MessageType.ERROR, parent=None): def __init__(self, cmdlineargs): """Build GUI interface from XML, etc.""" + self.x = None + self.filemode_saved_buff = None + self.encdec_sig_state_active = False + self.encdec_sig_state_sensitive = False + self.canceled = False + self.paused = False # Use GtkBuilder to build our GUI from the XML file builder = Gtk.Builder() @@ -215,7 +221,8 @@ def __init__(self, cmdlineargs): self.g_recip.set_text(a.recipients) self.g_asymmetric.set_active(True) if a.symmetric: - if a.recipients: self.g_advanced.set_active(True) + if a.recipients: + self.g_advanced.set_active(True) self.g_symmetric.set_active(True) if a.defaultkey: self.g_defaultkey.set_text(a.defaultkey) @@ -304,9 +311,11 @@ def openssl(fallback=False): def err_allmissing(): self.infobar('engine_all_missing') self.g_mengine.set_sensitive(False) - for w in self.g_encrypt, self.g_decrypt: w.set_sensitive(False) + for w in self.g_encrypt, self.g_decrypt: + w.set_sensitive(False) - class dummy: pass + class dummy: + pass self.x = dummy() self.x.io = dict(stdin='', stdout='', gstatus=0, infile=0, outfile=0) @@ -334,7 +343,8 @@ class dummy: pass self.g_window.set_title("Pyrite [{}]".format(self.engine)) self.buff2.set_text("Any output generated from calls to {} will be " - "displayed here.\n\nIn the View menu you can change " + "displayed here.\n\n" + "In the View menu you can change " "the verbosity level, hide this pane, or simply change " "the font size.".format(self.engine.lower())) @@ -455,7 +465,7 @@ def get_file_path_from_dnd_dropped_uri(self, uri): return path def set_stdstatus(self): - """Set a standard mode-depenedent status message.""" + """Set a standard mode-dependent status message.""" self.g_statusbar.pop(self.status) if self.g_signverify.get_active(): s = "Enter message to sign or verify" @@ -536,7 +546,8 @@ def chooser_grab_filename(self, mode, save_suggestion=None): # Setup overwrite-confirmation cb + current filename chooser.set_do_overwrite_confirmation(True) chooser.connect('confirm-overwrite', self.confirm_overwrite_callback) - if save_suggestion: chooser.set_current_name(save_suggestion) + if save_suggestion: + chooser.set_current_name(save_suggestion) if chooser.run() == Gtk.ResponseType.OK: filename = chooser.get_filename() @@ -565,7 +576,7 @@ def filemode_get_outfile(self, mode): self.launchxface(mode) def initiate_filemode(self): - """Ensure read access of file set by chooserwidget and notify user of next steps.""" + """Ensure read access of file set by chooser widget and notify user of next steps.""" # Prompt for filename but err out if file can't be read infile = self.g_chooserbtn.get_filename() @@ -648,7 +659,7 @@ def cleanup_filemode(self, *args): def action_quit(self, w): """Shutdown application and any child process.""" self.quiting = True - if self.x.childprocess and self.x.childprocess.returncode == None: + if self.x.childprocess and self.x.childprocess.returncode is None: if self.paused: self.x.childprocess.send_signal(SIGCONT) self.x.childprocess.terminate() @@ -716,7 +727,8 @@ def applypref(*args): # If success, destroy pref window, import new prefs, show infobar self.preferences.window.destroy() self.p = self.preferences.p - if self.x.io['infile']: self.cleanup_filemode() + if self.x.io['infile']: + self.cleanup_filemode() self.instantiate_xface(startup=True) self.infobar('preferences_apply_success', cfg.USERPREF_FILE) @@ -1084,11 +1096,12 @@ def action_pause_child_process(self, btn): # MAIN XFACE FUNCTION def launchxface(self, action): """Manage I/O between Gtk objects and our GpgXface or OpensslXface object.""" + # User Canceled self.canceled = False self.paused = False self.x.childprocess = None - ### PREPARE Xface ARGS + # PREPARE Xface ARGS passwd = None recip = None localuser = None @@ -1117,7 +1130,8 @@ def launchxface(self, action): asymmetric = self.g_asymmetric.get_active() if asymmetric: recip = self.g_recip.get_text() - if not recip: recip = None # If recip was '' , set to None + if not recip: + recip = None # If recip was '' , set to None # cipher, base64 cipher = self.grab_activetext_combobox(self.g_cipher) base64 = self.g_plaintext.get_active() @@ -1136,11 +1150,11 @@ def launchxface(self, action): # localuser if self.g_chk_defkey.get_active(): localuser = self.g_defaultkey.get_text() - if not localuser: localuser = None + if not localuser: + localuser = None # INITIAL FILE INPUT MODE PREP if self.x.io['infile'] and not self.x.io['outfile']: - if base64 or action in 'clearsign': outfile = self.x.io['infile'] + '.asc' elif self.engine in 'OpenSSL': @@ -1164,22 +1178,22 @@ def launchxface(self, action): return working_widgets = self.working_widgets_filemode - for w in working_widgets: w.set_sensitive(False) + for w in working_widgets: + w.set_sensitive(False) self.ib_filemode.hide() # FILE INPUT MODE PREP WHEN ALREADY HAVE OUTPUT FILE elif self.x.io['infile'] and self.x.io['outfile']: - working_widgets = self.working_widgets_filemode - for w in working_widgets: w.set_sensitive(False) + for w in working_widgets: + w.set_sensitive(False) self.ib_filemode.hide() # TEXT INPUT MODE PREP else: - working_widgets = self.working_widgets_textmode - for w in working_widgets: w.set_sensitive(False) - + for w in working_widgets: + w.set_sensitive(False) # Save textview buffer to Xface stdin self.x.io['stdin'] = self.buff.get_text(self.buff.get_start_iter(), self.buff.get_end_iter(), @@ -1222,8 +1236,9 @@ def launchxface(self, action): # Wait for subprocess to finish or for Cancel button to be clicked c = 0 - while not self.x.childprocess or self.x.childprocess.returncode == None: - if self.canceled: break + while not self.x.childprocess or self.x.childprocess.returncode is None: + if self.canceled: + break if c % 15 == 0 and not self.paused: self.g_progbar.pulse() Gtk.main_iteration() @@ -1232,46 +1247,34 @@ def launchxface(self, action): # If application is shutting down return # Restore widgets to normal states - for w in working_widgets: w.set_sensitive(True) + for w in working_widgets: + w.set_sensitive(True) self.show_working_progress(False) # FILE INPUT MODE CLEANUP if self.x.io['infile']: - if self.canceled: # User Canceled! - self.ib_filemode.show() - if action in {'enc', 'dec'}: action = "{}rypt".format(action.title()) elif action in {'embedsign', 'clearsign', 'detachsign'}: action = "Sign" elif action in 'verify': action = action.title() - self.infobar('x_canceled_filemode', customtext=action) - elif self.x.childprocess.returncode == 0: # File Success! - if self.engine in 'OpenSSL' and action in 'enc': self.infobar('x_opensslenc_success_filemode', self.x.io['outfile'], cipher) - elif action in {'enc', 'dec'}: self.infobar('x_crypt_success_filemode', self.x.io['outfile'], action) - elif action in {'embedsign', 'clearsign'}: self.infobar('x_sign_success_filemode', self.x.io['outfile']) - elif action in 'detachsign': self.infobar('x_detachsign_success_filemode', self.x.io['outfile']) - elif action in 'verify': self.infobar('x_verify_success') - self.cleanup_filemode() - else: # File Fail! - self.ib_filemode.show() if action in 'verify': self.infobar('x_verify_failed') @@ -1287,24 +1290,18 @@ def launchxface(self, action): # TEXT INPUT MODE CLEANUP else: - self.set_stdstatus() self.g_msgtxtview.set_sensitive(True) self.fix_msgtxtviewcolor(True) - if self.canceled: # User Canceled! - if action in {'enc', 'dec'}: action = "{}rypt".format(action.title()) elif action in {'embedsign', 'clearsign', 'detachsign'}: action = "Sign" elif action in 'verify': action = action.title() - self.infobar('x_canceled_textmode', customtext=action) - elif self.x.childprocess.returncode == 0: # Text Success! - if action in 'verify': self.infobar('x_verify_success') else: @@ -1313,9 +1310,7 @@ def launchxface(self, action): self.x.io['stdout'] = 0 if self.engine in 'OpenSSL' and action in 'enc': self.infobar('x_opensslenc_success_textmode', customtext=cipher) - else: # Text Fail! - if action in 'verify': self.infobar('x_verify_failed') return @@ -1374,7 +1369,6 @@ def show_working_progress(self, show=True, action=None): self.g_activityspin.set_visible(True) self.g_activityspin.start() Gtk.main_iteration() - else: # If finished processing: ensure progbar buttons are normal, reset status, stop spinner for w in self.g_cancel, self.g_pause: From 85314de06320c4a00d5f804b04e6f87b4b8ca7d9 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 13:58:16 +0300 Subject: [PATCH 23/34] crypt_interface.py fix PEP issues --- modules/crypt_interface.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index c3a0464..20cb508 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -27,14 +27,14 @@ from time import sleep -def flatten_list_to_stderr(list): +def flatten_list_to_stderr(lines): stderr.write("-" * 79 + "\n") - for item in list: + for item in lines: stderr.write(item + " ") stderr.write("\n\n") -class Gpg(): +class Gpg: """GPG interface for encryption/decryption/signing/verifying. First thing: use subprocess module to call a gpg process, ensuring @@ -96,11 +96,11 @@ def gpg( """Build a gpg cmdline and then launch gpg, saving output appropriately. This method inspects the contents of class attr 'io' -- a dict object that should - contain all of the following keys, at least initialized to 0 or '': + contain all the following keys, at least initialized to 0 or '': stdin # Input text for subprocess infile # Input filename for subprocess, in place of stdin outfile # Output filename if infile was given - io['infile'] should contain a filename OR be set to 0, in which case io'[stdin'] + io['infile'] should contain a filename OR be set to 0, in which case io['stdin'] must contain the input data. If using infile, outfile is not necessarily required, but it's probably a good idea unless you're doing sign-only. @@ -170,7 +170,7 @@ def gpg( if localuser: cmd.append(localuser) else: - cmd.append(self.get_gpgdefaultkey()) + cmd.append(self.get_gpg_default_key()) if recip: while recip[-1] == ' ' or recip[-1] == ';': recip = recip.strip() @@ -233,7 +233,7 @@ def gpg( # Print a separator + the command-arguments to stderr flatten_list_to_stderr(cmd) - # If working direct with files, setup our Popen instance with no stdin + # If working direct with files, set up our Popen instance with no stdin if self.io['infile']: self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][1]) # Otherwise, only difference for Popen is we need the stdin pipe @@ -249,20 +249,20 @@ def gpg( self.io['stdin'] = '' # Close os file descriptors - if fd_pwd_R: close(fd_pwd_R) + if fd_pwd_R: + close(fd_pwd_R) sleep(0.1) # Sleep a bit to ensure everything gets read close(self.io['stderr'][1]) if self.io['gstatus']: close(self.io['gstatus'][1]) - def get_gpgdefaultkey(self): + def get_gpg_default_key(self): """Return key id of first secret key in gpg keyring.""" - return check_output(split( - "{} --list-secret-keys --with-colons --fast-list-mode" - .format(self.GPG_BINARY))).split(':', 5)[4] + cmd = split("{} --list-secret-keys --with-colons --fast-list-mode".format(self.GPG_BINARY)) + return check_output(cmd).split(':', 5)[4] -class Openssl(): +class Openssl: """OpenSSL interface for encryption/decryption. First thing: use subprocess module to call an openssl process, ensuring it @@ -311,11 +311,11 @@ def openssl( """Build an openssl cmdline and then launch it, saving output appropriately. This method inspects the contents of class attr 'io' -- a dict object that should - contain all of the following keys, at least initialized to 0 or '': + contain all the following keys, at least initialized to 0 or '': stdin # Input text for subprocess infile # Input filename for subprocess, in place of stdin outfile # Output filename -- required if infile was given - io['infile'] should contain a filename OR be set to 0, in which case io'[stdin'] + io['infile'] should contain a filename OR be set to 0, in which case io['stdin'] must contain the input data. Whether reading input from infile or stdin, each openssl command's stdout & @@ -332,8 +332,9 @@ def openssl( "to work? ... NOPE. Chuck Testa.\n") raise Exception("infile, outfile must be different") - if cipher: cipher = cipher.lower() - if cipher == None: + if cipher: + cipher = cipher.lower() + if cipher is None: cipher = 'aes-256-cbc' elif cipher == '3des': cipher = 'des-ede3-cbc' @@ -380,7 +381,7 @@ def openssl( # Print a separator + the command-arguments to stderr flatten_list_to_stderr(cmd) - # If working direct with files, setup our Popen instance with no stdin + # If working direct with files, set up our Popen instance with no stdin if self.io['infile']: self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][1]) # Otherwise, only difference for Popen is we need the stdin pipe From c9b48919c5814a8330fe06a29eaef582f57fe97e Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 14:35:02 +0300 Subject: [PATCH 24/34] crypt_interface.py fix get_gpg_default_key() --- modules/crypt_interface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index 20cb508..a96bb64 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -258,8 +258,9 @@ def gpg( def get_gpg_default_key(self): """Return key id of first secret key in gpg keyring.""" - cmd = split("{} --list-secret-keys --with-colons --fast-list-mode".format(self.GPG_BINARY)) - return check_output(cmd).split(':', 5)[4] + cmd = split("gpg --list-secret-keys --with-colons --fast-list-mode") + output = str(check_output(cmd)) + return output.split(':', 5)[4] class Openssl: From f6efab6c6433626f12b9b9912e7156f94bf30686 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 14:39:54 +0300 Subject: [PATCH 25/34] GPG_BINARY is always gpg --- modules/core.py | 4 ++-- modules/crypt_interface.py | 11 +++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/modules/core.py b/modules/core.py index 3f25e31..6d1428f 100644 --- a/modules/core.py +++ b/modules/core.py @@ -290,7 +290,7 @@ def instantiate_xface(self, preferred=None, startup=False): # Loading gpg def gpg(fallback=False): self.x = crypt_interface.Gpg() - self.engine = self.x.GPG_BINARY.upper() + self.engine = 'GPG' self.g_mengine.set_label("Use OpenSSL as Engine") if fallback: self.g_mengine.set_sensitive(False) @@ -346,7 +346,7 @@ class dummy: "displayed here.\n\n" "In the View menu you can change " "the verbosity level, hide this pane, or simply change " - "the font size.".format(self.engine.lower())) + "the font size.".format(self.engine)) self.set_defaults_from_prefs(startup) diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index a96bb64..d83f327 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -55,7 +55,6 @@ def __init__(self, show_version=True): try: self.vers = Popen(['gpg', '--version'], stdout=PIPE).communicate()[0] - self.GPG_BINARY = 'gpg' except: stderr.write("gpg not found on your system.\n\n") raise @@ -133,7 +132,7 @@ def gpg( fd_pwd_R = None fd_pwd_W = None useagent = True - cmd = [self.GPG_BINARY] + cmd = ['gpg'] if self.io['gstatus']: # Status to file descriptor option @@ -206,13 +205,9 @@ def gpg( # Action-independent opts if useagent: - if self.GPG_BINARY in 'gpg': - cmd.append('--use-agent') + cmd.append('--use-agent') else: - if self.GPG_BINARY in 'gpg': - cmd.append('--no-use-agent') - else: - cmd.append('--batch') + cmd.append('--no-use-agent') if localuser: cmd.append('--local-user') cmd.append(localuser) From cf4729277e0001e9091db11ee8c016c2dfb29406 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 14:42:31 +0300 Subject: [PATCH 26/34] core.py gpg2 --- modules/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/core.py b/modules/core.py index 6d1428f..736d1f4 100644 --- a/modules/core.py +++ b/modules/core.py @@ -300,7 +300,7 @@ def gpg(fallback=False): def openssl(fallback=False): self.x = crypt_interface.Openssl() self.engine = 'OpenSSL' - self.g_mengine.set_label("Use GnuPG2 as Engine") + self.g_mengine.set_label("Use GnuPG as Engine") if fallback: self.g_mengine.set_sensitive(False) self.infobar('engine_gpg_missing') From b3cf5e7eb2d973fc2f29b749621e1d22c6710469 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 14:44:05 +0300 Subject: [PATCH 27/34] fix_msgtxtview_color --- modules/core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/core.py b/modules/core.py index 736d1f4..945a429 100644 --- a/modules/core.py +++ b/modules/core.py @@ -439,7 +439,7 @@ def setsensitive_gpgwidgets(x=True): # HELPER FUNCTIONS - def fix_msgtxtviewcolor(self, sensitive): + def fix_msgtxtview_color(self, sensitive): """Change Message area text to black when TextView insensitive.""" if sensitive: fg_color = Gdk.Color(0, 0, 0) @@ -498,7 +498,7 @@ def filemode_enablewidgets(self, x=True): self.g_mpaste, self.g_msgtxtview] for w in widgets: w.set_sensitive(x) - self.fix_msgtxtviewcolor(x) + self.fix_msgtxtview_color(x) # This is called when user tries to copyall, save, or {en,de}crypt/sign/verify def test_msgbuff_isempty(self, message): @@ -1122,7 +1122,7 @@ def launchxface(self, action): return False # Make TextView immutable to changes self.g_msgtxtview.set_sensitive(False) - self.fix_msgtxtviewcolor(False) + self.fix_msgtxtview_color(False) # enctoself enctoself = self.g_enctoself.get_active() @@ -1292,7 +1292,7 @@ def launchxface(self, action): else: self.set_stdstatus() self.g_msgtxtview.set_sensitive(True) - self.fix_msgtxtviewcolor(True) + self.fix_msgtxtview_color(True) if self.canceled: # User Canceled! if action in {'enc', 'dec'}: action = "{}rypt".format(action.title()) From d6c5298a73863e7467fdb1e74dfdbfe1719482f5 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 16:17:40 +0300 Subject: [PATCH 28/34] fix test_file_isbinary and negate to test_file_is_plain_text --- modules/core.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/modules/core.py b/modules/core.py index 945a429..df412d6 100644 --- a/modules/core.py +++ b/modules/core.py @@ -473,12 +473,11 @@ def set_stdstatus(self): s = "Enter message to encrypt or decrypt" self.g_statusbar.push(self.status, s) - def test_file_isbinary(self, filename): + def test_file_is_plain_text(self, filename): """Utilize nix file cmd to determine if filename is binary or text.""" cmd = split("file -b -e soft '{}'".format(filename)) - if check_output(cmd)[:4] in {'ASCI', 'UTF-'}: - return False - return True + output = check_output(cmd) + return output[:4] in (b'ASCI', b'UTF-') def open_in_txtview(self, filename): """Replace contents of msg TextView's TextBuffer with contents of file.""" @@ -594,14 +593,9 @@ def initiate_filemode(self): self.g_plaintext.set_sensitive(True) if self.p['txtoutput'] == 0: # Autodetect - if self.test_file_isbinary(infile): - self.g_plaintext.set_active(False) - else: - self.g_plaintext.set_active(True) - + self.g_plaintext.set_active(self.test_file_is_plain_text(infile)) elif self.p['txtoutput'] == 1: # Always Binary self.g_plaintext.set_active(False) - elif self.p['txtoutput'] == 2: # Always Text self.g_plaintext.set_active(True) From be2ae50c64e92d6cd0a3751092c8397c0c10bedd Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 16:26:04 +0300 Subject: [PATCH 29/34] cleanup file headers --- modules/cfg.py | 4 ---- modules/core.py | 4 ---- modules/crypt_interface.py | 4 ---- modules/messages.py | 4 ---- modules/prefs.py | 4 ---- pyrite.py | 7 ++----- 6 files changed, 2 insertions(+), 25 deletions(-) diff --git a/modules/cfg.py b/modules/cfg.py index d618a38..f58d195 100644 --- a/modules/cfg.py +++ b/modules/cfg.py @@ -1,8 +1,4 @@ #!/usr/bin/env python3 -# -# This file is part of Pyrite. -# Last file mod: 2013/09/15 -# Latest version at # Copyright 2012, 2013 Ryan Sawhill Aroha # # License: diff --git a/modules/core.py b/modules/core.py index df412d6..c8cc7f1 100644 --- a/modules/core.py +++ b/modules/core.py @@ -1,8 +1,4 @@ #!/usr/bin/env python3 -# -# This file is part of Pyrite. -# Last file mod: 2013/09/15 -# Latest version at # Copyright 2012, 2013 Ryan Sawhill Aroha # # License: diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index d83f327..571742b 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -1,8 +1,4 @@ #!/usr/bin/env python3 -# -# This file is part of Pyrite. -# Last file mod: 2013/09/15 -# Latest version at # Copyright 2012, 2013 Ryan Sawhill Aroha # # License: diff --git a/modules/messages.py b/modules/messages.py index 5f2a6f8..c385378 100644 --- a/modules/messages.py +++ b/modules/messages.py @@ -1,8 +1,4 @@ #!/usr/bin/env python3 -# -# This file is part of Pyrite. -# Last file mod: 2013/09/15 -# Latest version at # Copyright 2012, 2013 Ryan Sawhill Aroha # # License: diff --git a/modules/prefs.py b/modules/prefs.py index 55fbd6c..6de3d40 100644 --- a/modules/prefs.py +++ b/modules/prefs.py @@ -1,8 +1,4 @@ #!/usr/bin/env python3 -# -# This file is part of Pyrite. -# Last file mod: 2013/09/15 -# Latest version at # Copyright 2012, 2013 Ryan Sawhill Aroha # # License: diff --git a/pyrite.py b/pyrite.py index 0385bdd..e854efc 100755 --- a/pyrite.py +++ b/pyrite.py @@ -1,8 +1,4 @@ #!/usr/bin/env python3 -# -# This file is part of Pyrite. -# Last file mod: 2013/09/15 -# Latest version at # Copyright 2012, 2013 Ryan Sawhill Aroha # # License: @@ -73,7 +69,8 @@ args = parser.parse_args() # If no cmdline options specified, let's save some cycles later -if len(argv) == 1: args = None +if len(argv) == 1: + args = None if __name__ == "__main__": From 5e9ab4caa9c40e882da86c7649da742d5e6ac87e Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 16:30:48 +0300 Subject: [PATCH 30/34] Remove fixed bug with Drug and Drop --- README.md | 8 ++++---- pyrite.py | 7 ------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f4ddc0e..a37e070 100644 --- a/README.md +++ b/README.md @@ -92,11 +92,11 @@ There's a preferences dialog that lets you play with all the settings, from twea If you find yourself wondering about a particular feature, just hover your mouse over its widget -- there are detailed tooltips for everything. -## BUGS +## BUGS and TODO -1) After launching Pyrite, the **first** drag/drop of a file onto the *Input File For Direct Operation* GtkFileChooserButton fails. After that the button works properly. I've been seeking out expertise on this weird bug but I haven't gotten anywhere. If you have any hints, hit me up, or check out [my post about it on stackoverflow](https://stackoverflow.com/questions/9047844/pygtk-troubles-with-drag-and-drop-file-to-gtkfilechooserbutton). - -2) No undo. It wasn't a top priority at the beginning, but I think it's pretty essential for an application that basically contains a text editor to have an undo/redo stack. I'll do it eventually. +- No undo. It wasn't a top priority at the beginning, but I think it's pretty essential for an application that basically contains a text editor to have an undo/redo stack. I'll do it eventually. +- Icons for encrypt, decrypt, sign, verify buttons, application +- Update notifications ## BACKGROUND diff --git a/pyrite.py b/pyrite.py index e854efc..9854182 100755 --- a/pyrite.py +++ b/pyrite.py @@ -15,13 +15,6 @@ # # You should have received a copy of the GNU General Public License # along with Pyrite. If not, see . -# -# TODO: -# * Icons for for encrypt, decrypt, sign, verify buttons, application -# * Undo stack. Blech. Kill me. -# * Update notifications -# * BUG: First drag/drop onto FileChooserButton fails; 2nd, 3rd, etc succeed. -# It's a GTK+ issue. Reported. bugzilla.gnome.org/show_bug.cgi?id=669718 import argparse from sys import argv From dad7b10037cd594a76ac784bad92f212b91718f4 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 16:38:39 +0300 Subject: [PATCH 31/34] README.md put Features first and remove Background The Background was nice to read but it doesn't add useful info --- README.md | 66 +++++++++++++++++++++---------------------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index a37e070..41df2f1 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,30 @@ Pyrite - Python/GTK+ encryption/signing frontend for GnuPG and OpenSSL ![](http://b19.org/linux/pyrite/1enc_txt.png) +## FEATURES + +Pyrite acts as a frontend for GnuPG, doing symmetric or asymmetric encrypting/decrypting, as well as signing and verifying. +Additionally, it can use OpenSSL for simple symmetric encryption/decryption. + +Pyrite can operate on text input or can take input and output filenames (text or binary) to pass directly to the backend program (i.e., gpg or openssl). + +As you can see from the screenshots, Pyrite can utilize virtually all the encrypting features of GnuPG -- you can mix and match passphrase & public-key encryption & signing with one file, just like gpg, which will require interacting with your gpg-agent. +Or you can keep it simple and just use a passphrase as a shared key, in which case gpg-agent is bypassed and you only have to type the passphrase once. + +Also shown in the screenshots is a Sign/Verify mode, where you can choose between the three types of signing: normal (Pyrite calls it "embedded"), where a signed copy of the message is created; clearsign, where the message is wrapped with a plaintext ASCII sig; or detached-sign, where a separate sig file is created. + +If you're operating directly on files (in sign or encrypt mode) instead of ASCII text in the Pyrite window, you can choose what kind of output you want -- ASCII-armored (base64-encoded) text or normal binary output. + +Not shown in the screenshots is drag & drop. You can drag text files onto the Message area and they are loaded up and you can drag text or binary files onto the *Input File For Direct Operation* button to set that. + +If you end up working on very large input, you'll get a chance to *really* see the progress bar + pause/cancel buttons. +At the moment the progress bar doesn't report actual progress (that's coming), but the buttons do what they advertise, pausing or canceling the backend processing. + +To top it all off, everything is configurable. +There's a preferences dialog that lets you play with all the settings, from tweaking gpg verbosity to setting the default operating mode to choosing your favorite cipher to configuring font size/color and window opacity. + +If you find yourself wondering about a particular feature, just hover your mouse over its widget -- there are detailed tooltips for everything. + ## FEDORA/RHEL7 INSTALLATION There's an RPM (and yum repository) @ [people.redhat.com/rsawhill/rpms](https://people.redhat.com/rsawhill/rpms/). To configure it and install Pyrite, simply run the following as root: @@ -67,49 +91,12 @@ optional arguments: ``` -## FEATURES - -Pyrite acts as a frontend for GnuPG, doing symmetric or asymmetric encrypting/decrypting, as well as signing and verifying. -Additionally, it can use OpenSSL for simple symmetric encryption/decryption. - -Pyrite can operate on text input or can take input and output filenames (text or binary) to pass directly to the backend program (i.e., gpg or openssl). - -As you can see from the screenshots, Pyrite can utilize virtually all the encrypting features of GnuPG -- you can mix and match passphrase & public-key encryption & signing with one file, just like gpg, which will require interacting with your gpg-agent. -Or you can keep it simple and just use a passphrase as a shared key, in which case gpg-agent is bypassed and you only have to type the passphrase once. - -Also shown in the screenshots is a Sign/Verify mode, where you can choose between the three types of signing: normal (Pyrite calls it "embedded"), where a signed copy of the message is created; clearsign, where the message is wrapped with a plaintext ASCII sig; or detached-sign, where a separate sig file is created. - -If you're operating directly on files (in sign or encrypt mode) instead of ASCII text in the Pyrite window, you can choose what kind of output you want -- ASCII-armored (base64-encoded) text or normal binary output. - -Not shown in the screenshots is drag & drop. You can drag text files onto the Message area and they are loaded up and you can drag text or binary files onto the *Input File For Direct Operation* button to set that. - -If you end up working on very large input, you'll get a chance to *really* see the progress bar + pause/cancel buttons. -At the moment the progress bar doesn't report actual progress (that's coming), but the buttons do what they advertise, pausing or canceling the backend processing. - -To top it all off, everything is configurable. -There's a preferences dialog that lets you play with all the settings, from tweaking gpg verbosity to setting the default operating mode to choosing your favorite cipher to configuring font size/color and window opacity. - -If you find yourself wondering about a particular feature, just hover your mouse over its widget -- there are detailed tooltips for everything. - - ## BUGS and TODO - No undo. It wasn't a top priority at the beginning, but I think it's pretty essential for an application that basically contains a text editor to have an undo/redo stack. I'll do it eventually. - Icons for encrypt, decrypt, sign, verify buttons, application - Update notifications - -## BACKGROUND - -The original goal of this project was to make symmetric {en,de}cryption more accessible and easy to use. -While GPG rocks if you're comfortable on the commandline (for both symmetric & public-key), and there are GUI encryption options for public-key encryption (seahorse-plugins for nautilus being the best, in my opinion), there's not much out there for people who need to do the simplest kind of encryption -- with a shared passphrase. - -After creating a few simple apps with BASH scripting, I decided it was time to learn Python. -After the first few days I was in love. - -Long story short, after a couple weeks of learning, I released my first version of this project in January 2012, quickly added public-key encryption, signing, & verifying, and have been improving it ever since. -This being my first learning experience with GTK+, I have lots more to learn, but I'm damn proud of Pyrite. - PLEASE contact me (or [post a new issue on the tracker](/ryran/pyrite/issues)) with any suggestions, feedback, bug reports, or questions! @@ -137,16 +124,13 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License @[gnu.org/licenses/gpl.html](https://gnu.org/licenses/gpl.html>) for more details. - - -------- - Hmmmm. You're still here? Oh. You must be wondering why the name [*Pyrite*](https://en.wikipedia.org/wiki/Pyrite), eh? -Well, I'll let my friend River--who came up with the name--explain it to you: +Well, I'll let my friend River who came up with the name explain it to you: > It should be 'Pyrite', because people think they are getting your data, but really it's just gibberish to them. > Fool's gold. From 6c4f1421cdae45acfff01db66d39eb1e2304a2d6 Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 16:52:39 +0300 Subject: [PATCH 32/34] v2.0.0 --- modules/cfg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/cfg.py b/modules/cfg.py index f58d195..c481d90 100644 --- a/modules/cfg.py +++ b/modules/cfg.py @@ -23,7 +23,7 @@ from os import getenv # Important variables -VERSION = 'v1.0.2' +VERSION = 'v2.0.0' ASSETDIR = './' USERPREF_FILE = getenv('HOME') + '/.pyrite' USERPREF_FORMAT_INFO = {'version': 'Must6fa'} From 950b52e822f05e59e95d59b25b2f24295b01524a Mon Sep 17 00:00:00 2001 From: Sergey Ponomarev Date: Sat, 28 Oct 2023 17:45:45 +0300 Subject: [PATCH 33/34] core.py simplify --- modules/core.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/core.py b/modules/core.py index c8cc7f1..a0bc62e 100644 --- a/modules/core.py +++ b/modules/core.py @@ -852,11 +852,6 @@ def action_cipher_changed(self, w): # Called by Encrypt/Sign toolbar btn def action_encrypt(self, w): """Encrypt or sign input.""" - # DEBUG - # self.g_chooserbtn.select_filename('/etc/passwd') - # self.g_expander.set_expanded(True) - # Gtk.main_iteration() - # return if self.g_signverify.get_active(): # If in sign-only mode, figure out which sig-type if self.g_sigmode.get_active() == 0: @@ -1296,7 +1291,8 @@ def launchxface(self, action): self.infobar('x_verify_success') else: # Set TextBuffer to gpg stdout - self.buff.set_text(self.x.io['stdout'].decode('utf-8')) + b = self.x.io['stdout'].decode('utf-8') + self.buff.set_text(b) self.x.io['stdout'] = 0 if self.engine in 'OpenSSL' and action in 'enc': self.infobar('x_opensslenc_success_textmode', customtext=cipher) @@ -1321,16 +1317,14 @@ def update_task_status(self, fd, condition, output='task'): # If there's data to be read, let's read it if condition == GLib.IOCondition.IN: + b = read(fd, 1024).decode('utf-8') if output in 'task': # Output to Task Status - b = read(fd, 1024).decode('utf-8') self.buff2.insert(self.buff2.get_end_iter(), b) else: # Output to stderr (will show if run from terminal) - b = read(fd, 1024).decode('utf-8') stderr.write(b) return True - # If other end of pipe hangs up, close our fd and destroy the watcher elif condition == GLib.IOCondition.HUP: if output in 'term': From c16e0633a6514a59afc94d387c250f73b6db75fa Mon Sep 17 00:00:00 2001 From: Karel Bilek Date: Sun, 29 Oct 2023 00:00:01 +0300 Subject: [PATCH 34/34] #23 Recipients autocomplete Instead of just a key id we can list all known public keys and extract names and email from them. Signed-off-by: Sergey Ponomarev --- modules/core.py | 35 ++++++++++++++++++++++++++++++++++- modules/crypt_interface.py | 4 ++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/modules/core.py b/modules/core.py index a0bc62e..eb33d74 100644 --- a/modules/core.py +++ b/modules/core.py @@ -911,7 +911,7 @@ def action_toggle_asymmetric(self, w): if not self.g_advanced.get_active(): # If not in advanced mode, disable Symmetric self.g_symmetric.set_active(False) - + self.load_recipients_autocmplete() else: # If leaving toggled state, hide recip entry, enctoself for widget in asymm_widgets: @@ -1362,6 +1362,39 @@ def show_working_progress(self, show=True, action=None): self.g_activityspin.set_visible(False) self.g_statusbar.pop(self.status) + + def loadmails_string_list(self): + """Return emails from all known keys.""" + mails = list() + if self.engine == 'OpenSSL': + return mails + cmd = split("gpg --list-public-keys --with-colons") + keys_string = check_output(cmd).decode('utf-8') + keys_all = keys_string.split('\n') + for line in keys_all: + line_fields = line.split(':') + if line_fields[0] == 'uid': + name_email = line_fields[9] + mails.append(name_email) + return mails + + # Loading names and emails for the recipient menu completion + def load_recipients_autocmplete(self): + mails = Gtk.ListStore(str) + for mail in self.loadmails_string_list(): + mails.append([mail]) + completion = Gtk.EntryCompletion() + completion.set_model(mails) + completion.set_text_column(0) + completion.set_match_func(self.recipient_contains, None) + self.g_recip.set_completion(completion) + + def recipient_contains(self, completion, key_string, iter, data): + model = completion.get_model() + # get the completion strings + modelstr = model[iter][0] + return key_string in modelstr + # RUN MAIN APPLICATION WINDOW def main(self): """Show main window, and start GTK+ main loop.""" diff --git a/modules/crypt_interface.py b/modules/crypt_interface.py index 571742b..1e25779 100644 --- a/modules/crypt_interface.py +++ b/modules/crypt_interface.py @@ -163,7 +163,7 @@ def gpg( if enctoself: cmd.append('--recipient') if localuser: - cmd.append(localuser) + cmd.append("'" + localuser + "'") else: cmd.append(self.get_gpg_default_key()) if recip: @@ -172,7 +172,7 @@ def gpg( recip = recip.strip(';') for r in recip.split(';'): cmd.append('--recipient') - cmd.append(r) + cmd.append("'" + r + "'") # Decrypt opts elif action in 'dec':