def __init__(self, uac=True, shred_paths=None): if uac and 'nt' == os.name and Windows.elevate_privileges(): # privileges escalated in other process sys.exit(0) import RecognizeCleanerML RecognizeCleanerML.RecognizeCleanerML() register_cleaners() self.create_window() gobject.threads_init() if shred_paths: self.shred_paths(shred_paths) return if options.get("first_start") and 'posix' == os.name: pref = PreferencesDialog(self.window, self.cb_refresh_operations) pref.run() options.set('first_start', False) if online_update_notification_enabled and options.get("check_online_updates"): self.check_online_updates() if 'nt' == os.name: # BitDefender false positive. BitDefender didn't mark BleachBit as infected or show # anything in its log, but sqlite would fail to import unless BitDefender was in "game mode." # http://bleachbit.sourceforge.net/forum/074-fails-errors try: import sqlite3 except ImportError, e: print e print dir(e) self.append_text( _("Error loading the SQLite module: the antivirus software may be blocking it."), 'error')
def __toggle_callback(self, cell, path): """Callback function to toggle option""" options.toggle(path) if online_update_notification_enabled: self.cb_beta.set_sensitive(options.get('check_online_updates')) if 'nt' == os.name: self.cb_winapp2.set_sensitive( options.get('check_online_updates')) if 'auto_hide' == path: self.cb_refresh_operations() if 'auto_start' == path: if 'nt' == os.name: swc = Windows.start_with_computer if 'posix' == os.name: swc = Unix.start_with_computer try: swc(options.get(path)) except: traceback.print_exc() dlg = gtk.MessageDialog(self.parent, type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_OK, message_format=str(sys.exc_info()[1])) dlg.run() dlg.destroy()
def check_online_updates(self): """Check for software updates in background""" import Update try: updates = Update.check_updates(options.get('check_beta'), options.get('update_winapp2'), self.append_text, lambda: gobject.idle_add(self.cb_refresh_operations)) if updates: gobject.idle_add(lambda: Update.update_dialog(self.window, updates)) except: traceback.print_exc() self.append_text(_("Error when checking for updates: ") + str(sys.exc_info()[1]), 'error')
def check_online_updates(self): """Check for software updates in background""" import Update try: updates = Update.check_updates(options.get('check_beta'), options.get('update_winapp2'), self.append_text, lambda: gobject.idle_add(self.cb_refresh_operations)) if updates: gobject.idle_add( lambda: Update.update_dialog(self.window, updates)) except: traceback.print_exc() self.append_text( _("Error when checking for updates: ") + str(sys.exc_info()[1]), 'error')
def clean_ini(path, section, parameter): """Delete sections and parameters (aka option) in the file""" # read file to parser config = ConfigParser.RawConfigParser() fp = codecs.open(path, 'r', encoding='utf_8_sig') config.readfp(fp) # change file changed = False if config.has_section(section): if None == parameter: changed = True config.remove_section(section) elif config.has_option(section, parameter): changed = True config.remove_option(section, parameter) # write file if changed: from Options import options fp.close() if options.get('shred'): delete(path, True) fp = codecs.open(path, 'wb', encoding='utf_8') config.write(fp)
def bytes_to_human(bytes_i): """Display a file size in human terms (megabytes, etc.) using preferred standard (SI or IEC)""" if bytes_i < 0: return '-' + bytes_to_human(-bytes_i) from Options import options if options.get('units_iec'): prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi'] base = 1024.0 else: prefixes = ['', 'k', 'M', 'G', 'T', 'P'] base = 1000.0 assert (isinstance(bytes_i, (int, long))) if 0 == bytes_i: return "0" if bytes_i >= base**3: decimals = 2 elif bytes_i >= base: decimals = 1 else: decimals = 0 for exponent in range(0, len(prefixes)): if bytes_i < base: abbrev = round(bytes_i, decimals) suf = prefixes[exponent] return locale.str(abbrev) + suf + 'B' else: bytes_i /= base return 'A lot.'
def execute_sqlite3(path, cmds): """Execute 'cmds' on SQLite database 'path'""" import sqlite3 conn = sqlite3.connect(path) cursor = conn.cursor() # overwrites deleted content with zeros # https://www.sqlite.org/pragma.html#pragma_secure_delete from Options import options if options.get('shred'): cursor.execute('PRAGMA secure_delete=ON') for cmd in cmds.split(';'): try: cursor.execute(cmd) except sqlite3.DatabaseError, exc: raise sqlite3.DatabaseError('%s: %s' % (Common.decode_str(exc), path)) except sqlite3.OperationalError, exc: logger = logging.getLogger(__name__) if exc.message.find('no such function: ') >= 0: # fixme: determine why randomblob and zeroblob are not # available logger.warning(exc.message) else: raise sqlite3.OperationalError('%s: %s' % (Common.decode_str(exc), path))
def worker_done(self, worker, really_delete): """Callback for when Worker is done""" self.progressbar.set_text("") self.progressbar.set_fraction(1) if not self.stop_now: self.progressbar.set_text(_("Done.")) else: self.progressbar.set_text(_("Stopped.")) self.textview.scroll_mark_onscreen(self.textbuffer.get_insert()) self.set_sensitive(True) # Close the program after cleaning is completed. # if the option is selected under preference. if really_delete: if options.get("exit_done"): sys.exit() # notification for long-running process elapsed = (time.time() - self.start_time) print 'debug: elapsed time: %d seconds' % elapsed if elapsed < 10 or self.window.is_active(): return try: import pynotify except: print "debug: pynotify not available" else: if pynotify.init(APP_NAME): notify = pynotify.Notification('BleachBit', _("Done."), icon='bleachbit') notify.show() notify.set_timeout(10000)
def bytes_to_human(bytes_i): """Display a file size in human terms (megabytes, etc.) using preferred standard (SI or IEC)""" if bytes_i < 0: return '-' + bytes_to_human(-bytes_i) from Options import options if options.get('units_iec'): prefixes = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi'] base = 1024.0 else: prefixes = ['', 'k', 'M', 'G', 'T', 'P'] base = 1000.0 assert(isinstance(bytes_i, (int, long))) if 0 == bytes_i: return "0" if bytes_i >= base ** 3: decimals = 2 elif bytes_i >= base: decimals = 1 else: decimals = 0 for exponent in range(0, len(prefixes)): if bytes_i < base: abbrev = round(bytes_i, decimals) suf = prefixes[exponent] return locale.str(abbrev) + suf + 'B' else: bytes_i /= base return 'A lot.'
def __init__(self, parent_window=None): self.parent_window = parent_window try: self.salt = options.get('hashsalt') except ConfigParser.NoOptionError: self.salt = hashdigest(str(random.random())) options.set('hashsalt', self.salt) self.__scan()
def __general_page(self): """Return a widget containing the general page""" if 'nt' == os.name: swcc = Windows.start_with_computer_check if 'posix' == os.name: swcc = Unix.start_with_computer_check options.set('auto_start', swcc()) vbox = gtk.VBox() if online_update_notification_enabled: cb_updates = gtk.CheckButton( _("Check periodically for software updates via the Internet")) cb_updates.set_active(options.get('check_online_updates')) cb_updates.connect( 'toggled', self.__toggle_callback, 'check_online_updates') cb_updates.set_tooltip_text( _("If an update is found, you will be given the option to view information about it. Then, you may manually download and install the update.")) vbox.pack_start(cb_updates, False) updates_box = gtk.VBox() updates_box.set_border_width(10) self.cb_beta = gtk.CheckButton(_("Check for new beta releases")) self.cb_beta.set_active(options.get('check_beta')) self.cb_beta.set_sensitive(options.get('check_online_updates')) self.cb_beta.connect( 'toggled', self.__toggle_callback, 'check_beta') updates_box.pack_start(self.cb_beta, False) if 'nt' == os.name: self.cb_winapp2 = gtk.CheckButton( _("Download and update cleaners from community (winapp2.ini)")) self.cb_winapp2.set_active(options.get('update_winapp2')) self.cb_winapp2.set_sensitive( options.get('check_online_updates')) self.cb_winapp2.connect( 'toggled', self.__toggle_callback, 'update_winapp2') updates_box.pack_start(self.cb_winapp2, False) vbox.pack_start(updates_box, False) # TRANSLATORS: Overwriting is the same as shredding. It is a way # to prevent recovery of the data. You could also translate # 'Shred files to prevent recovery.' cb_shred = gtk.CheckButton(_("Overwrite files to hide contents")) cb_shred.set_active(options.get('shred')) cb_shred.connect('toggled', self.__toggle_callback, 'shred') cb_shred.set_tooltip_text( _("Overwriting is ineffective on some file systems and with certain BleachBit operations. Overwriting is significantly slower.")) vbox.pack_start(cb_shred, False) cb_start = gtk.CheckButton(_("Start BleachBit with computer")) cb_start.set_active(options.get('auto_start')) cb_start.connect('toggled', self.__toggle_callback, 'auto_start') vbox.pack_start(cb_start, False) return vbox
def __shred_sqlite_char_columns(table, cols = None, where = ""): """Create an SQL command to shred character columns""" cmd = "" if cols and options.get('shred'): cmd += "update %s set %s %s;" % \ (table, ",".join(["%s = randomblob(length(%s))" % (col, col) for col in cols]), where) cmd += "update %s set %s %s;" % \ (table, ",".join(["%s = zeroblob(length(%s))" % (col, col) for col in cols]), where) cmd += "delete from %s %s;" % (table, where) return cmd
def run_operations(self, __widget): """Event when the 'delete' toolbar button is clicked.""" # fixme: should present this dialog after finding operations # Disable delete confirmation message. # if the option is selected under preference. self.stop_now = False print "Cleane clicked" , self.stop_now if not options.get("no_popup"): if not GuiBasic.delete_confirmation_dialog(self.window, True): return self.preview_or_run_operations(True)
def __shred_sqlite_char_columns(table, cols=None, where=""): """Create an SQL command to shred character columns""" cmd = "" if cols and options.get('shred'): cmd += "update or ignore %s set %s %s;" % \ (table, ",".join(["%s = randomblob(length(%s))" % (col, col) for col in cols]), where) cmd += "update or ignore %s set %s %s;" % \ (table, ",".join(["%s = zeroblob(length(%s))" % (col, col) for col in cols]), where) cmd += "delete from %s %s;" % (table, where) return cmd
def refresh_rows(self): """Clear rows (cleaners) and add them fresh""" if None != self.row_changed_handler_id: self.tree_store.disconnect(self.row_changed_handler_id) self.tree_store.clear() for key in sorted(backends): c_name = backends[key].get_name() c_id = backends[key].get_id() c_value = options.get_tree(c_id, None) if not c_value and options.get('auto_hide') \ and backends[key].auto_hide(): print "info: automatically hiding cleaner '%s'" % (c_id) continue parent = self.tree_store.append(None, (c_name, c_value, c_id)) for (o_id, o_name) in backends[key].get_options(): o_value = options.get_tree(c_id, o_id) self.tree_store.append(parent, (o_name, o_value, o_id)) self.row_changed_handler_id = self.tree_store.connect("row-changed", self.on_row_changed)
def delete(path, shred=False, ignore_missing=False, allow_shred=True): """Delete path that is either file, directory, link or FIFO. If shred is enabled as a function parameter or the BleachBit global parameter, the path will be shredded unless allow_shred = False. """ from Options import options logger = logging.getLogger(__name__) is_special = False path = extended_path(path) if not os.path.lexists(path): if ignore_missing: return raise OSError(2, 'No such file or directory', path) if 'posix' == os.name: # With certain (relatively rare) files on Windows os.lstat() # may return Access Denied mode = os.lstat(path)[stat.ST_MODE] is_special = stat.S_ISFIFO(mode) or stat.S_ISLNK(mode) if is_special: os.remove(path) elif os.path.isdir(path): delpath = path if allow_shred and (shred or options.get('shred')): delpath = wipe_name(path) try: os.rmdir(delpath) except OSError, e: # [Errno 39] Directory not empty # https://bugs.launchpad.net/bleachbit/+bug/1012930 if errno.ENOTEMPTY == e.errno: logger.info("directory is not empty: %s", path) else: raise except WindowsError, e: # WindowsError: [Error 145] The directory is not empty: # 'C:\\Documents and Settings\\username\\Local Settings\\Temp\\NAILogs' # Error 145 may happen if the files are scheduled for deletion # during reboot. if 145 == e.winerror: logger.info("directory is not empty: %s", path) else: raise
def clean_json(path, target): """Delete key in the JSON file""" try: import json except: import simplejson as json changed = False targets = target.split("/") # read file to parser js = json.load(open(path, "r")) # change file pos = js while True: new_target = targets.pop(0) if not isinstance(pos, dict): break if new_target in pos and len(targets) > 0: # descend pos = pos[new_target] elif new_target in pos: # delete terminal target changed = True del (pos[new_target]) else: # target not found break if 0 == len(targets): # target not found break if changed: from Options import options if options.get("shred"): delete(path, True) # write file json.dump(js, open(path, "w"))
def delete(path, shred=False, ignore_missing=False): """Delete path that is either file, directory, link or FIFO""" from Options import options is_special = False if not os.path.lexists(path): if ignore_missing: return raise OSError(2, "No such file or directory", path) if "posix" == os.name: # With certain (relatively rare) files on Windows os.lstat() # may return Access Denied mode = os.lstat(path)[stat.ST_MODE] is_special = stat.S_ISFIFO(mode) or stat.S_ISLNK(mode) if is_special: os.remove(path) elif os.path.isdir(path): delpath = path if shred or options.get("shred"): delpath = wipe_name(path) try: os.rmdir(delpath) except OSError, e: # [Errno 39] Directory not empty # https://bugs.launchpad.net/bleachbit/+bug/1012930 if errno.ENOTEMPTY == e.errno: print "info: directory '%s' is not empty" % (path) else: raise except WindowsError, e: # WindowsError: [Error 145] The directory is not empty: # 'C:\\Documents and Settings\\username\\Local Settings\\Temp\\NAILogs' # Error 145 may happen if the files are scheduled for deletion # during reboot. if 145 == e.winerror: print "info: directory '%s' is not empty" % (path) else: raise
def clean_json(path, target): """Delete key in the JSON file""" try: import json except: import simplejson as json changed = False targets = target.split('/') # read file to parser js = json.load(open(path, 'r')) # change file pos = js while True: new_target = targets.pop(0) if not isinstance(pos, dict): break if new_target in pos and len(targets) > 0: # descend pos = pos[new_target] elif new_target in pos: # delete terminal target changed = True del (pos[new_target]) else: # target not found break if 0 == len(targets): # target not found break if changed: from Options import options if options.get('shred'): delete(path, True) # write file json.dump(js, open(path, 'w'))
def __general_page(self): """Return a widget containing the general page""" if 'nt' == os.name: swcc = Windows.start_with_computer_check if 'posix' == os.name: swcc = Unix.start_with_computer_check options.set('auto_start', swcc()) vbox = gtk.VBox() if online_update_notification_enabled: cb_updates = gtk.CheckButton( _("Check periodically for software updates via the Internet")) cb_updates.set_active(options.get('check_online_updates')) cb_updates.connect('toggled', self.__toggle_callback, 'check_online_updates') cb_updates.set_tooltip_text( _("If an update is found, you will be given the option to view information about it. Then, you may manually download and install the update." )) vbox.pack_start(cb_updates, False) updates_box = gtk.VBox() updates_box.set_border_width(10) self.cb_beta = gtk.CheckButton(_("Check for new beta releases")) self.cb_beta.set_active(options.get('check_beta')) self.cb_beta.set_sensitive(options.get('check_online_updates')) self.cb_beta.connect('toggled', self.__toggle_callback, 'check_beta') updates_box.pack_start(self.cb_beta, False) if 'nt' == os.name: self.cb_winapp2 = gtk.CheckButton( _("Download and update cleaners from community (winapp2.ini)" )) self.cb_winapp2.set_active(options.get('update_winapp2')) self.cb_winapp2.set_sensitive( options.get('check_online_updates')) self.cb_winapp2.connect('toggled', self.__toggle_callback, 'update_winapp2') updates_box.pack_start(self.cb_winapp2, False) vbox.pack_start(updates_box, False) # TRANSLATORS: This means to hide cleaners which would do # nothing. For example, if Firefox were never used on # this system, this option would hide Firefox to simplify # the list of cleaners. cb_auto_hide = gtk.CheckButton(_("Hide irrelevant cleaners")) cb_auto_hide.set_active(options.get('auto_hide')) cb_auto_hide.connect('toggled', self.__toggle_callback, 'auto_hide') vbox.pack_start(cb_auto_hide, False) # TRANSLATORS: Overwriting is the same as shredding. It is a way # to prevent recovery of the data. You could also translate # 'Shred files to prevent recovery.' cb_shred = gtk.CheckButton(_("Overwrite files to hide contents")) cb_shred.set_active(options.get('shred')) cb_shred.connect('toggled', self.__toggle_callback, 'shred') cb_shred.set_tooltip_text( _("Overwriting is ineffective on some file systems and with certain BleachBit operations. Overwriting is significantly slower." )) vbox.pack_start(cb_shred, False) cb_start = gtk.CheckButton(_("Start BleachBit with computer")) cb_start.set_active(options.get('auto_start')) cb_start.connect('toggled', self.__toggle_callback, 'auto_start') vbox.pack_start(cb_start, False) # Close the application after cleaning is complete. cb_exit = gtk.CheckButton(_("Exit after cleaning")) cb_exit.set_active(options.get('exit_done')) cb_exit.connect('toggled', self.__toggle_callback, 'exit_done') vbox.pack_start(cb_exit, False) # Disable delete confirmation message. cb_popup = gtk.CheckButton(_("Confirm before delete")) cb_popup.set_active(options.get('delete_confirmation')) cb_popup.connect('toggled', self.__toggle_callback, 'delete_confirmation') vbox.pack_start(cb_popup, False) # Use base 1000 over 1024? cb_units_iec = gtk.CheckButton( _("Use IEC sizes (1 KiB = 1024 bytes) instead of SI (1 kB = 1000 bytes)" )) cb_units_iec.set_active(options.get("units_iec")) cb_units_iec.connect('toggled', self.__toggle_callback, 'units_iec') vbox.pack_start(cb_units_iec, False) return vbox
if errno.ENOTEMPTY == e.errno: print "info: directory '%s' is not empty" % (path) else: raise except WindowsError, e: # WindowsError: [Error 145] The directory is not empty: # 'C:\\Documents and Settings\\username\\Local Settings\\Temp\\NAILogs' # Error 145 may happen if the files are scheduled for deletion # during reboot. if 145 == e.winerror: print "info: directory '%s' is not empty" % (path) else: raise elif os.path.isfile(path): # wipe contents if shred or options.get("shred"): try: wipe_contents(path) except IOError, e: # permission denied (13) happens shredding MSIE 8 on Windows 7 print "debug: IOError #%s shredding '%s'" % (e.errno, path) # wipe name if shred or options.get("shred"): os.remove(wipe_name(path)) # unlink else: os.remove(path) else: print "info: special file type cannot be deleted: '%s'" % (path)
if errno.ENOTEMPTY == e.errno: print "info: directory '%s' is not empty" % (path) else: raise except WindowsError, e: # WindowsError: [Error 145] The directory is not empty: # 'C:\\Documents and Settings\\username\\Local Settings\\Temp\\NAILogs' # Error 145 may happen if the files are scheduled for deletion # during reboot. if 145 == e.winerror: print "info: directory '%s' is not empty" % (path) else: raise elif os.path.isfile(path): # wipe contents if allow_shred and (shred or options.get("shred")): try: wipe_contents(path) except IOError, e: # permission denied (13) happens shredding MSIE 8 on Windows 7 print "debug: IOError #%s shredding '%s'" % (e.errno, path) # wipe name os.remove(wipe_name(path)) else: # unlink os.remove(path) else: print "info: special file type cannot be deleted: '%s'" % (path) def ego_owner(filename):
if errno.ENOTEMPTY == e.errno: logger.info("directory is not empty: %s", path) else: raise except WindowsError, e: # WindowsError: [Error 145] The directory is not empty: # 'C:\\Documents and Settings\\username\\Local Settings\\Temp\\NAILogs' # Error 145 may happen if the files are scheduled for deletion # during reboot. if 145 == e.winerror: logger.info("directory is not empty: %s", path) else: raise elif os.path.isfile(path): # wipe contents if allow_shred and (shred or options.get('shred')): try: wipe_contents(path) except IOError, e: # permission denied (13) happens shredding MSIE 8 on Windows 7 logger.debug("IOError #%s shredding '%s'", e.errno, path) # wipe name os.remove(wipe_name(path)) else: # unlink os.remove(path) else: logger.info("special file type cannot be deleted: %s", path) def ego_owner(filename):
def __general_page(self): """Return a widget containing the general page""" if 'nt' == os.name: swcc = Windows.start_with_computer_check if 'posix' == os.name: swcc = Unix.start_with_computer_check options.set('auto_start', swcc()) vbox = gtk.VBox() if online_update_notification_enabled: cb_updates = gtk.CheckButton( _("Check periodically for software updates via the Internet")) cb_updates.set_active(options.get('check_online_updates')) cb_updates.connect( 'toggled', self.__toggle_callback, 'check_online_updates') cb_updates.set_tooltip_text( _("If an update is found, you will be given the option to view information about it. Then, you may manually download and install the update.")) vbox.pack_start(cb_updates, False) updates_box = gtk.VBox() updates_box.set_border_width(10) self.cb_beta = gtk.CheckButton(_("Check for new beta releases")) self.cb_beta.set_active(options.get('check_beta')) self.cb_beta.set_sensitive(options.get('check_online_updates')) self.cb_beta.connect( 'toggled', self.__toggle_callback, 'check_beta') updates_box.pack_start(self.cb_beta, False) if 'nt' == os.name: self.cb_winapp2 = gtk.CheckButton( _("Download and update cleaners from community (winapp2.ini)")) self.cb_winapp2.set_active(options.get('update_winapp2')) self.cb_winapp2.set_sensitive( options.get('check_online_updates')) self.cb_winapp2.connect( 'toggled', self.__toggle_callback, 'update_winapp2') updates_box.pack_start(self.cb_winapp2, False) vbox.pack_start(updates_box, False) # TRANSLATORS: This means to hide cleaners which would do # nothing. For example, if Firefox were never used on # this system, this option would hide Firefox to simplify # the list of cleaners. cb_auto_hide = gtk.CheckButton(_("Hide irrelevant cleaners")) cb_auto_hide.set_active(options.get('auto_hide')) cb_auto_hide.connect('toggled', self.__toggle_callback, 'auto_hide') vbox.pack_start(cb_auto_hide, False) # TRANSLATORS: Overwriting is the same as shredding. It is a way # to prevent recovery of the data. You could also translate # 'Shred files to prevent recovery.' cb_shred = gtk.CheckButton(_("Overwrite files to hide contents")) cb_shred.set_active(options.get('shred')) cb_shred.connect('toggled', self.__toggle_callback, 'shred') cb_shred.set_tooltip_text( _("Overwriting is ineffective on some file systems and with certain BleachBit operations. Overwriting is significantly slower.")) vbox.pack_start(cb_shred, False) cb_start = gtk.CheckButton(_("Start BleachBit with computer")) cb_start.set_active(options.get('auto_start')) cb_start.connect('toggled', self.__toggle_callback, 'auto_start') vbox.pack_start(cb_start, False) # Close the application after cleaning is complete. cb_exit = gtk.CheckButton(_("Exit after cleaning")) cb_exit.set_active(options.get('exit_done')) cb_exit.connect('toggled', self.__toggle_callback, 'exit_done') vbox.pack_start(cb_exit, False) # Disable delete confirmation message. cb_popup = gtk.CheckButton(_("Confirm before delete")) cb_popup.set_active(options.get('delete_confirmation')) cb_popup.connect( 'toggled', self.__toggle_callback, 'delete_confirmation') vbox.pack_start(cb_popup, False) return vbox