def guess_overwrite_paths(): """Guess which partitions to overwrite (to hide deleted files)""" # In case overwriting leaves large files, placing them in # ~/.config makes it easy to find them and clean them. ret = [] if 'posix' == os.name: home = expanduser('~/.cache') if not os.path.exists(home): home = expanduser("~") ret.append(home) if not same_partition(home, '/tmp/'): ret.append('/tmp') elif 'nt' == os.name: localtmp = bleachbit.expandvars('$TMP') if not os.path.exists(localtmp): logger.warning('%TMP% does not exist: %s', localtmp) localtmp = None from bleachbit.Windows import get_fixed_drives for drive in get_fixed_drives(): if localtmp and same_partition(localtmp, drive): ret.append(localtmp) else: ret.append(drive) else: NotImplementedError('Unsupported OS in guess_overwrite_paths') return ret
def whitelisted(self, pathname): """Return boolean whether file is whitelisted""" regexes = [ '^/tmp/.X0-lock$', '^/tmp/.truecrypt_aux_mnt.*/(control|volume)$', '^/tmp/.vbox-[^/]+-ipc/lock$', '^/tmp/.wine-[0-9]+/server-.*/lock$', '^/tmp/gconfd-[^/]+/lock/ior$', '^/tmp/fsa/', # fsarchiver '^/tmp/kde-', '^/tmp/kdesudo-', '^/tmp/ksocket-', '^/tmp/orbit-[^/]+/bonobo-activation-register[a-z0-9-]*.lock$', '^/tmp/orbit-[^/]+/bonobo-activation-server-[a-z0-9-]*ior$', '^/tmp/pulse-[^/]+/pid$', '^/var/tmp/kdecache-', '^' + expanduser('~/.cache/wallpaper/'), # Clean Firefox cache from Firefox cleaner (LP#1295826) '^' + expanduser('~/.cache/mozilla'), # Clean Google Chrome cache from Google Chrome cleaner (LP#656104) '^' + expanduser('~/.cache/google-chrome'), '^' + expanduser('~/.cache/gnome-control-center/'), # iBus Pinyin # https://bugs.launchpad.net/bleachbit/+bug/1538919 '^' + expanduser('~/.cache/ibus/')] for regex in regexes: if re.match(regex, pathname) is not None: return True return False
def guess_overwrite_paths(): """Guess which partitions to overwrite (to hide deleted files)""" # In case overwriting leaves large files, placing them in # ~/.config makes it easy to find them and clean them. ret = [] if 'posix' == os.name: home = expanduser('~/.cache') if not os.path.exists(home): home = expanduser("~") ret.append(home) if not same_partition(home, '/tmp/'): ret.append('/tmp') elif 'nt' == os.name: localtmp = bleachbit.expandvars('$TMP') if not os.path.exists(localtmp): logger.warning(_("%TMP% does not exist: %s"), localtmp) localtmp = None from bleachbit.Windows import get_fixed_drives for drive in get_fixed_drives(): if localtmp and same_partition(localtmp, drive): ret.append(localtmp) else: ret.append(drive) else: NotImplementedError('Unsupported OS in guess_overwrite_paths') return ret
def init_whitelist(self): """Initialize the whitelist only once for performance""" regexes = [ '^/tmp/.X0-lock$', '^/tmp/.truecrypt_aux_mnt.*/(control|volume)$', '^/tmp/.vbox-[^/]+-ipc/lock$', '^/tmp/.wine-[0-9]+/server-.*/lock$', '^/tmp/gconfd-[^/]+/lock/ior$', '^/tmp/fsa/', # fsarchiver '^/tmp/kde-', '^/tmp/kdesudo-', '^/tmp/ksocket-', '^/tmp/orbit-[^/]+/bonobo-activation-register[a-z0-9-]*.lock$', '^/tmp/orbit-[^/]+/bonobo-activation-server-[a-z0-9-]*ior$', '^/tmp/pulse-[^/]+/pid$', '^/var/tmp/kdecache-', '^' + expanduser('~/.cache/wallpaper/'), # Flatpak mount point '^' + expanduser('~/.cache/doc($|/)'), # Clean Firefox cache from Firefox cleaner (LP#1295826) '^' + expanduser('~/.cache/mozilla/'), # Clean Google Chrome cache from Google Chrome cleaner (LP#656104) '^' + expanduser('~/.cache/google-chrome/'), '^' + expanduser('~/.cache/gnome-control-center/'), # Clean Evolution cache from Evolution cleaner (GitHub #249) '^' + expanduser('~/.cache/evolution/'), # iBus Pinyin # https://bugs.launchpad.net/bleachbit/+bug/1538919 '^' + expanduser('~/.cache/ibus/'), # Linux Bluetooth daemon obexd directory is typically empty, so be careful # not to delete the empty directory. '^' + expanduser('~/.cache/obexd($|/)') ] for regex in regexes: self.regexes_compiled.append(re.compile(regex))
def worker_done(self, worker, really_delete): """Callback for when Worker is done""" self.progressbar.set_text("") self.progressbar.set_fraction(1) self.progressbar.set_text(_("Done.")) 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) logger.debug('elapsed time: %d seconds', elapsed) if elapsed < 10 or self.is_active(): return try: gi.require_version('Notify', '0.7') from gi.repository import Notify except: logger.debug('Notify not available') else: if Notify.init(APP_NAME): notify = Notify.Notification.new('BleachBit', _("Done."), 'bleachbit') if os.name == 'posix' and bleachbit.expanduser('~') == '/root': notify.set_hint("desktop-entry", "bleachbit-root") else: notify.set_hint("desktop-entry", "bleachbit") notify.show() notify.set_timeout(10000)
def browse_folder(parent, title, multiple, stock_button): """Ask the user to select a folder. Return the full path or None.""" if os.name == 'nt' and not os.getenv('BB_NATIVE'): ret = Windows.browse_folder(parent, title) return [ret] if multiple and not ret is None else ret # fall back to GTK+ chooser = Gtk.FileChooserDialog(transient_for=parent, title=title, action=Gtk.FileChooserAction.SELECT_FOLDER) chooser.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, stock_button, Gtk.ResponseType.OK) chooser.set_default_response(Gtk.ResponseType.OK) chooser.set_select_multiple(multiple) chooser.set_current_folder(expanduser('~')) resp = chooser.run() if multiple: ret = chooser.get_filenames() else: ret = chooser.get_filename() chooser.hide() chooser.destroy() if Gtk.ResponseType.OK != resp: # user cancelled return None return ret
def test_delete(self): """Unit test for class Delete""" paths = [u'~'] if 'nt' == os.name: # Python 2.6 and later supports %foo% paths.append(u'%USERPROFILE%') # Python 2.5 and later supports $foo paths.append(u'${USERPROFILE}') paths.append(u'$USERPROFILE') if 'posix' == os.name: paths.append(u'$HOME') for path in paths: for mode in ('delete', 'truncate', 'delete_forward'): expanded = expanduser(expandvars(path)) filename = self.mkstemp(dir=expanded, prefix='bleachbit-action-delete') command = mode if 'delete_forward' == mode: # forward slash needs to be normalized on Windows if 'nt' == os.name: command = 'delete' filename = filename.replace('\\', '/') else: # test not needed on this OS os.remove(filename) continue action_str = u'<action command="%s" search="file" path="%s" />' % \ (command, filename) self._test_action_str(action_str) self.assertNotExists(filename)
def is_running(self): """Return whether the program is currently running""" logger = logging.getLogger(__name__) for running in self.running: test = running[0] pathname = running[1] if 'exe' == test and 'posix' == os.name: if Unix.is_running(pathname): logger.debug("process '%s' is running", pathname) return True elif 'exe' == test and 'nt' == os.name: if Windows.is_process_running(pathname): logger.debug("process '%s' is running", pathname) return True elif 'pathname' == test: expanded = expanduser(expandvars(pathname)) for globbed in glob.iglob(expanded): if os.path.exists(globbed): logger.debug( "file '%s' exists indicating '%s' is running", globbed, self.name) return True else: raise RuntimeError( "Unknown running-detection test '%s'" % test) return False
def __init__(self, uac=True, shred_paths=None, exit=False): if uac and 'nt' == os.name and Windows.elevate_privileges(): # privileges escalated in other process sys.exit(0) Gtk.Application.__init__(self, application_id='org.gnome.Bleachbit', flags=Gio.ApplicationFlags.FLAGS_NONE) if not exit: from bleachbit import RecognizeCleanerML RecognizeCleanerML.RecognizeCleanerML() register_cleaners() GObject.threads_init() if shred_paths: self.shred_paths(shred_paths) return 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." # https://www.bleachbit.org/forum/074-fails-errors try: import sqlite3 except ImportError: logger.exception(_("Error loading the SQLite module: the antivirus software may be blocking it.")) if 'posix' == os.name and bleachbit.expanduser('~') == '/root': self.append_text( _('You are running BleachBit with administrative privileges for cleaning shared parts of the system, and references to the user profile folder will clean only the root account.')) if 'nt' == os.name and options.get('shred'): from win32com.shell.shell import IsUserAnAdmin if not IsUserAnAdmin(): self.append_text( _('Run BleachBit with administrator privileges to improve the accuracy of overwriting the contents of files.')) self.append_text('\n') if exit: # This is used for automated testing of whether the GUI can start. print('Success') GObject.idle_add(lambda: self.quit(), priority=GObject.PRIORITY_LOW)
def __init__(self, action_element): """Initialize file search""" self.regex = action_element.getAttribute('regex') assert (isinstance(self.regex, (str, unicode, types.NoneType))) self.nregex = action_element.getAttribute('nregex') assert (isinstance(self.nregex, (str, unicode, types.NoneType))) self.wholeregex = action_element.getAttribute('wholeregex') assert (isinstance(self.wholeregex, (str, unicode, types.NoneType))) self.nwholeregex = action_element.getAttribute('nwholeregex') assert (isinstance(self.nwholeregex, (str, unicode, types.NoneType))) self.search = action_element.getAttribute('search') self.object_type = action_element.getAttribute('type') self.path = expanduser(expandvars(action_element.getAttribute('path'))) if 'nt' == os.name and self.path: # convert forward slash to backslash for compatibility with getsize() # and for display. Do not convert an empty path, or it will become # the current directory (.). self.path = os.path.normpath(self.path) self.ds = {} if 'deep' == self.search: self.ds['regex'] = self.regex self.ds['nregex'] = self.nregex self.ds['cache'] = General.boolstr_to_bool( action_element.getAttribute('cache')) self.ds['command'] = action_element.getAttribute('command') self.ds['path'] = self.path if not any([ self.object_type, self.regex, self.nregex, self.wholeregex, self.nwholeregex ]): # If the filter is not needed, bypass it for speed. self.get_paths = self._get_paths
def is_running(self): """Return whether the program is currently running""" # 프로그램이 현재 실행 중인지 여부를 반환하는 함수 logger = logging.getLogger(__name__) # __name__에서 로거 추출 for running in self.running: # running 리스트의 값들 반복 test = running[0] # running[0] = detection_type ( add_running 함수 참고) pathname = running[1] if 'exe' == test and 'posix' == os.name: # os이름이 posix이고 exe 타입인지 확인 if Unix.is_running(pathname): # Unix에서 is_running함수썼을때 True이면 logger.debug("process '%s' is running", pathname) # 프로세스가 실행중이라는 debug 메세지를 로그하고 True반환 return True elif 'exe' == test and 'nt' == os.name: # os이름이 nt 이고 exe타입인지 확인 if Windows.is_process_running(pathname): # 만약 Windows에서 is_process_running 함수를 사용했을때 true이면 logger.debug("process '%s' is running", pathname) # 프로세스가 실행중이라는 debug 메세지를 로그하고 true반환 return True elif 'pathname' == test: # test expanded = expanduser(expandvars(pathname)) # pathname 안에 환경변수가 있으면 확장하고 현재 사용자 디렉토리의 절대경로로 대체 # ex) C:\\Documents and Settings\\Administrator\\pathname for globbed in glob.iglob(expanded): # iglob() : expanded의 모든 값을 실제로 동시에 저장하지 않고 # glob()값과 동일한 값을 산출하는 반복기를 반환함 if os.path.exists(globbed): # globbed로 저장한 경로에 특정파일이 존재하는지 확인 logger.debug( "file '%s' exists indicating '%s' is running", globbed, self.name) # 존재하면 파일이 존재하며 실행중임을 나타내는 메세지출력 return True else: raise RuntimeError( "Unknown running-detection test '%s'" % test) # test가 exe , pathname도 아니면 알수없는 실행타입메세지와 함께 런타임에러발생 return False
def test_children_in_directory(self): """Unit test for function children_in_directory()""" # test an existing directory that usually exists dirname = expanduser("~/.config") for filename in children_in_directory(dirname, True): self.assertTrue(os.path.isabs(filename)) for filename in children_in_directory(dirname, False): self.assertTrue(os.path.isabs(filename)) self.assertFalse(os.path.isdir(filename)) # test a constructed file in a constructed directory dirname = self.mkdtemp(prefix='bleachbit-test-children') filename = self.mkstemp(prefix="somefile", dir=dirname) for loopfilename in children_in_directory(dirname, True): self.assertEqual(loopfilename, filename) for loopfilename in children_in_directory(dirname, False): self.assertEqual(loopfilename, filename) os.remove(filename) # test subdirectory subdirname = os.path.join(dirname, "subdir") os.mkdir(subdirname) for filename in children_in_directory(dirname, True): self.assertEqual(filename, subdirname) for filename in children_in_directory(dirname, False): raise AssertionError('Found a file that shouldn\'t have been found: ' + filename) os.rmdir(subdirname) os.rmdir(dirname)
def test_listdir(self): """Unit test for listdir()""" if 'posix' == os.name: dir1 = '/tmp' dir2 = expanduser('~/.config') if 'nt' == os.name: dir1 = expandvars(r'%windir%\fonts') dir2 = expandvars(r'%userprofile%\desktop') # If these directories do not exist, the test results are not valid. self.assertExists(dir1) self.assertExists(dir2) # Every path found in dir1 and dir2 should be found in (dir1, dir2). paths1 = set(listdir(dir1)) paths2 = set(listdir(dir2)) paths12 = set(listdir((dir1,dir2))) self.assertTrue(paths1 < paths12) self.assertTrue(paths2 < paths12) # The individual calls should be equivalent to a combined call. self.assertSetEqual(paths1.union(paths2), paths12) # The directories should not be empty. self.assertGreater(len(paths1), 0) self.assertGreater(len(paths2), 0) # Every path found should exist. for pathname in paths12: self.assertLExists(pathname)
def test_children_in_directory(self): """Unit test for function children_in_directory()""" # test an existing directory that usually exists dirname = expanduser("~/.config") for filename in children_in_directory(dirname, True): self.assertTrue(os.path.isabs(filename)) for filename in children_in_directory(dirname, False): self.assertTrue(os.path.isabs(filename)) self.assertFalse(os.path.isdir(filename)) # test a constructed file in a constructed directory dirname = self.mkdtemp(prefix='bleachbit-test-children') filename = self.mkstemp(prefix="somefile", dir=dirname) for loopfilename in children_in_directory(dirname, True): self.assertEqual(loopfilename, filename) for loopfilename in children_in_directory(dirname, False): self.assertEqual(loopfilename, filename) os.remove(filename) # test subdirectory subdirname = os.path.join(dirname, "subdir") os.mkdir(subdirname) for filename in children_in_directory(dirname, True): self.assertEqual(filename, subdirname) for filename in children_in_directory(dirname, False): raise AssertionError('Found a file that shouldn\'t have been found: ' + filename) os.rmdir(subdirname) os.rmdir(dirname)
def test_free_space(self): """Unit test for free_space()""" home = expanduser('~') result = free_space(home) self.assertNotEqual(result, None) self.assertGreater(result, -1) self.assertIsInteger(result) # compare to WMIC if 'nt' != os.name: return args = ['wmic', 'LogicalDisk', 'get', 'DeviceID,', 'FreeSpace'] from bleachbit.General import run_external (rc, stdout, stderr) = run_external(args) if rc: print('error calling WMIC\nargs=%s\nstderr=%s' % (args, stderr)) return import re for line in stdout.split('\n'): line = line.strip() if not re.match('([A-Z]):\s+(\d+)', line): continue drive, bytes_free = re.split('\s+', line) print('Checking free space for %s' % drive) bytes_free = int(bytes_free) free = free_space(unicode(drive)) self.assertEqual(bytes_free, free)
def browse_folder(parent, title, multiple, stock_button): """Ask the user to select a folder. Return the full path or None.""" if 'nt' == os.name and None == os.getenv('BB_NATIVE'): ret = Windows.browse_folder(parent.window.handle if parent else None, title) return [ret] if multiple and not ret is None else ret # fall back to GTK+ chooser = gtk.FileChooserDialog( parent=parent, title=title, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, stock_button, gtk.RESPONSE_OK), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) chooser.set_select_multiple(multiple) chooser.set_current_folder(expanduser('~')) resp = chooser.run() if multiple: ret = chooser.get_filenames() else: ret = chooser.get_filename() chooser.hide() chooser.destroy() if gtk.RESPONSE_OK != resp: # user cancelled return None return ret
def test_listdir(self): """Unit test for listdir()""" if 'posix' == os.name: dir1 = '/tmp' dir2 = expanduser('~/.config') if 'nt' == os.name: dir1 = expandvars(r'%windir%\fonts') dir2 = expandvars(r'%userprofile%\desktop') # If these directories do not exist, the test results are not valid. self.assertExists(dir1) self.assertExists(dir2) # Every path found in dir1 and dir2 should be found in (dir1, dir2). paths1 = set(listdir(dir1)) paths2 = set(listdir(dir2)) paths12 = set(listdir((dir1,dir2))) self.assertTrue(paths1 < paths12) self.assertTrue(paths2 < paths12) # The individual calls should be equivalent to a combined call. self.assertSetEqual(paths1.union(paths2), paths12) # The directories should not be empty. self.assertGreater(len(paths1), 0) self.assertGreater(len(paths2), 0) # Every path found should exist. for pathname in paths12: self.assertLExists(pathname)
def expand_glob_join(pathname1, pathname2): """Join pathname1 and pathname1, expand pathname, glob, and return as list""" ret = [] pathname3 = expanduser(bleachbit.expandvars(os.path.join(pathname1, pathname2))) for pathname4 in glob.iglob(pathname3): ret.append(pathname4) return ret
def browse_folder(parent, title, multiple, stock_button): """Ask the user to select a folder. Return the full path or None.""" if 'nt' == os.name and None == os.getenv('BB_NATIVE'): ret = Windows.browse_folder( parent.window.handle if parent else None, title) return [ret] if multiple and not ret is None else ret # fall back to GTK+ chooser = Gtk.FileChooserDialog(transient_for=parent, title=title, action = Gtk.FileChooserAction.SELECT_FOLDER) chooser.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, stock_button, Gtk.ResponseType.OK) chooser.set_default_response(Gtk.ResponseType.OK) chooser.set_select_multiple(multiple) chooser.set_current_folder(expanduser('~')) resp = chooser.run() if multiple: ret = chooser.get_filenames() else: ret = chooser.get_filename() chooser.hide() chooser.destroy() if Gtk.ResponseType.OK != resp: # user cancelled return None return ret
def worker_done(self, worker, really_delete): """Callback for when Worker is done""" self.progressbar.set_text("") self.progressbar.set_fraction(1) self.progressbar.set_text(_("Done.")) 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) logger.debug('elapsed time: %d seconds', elapsed) if elapsed < 10 or self.window.is_active(): return try: import pynotify except ImportError: logger.debug('pynotify not available') else: if pynotify.init(APP_NAME): notify = pynotify.Notification('BleachBit', _("Done."), icon='bleachbit') if 'posix' == os.name and bleachbit.expanduser('~') == '/root': notify.set_hint("desktop-entry", "bleachbit-root") else: notify.set_hint("desktop-entry", "bleachbit") notify.show() notify.set_timeout(10000)
def is_running(self): """Return whether the program is currently running""" logger = logging.getLogger(__name__) for running in self.running: test = running[0] pathname = running[1] if 'exe' == test and 'posix' == os.name: if Unix.is_running(pathname): logger.debug("process '%s' is running", pathname) return True elif 'exe' == test and 'nt' == os.name: if Windows.is_process_running(pathname): logger.debug("process '%s' is running", pathname) return True elif 'pathname' == test: expanded = expanduser(expandvars(pathname)) for globbed in glob.iglob(expanded): if os.path.exists(globbed): logger.debug( "file '%s' exists indicating '%s' is running", globbed, self.name) return True else: raise RuntimeError("Unknown running-detection test '%s'" % test) return False
def test_free_space(self): """Unit test for free_space()""" home = expanduser('~') result = free_space(home) self.assertNotEqual(result, None) self.assertGreater(result, -1) self.assertIsInteger(result) # compare to WMIC if 'nt' != os.name: return args = ['wmic', 'LogicalDisk', 'get', 'DeviceID,', 'FreeSpace'] from bleachbit.General import run_external (rc, stdout, stderr) = run_external(args) if rc: print('error calling WMIC\nargs=%s\nstderr=%s' % (args, stderr)) return import re for line in stdout.split('\n'): line = line.strip() if not re.match('([A-Z]):\s+(\d+)', line): continue drive, bytes_free = re.split('\s+', line) print('Checking free space for %s' % drive) bytes_free = int(bytes_free) free = free_space(unicode(drive)) self.assertEqual(bytes_free, free)
def browse_folder(parent, title, multiple, stock_button): """Ask the user to select a folder. Return the full path or None.""" if 'nt' == os.name and None == os.getenv('BB_NATIVE'): ret = Windows.browse_folder( parent.window.handle if parent else None, title) return [ret] if multiple and not ret is None else ret # fall back to GTK+ chooser = gtk.FileChooserDialog(parent=parent, title=title, buttons=( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, stock_button, gtk.RESPONSE_OK), action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) chooser.set_select_multiple(multiple) chooser.set_current_folder(expanduser('~')) resp = chooser.run() if multiple: ret = chooser.get_filenames() else: ret = chooser.get_filename() chooser.hide() chooser.destroy() if gtk.RESPONSE_OK != resp: # user cancelled return None return ret
def expand_glob_join(pathname1, pathname2): """Join pathname1 and pathname1, expand pathname, glob, and return as list""" ret = [] pathname3 = expanduser(bleachbit.expandvars(os.path.join(pathname1, pathname2))) for pathname4 in glob.iglob(pathname3): ret.append(pathname4) return ret
def test_delete(self): """Unit test for class Delete""" paths = [u'~'] if 'nt' == os.name: # Python 2.6 and later supports %foo% paths.append(u'%USERPROFILE%') # Python 2.5 and later supports $foo paths.append(u'${USERPROFILE}') paths.append(u'$USERPROFILE') if 'posix' == os.name: paths.append(u'$HOME') for path in paths: for mode in ('delete', 'truncate', 'delete_forward'): expanded = expanduser(expandvars(path)) filename = self.mkstemp(dir=expanded, prefix='bleachbit-action-delete') command = mode if 'delete_forward' == mode: # forward slash needs to be normalized on Windows if 'nt' == os.name: command = 'delete' filename = filename.replace('\\', '/') else: # test not needed on this OS os.remove(filename) continue action_str = u'<action command="%s" search="file" path="%s" />' % \ (command, filename) self._test_action_str(action_str) self.assertNotExists(filename)
def is_running(self): """Return whether the program is currently running""" resp_cli="" logger = logging.getLogger(__name__) for running in self.running: test = running[0] pathname = running[1] if 'exe' == test and 'posix' == os.name: if Unix.is_running(pathname): #print "debug: process '%s' is running" % pathname logger.debug("Debug: process '%s' is running", pathname) if options.get("close_run"): if not subprocess.mswindows: #print "debug: Closing process '%s'" % pathname if "--preset" in sys.argv: resp_cli = raw_input("Do you Want BleachBit to Close " + pathname + " y/n : ") else: resp = GuiBasic.message_dialog(None,"Do you Want BleachBit to Close " + pathname,gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO) if gtk.RESPONSE_YES == resp or resp_cli.lower() == "y": # user cancelled, so don't toggle option logger.debug("Debug: Closing process '%s'",pathname) subprocess.check_output(["killall", "-9", pathname]) if not Unix.is_running(pathname): logger.debug("Debug: Closing process '%s' successful",pathname) return False return True elif 'exe' == test and 'nt' == os.name: if Windows.is_process_running(pathname): #print "debug: process '%s' is running" % pathname logger.debug("Debug: process '%s' is running", pathname) if options.get("close_run"): if subprocess.mswindows: #print "debug: Closing process '%s'" % pathname if "--preset" in sys.argv: resp_cli = raw_input("Do you Want BleachBit to Close " + pathname + " y/n : ") else: resp = GuiBasic.message_dialog(None,"Do you Want BleachBit to Close " + pathname,gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO) if gtk.RESPONSE_YES == resp or resp_cli.lower() == "y": logger.debug("debug: Closing process '%s'",pathname) subprocess.check_output(["taskkill", "/IM", pathname]) if not Windows.is_process_running(pathname): logger.debug("debug: Closing process '%s' successful",pathname) return False logger.debug("process '%s' is running", pathname) return True elif 'exe' == test and 'nt' == os.name: if Windows.is_process_running(pathname): logger.debug("process '%s' is running", pathname) return True elif 'pathname' == test: expanded = expanduser(expandvars(pathname)) for globbed in glob.iglob(expanded): if os.path.exists(globbed): logger.debug("file '%s' exists indicating '%s' is running", self.name) return True else: raise RuntimeError( "Unknown running-detection test '%s'" % test) return False
def diagnostic_info(): """Return diagnostic information as a string""" s = "BleachBit version %s" % bleachbit.APP_VERSION try: import gtk s += '\nGTK version %s' % '.'.join([str(x) for x in gtk.gtk_version]) except: pass s += "\nlocal_cleaners_dir = %s" % bleachbit.local_cleaners_dir s += "\nlocale_dir = %s" % bleachbit.locale_dir s += "\noptions_dir = %s" % bleachbit.options_dir.decode(bleachbit.FSE) s += "\npersonal_cleaners_dir = %s" % bleachbit.personal_cleaners_dir.decode(bleachbit.FSE) s += "\nsystem_cleaners_dir = %s" % bleachbit.system_cleaners_dir s += "\nlocale.getdefaultlocale = %s" % str(locale.getdefaultlocale()) if 'posix' == os.name: envs = ('DESKTOP_SESSION', 'LOGNAME', 'USER', 'SUDO_UID') if 'nt' == os.name: envs = ('APPDATA', 'LocalAppData', 'LocalAppDataLow', 'Music', 'USERPROFILE', 'ProgramFiles', 'ProgramW6432', 'TMP') for env in envs: if os.getenv(env): s += "\nos.getenv('%s') = %s" % (env, os.getenv(env).decode(bleachbit.FSE)) else: s += "\nos.getenv('%s') = %s" % (env, os.getenv(env)) s += "\nos.path.expanduser('~') = %s" % bleachbit.expanduser('~').decode(bleachbit.FSE) if sys.platform.startswith('linux'): if hasattr(platform, 'linux_distribution'): s += "\nplatform.linux_distribution() = %s" % str( platform.linux_distribution()) else: s += "\nplatform.dist() = %s" % str(platform.dist()) # Mac Version Name - Dictonary "masosx_dict" macosx_dict = {'5':'Lepoard','6':'Snow Lepoard','7':'Lion','8':'Mountain Lion','9':'Mavericks','10':'Yosemite','11':'El Capitan','12':'Sierra'} if sys.platform.startswith('darwin'): if hasattr(platform, 'mac_ver'): for key in macosx_dict: if (platform.mac_ver()[0].split('.')[1] == key): s += "\nplatform.mac_ver() = %s" % str( platform.mac_ver()[0] + " (" + macosx_dict[key] + ")") else: s += "\nplatform.dist() = %s" % str(platform.dist()) if 'nt' == os.name: s += "\nplatform.win32_ver[1]() = %s" % platform.win32_ver()[1] s += "\nplatform.platform = %s" % platform.platform() s += "\nplatform.version = %s" % platform.version() s += "\nsys.argv = %s" % sys.argv s += "\nsys.executable = %s" % sys.executable s += "\nsys.version = %s" % sys.version if 'nt' == os.name: s += "\nwin32com.shell.shell.IsUserAnAdmin() = %s" % shell.IsUserAnAdmin( ) s += "\n__file__ = %s" % __file__ return s
def __init__(self, uac=True, shred_paths=None, exit=False): if uac and 'nt' == os.name and Windows.elevate_privileges(): # privileges escalated in other process sys.exit(0) if not exit: from bleachbit import RecognizeCleanerML RecognizeCleanerML.RecognizeCleanerML() register_cleaners() self.create_window() gobject.threads_init() # Redirect logging to the GUI. bb_logger = logging.getLogger('bleachbit') gtklog = GtkLoggerHandler(self.append_text) bb_logger.addHandler(gtklog) if 'nt' == os.name and 'windows_exe' == getattr(sys, 'frozen', None): # On Microsoft Windows this avoids py2exe redirecting stderr to # bleachbit.exe.log. # sys.frozen = console_exe means the console is shown from bleachbit import logger_sh bb_logger.removeHandler(logger_sh) 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 bleachbit.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." # https://www.bleachbit.org/forum/074-fails-errors try: import sqlite3 except ImportError: logger.exception( _("Error loading the SQLite module: the antivirus software may be blocking it." )) if 'posix' == os.name and bleachbit.expanduser('~') == '/root': self.append_text( _('You are running BleachBit with administrative privileges for cleaning shared parts of the system, and references to the user profile folder will clean only the root account.' )) if 'nt' == os.name and options.get('shred'): from win32com.shell.shell import IsUserAnAdmin if not IsUserAnAdmin(): self.append_text( _('Run BleachBit with administrator privileges to improve the accuracy of overwriting the contents of files.' )) self.append_text('\n') if exit: # This is used for automated testing of whether the GUI can start. gobject.idle_add(lambda: gtk.main_quit(), priority=gobject.PRIORITY_LOW)
def clean_operation(self, operation): """Perform a single cleaning operation""" operation_options = self.operations[operation] assert (isinstance(operation_options, list)) logger.debug("clean_operation('%s'), options = '%s'", operation, operation_options) if not operation_options: raise StopIteration if self.really_delete and backends[operation].is_running(): # TRANSLATORS: %s expands to a name such as 'Firefox' or 'System'. # TRANSLATORs: %s은(는) 'Firefox' 또는 'System'과 같은 이름으로 확장 err = _("%s cannot be cleaned because it is currently running. Close it, and try again.") \ % backends[operation].get_name() self.ui.append_text(err + "\n", 'error') self.total_errors += 1 return import time self.yield_time = time.time() total_size = 0 for option_id in operation_options: self.size = 0 assert (isinstance(option_id, (str, unicode))) # normal scan for cmd in backends[operation].get_commands(option_id): for ret in self.execute(cmd, '%s.%s' % (operation, option_id)): if True == ret: # Return control to PyGTK idle loop to keep # it responding allow the user to abort # PyGTK 유휴 루프에 컨트롤을 되돌려 보관 # 응답 중 사용자가 중단하도록 허용 self.yield_time = time.time() yield True if time.time() - self.yield_time > 0.25: if self.really_delete: self.ui.update_total_size(self.total_bytes) yield True self.yield_time = time.time() self.ui.update_item_size(operation, option_id, self.size) total_size += self.size # deep scan # deep scan for ds in backends[operation].get_deep_scan(option_id): if '' == ds['path']: ds['path'] = expanduser('~') if 'delete' != ds['command']: raise NotImplementedError( 'Deep scan only supports deleting now') if ds['path'] not in self.deepscans: self.deepscans[ds['path']] = [] self.deepscans[ds['path']].append(ds) self.ui.update_item_size(operation, -1, total_size)
def __init__(self, auto_exit, *args, **kwargs): super(GUI, self).__init__(*args, **kwargs) self.auto_exit = auto_exit self.set_wmclass(APP_NAME, APP_NAME) self.populate_window() # Redirect logging to the GUI. bb_logger = logging.getLogger('bleachbit') gtklog = GtkLoggerHandler(self.append_text) bb_logger.addHandler(gtklog) if os.name == 'nt' and getattr(sys, 'frozen', None) == 'windows_exe': # On Microsoft Windows this avoids py2exe redirecting stderr to # bleachbit.exe.log. # sys.frozen = console_exe means the console is shown from bleachbit import logger_sh bb_logger.removeHandler(logger_sh) Gtk.Settings.get_default().set_property( 'gtk-application-prefer-dark-theme', options.get('dark_mode')) if options.is_corrupt(): logger.error( _('Resetting the configuration file because it is corrupt: %s') % bleachbit.options_file) bleachbit.Options.init_configuration() if options.get("first_start") and os.name == 'posix' and not auto_exit: pref = PreferencesDialog(self, self.cb_refresh_operations) pref.run() options.set('first_start', False) if os.name == 'nt': # 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 as e: self.append_text( _("Error loading the SQLite module: the antivirus software may be blocking it." ), 'error') if os.name == 'posix' and bleachbit.expanduser('~') == '/root': self.append_text( _('You are running BleachBit with administrative privileges for cleaning shared parts of the system, and references to the user profile folder will clean only the root account.' )) if os.name == 'nt' and options.get('shred'): from win32com.shell.shell import IsUserAnAdmin if not IsUserAnAdmin(): self.append_text( _('Run BleachBit with administrator privileges to improve the accuracy of overwriting the contents of files.' )) self.append_text('\n') GLib.idle_add(self.cb_refresh_operations)
def test_same_partition(self): """Unit test for same_partition()""" home = expanduser('~') self.assertTrue(same_partition(home, home)) if 'posix' == os.name: self.assertFalse(same_partition(home, '/dev')) if 'nt' == os.name: home_drive = os.path.splitdrive(home)[0] from bleachbit.Windows import get_fixed_drives for drive in get_fixed_drives(): this_drive = os.path.splitdrive(drive)[0] self.assertEqual(same_partition(home, drive), home_drive == this_drive)
def _set_paths(self, raw_path, path_vars): """Set the list of paths to work on""" self.paths = [] # expand special $$foo$$ which may give multiple values for path2 in expand_multi_var(raw_path, path_vars): path3 = expanduser(expandvars(path2)) if os.name == 'nt' and path3: # convert forward slash to backslash for compatibility with getsize() # and for display. Do not convert an empty path, or it will become # the current directory (.). path3 = os.path.normpath(path3) self.paths.append(path3)
def _set_paths(self, raw_path, path_vars): """Set the list of paths to work on""" self.paths = [] # expand special $$foo$$ which may give multiple values for path2 in expand_multi_var(raw_path, path_vars): path3 = expanduser(expandvars(path2)) if os.name == 'nt' and path3: # convert forward slash to backslash for compatibility with getsize() # and for display. Do not convert an empty path, or it will become # the current directory (.). path3 = os.path.normpath(path3) self.paths.append(path3)
def test_same_partition(self): """Unit test for same_partition()""" home = expanduser('~') self.assertTrue(same_partition(home, home)) if 'posix' == os.name: self.assertFalse(same_partition(home, '/dev')) if 'nt' == os.name: home_drive = os.path.splitdrive(home)[0] from bleachbit.Windows import get_fixed_drives for drive in get_fixed_drives(): this_drive = os.path.splitdrive(drive)[0] self.assertEqual(same_partition(home, drive), home_drive == this_drive)
def init_whitelist(self): """Initialize the whitelist only once for performance 성능을 위해 한 번만 화이트리스트 초기화 """ regexes = [ '^/tmp/.X0-lock$', '^/tmp/.truecrypt_aux_mnt.*/(control|volume)$', '^/tmp/.vbox-[^/]+-ipc/lock$', '^/tmp/.wine-[0-9]+/server-.*/lock$', '^/tmp/gconfd-[^/]+/lock/ior$', '^/tmp/fsa/', # fsarchiver '^/tmp/kde-', '^/tmp/kdesudo-', '^/tmp/ksocket-', '^/tmp/orbit-[^/]+/bonobo-activation-register[a-z0-9-]*.lock$', '^/tmp/orbit-[^/]+/bonobo-activation-server-[a-z0-9-]*ior$', '^/tmp/pulse-[^/]+/pid$', '^/var/tmp/kdecache-', '^' + expanduser('~/.cache/wallpaper/'), # ~을 사용자 디렉토리로 대체하여 ^에 추가 # Clean Firefox cache from Firefox cleaner (LP#1295826) '^' + expanduser('~/.cache/mozilla'), # ~을 사용자 디렉토리로 대체하여 ^에 추가 # Clean Google Chrome cache from Google Chrome cleaner (LP#656104) '^' + expanduser('~/.cache/google-chrome'), # ~을 사용자 디렉토리로 대체하여 ^에 추가 '^' + expanduser('~/.cache/gnome-control-center/'), # ~을 사용자 디렉토리로 대체하여 ^에 추가 # iBus Pinyin # https://bugs.launchpad.net/bleachbit/+bug/1538919 '^' + expanduser('~/.cache/ibus/'), # ~을 사용자 디렉토리로 대체하여 ^에 추가 # Linux Bluetooth daemon obexd '^' + expanduser('~/.cache/obexd/')] # ~을 사용자 디렉토리로 대체하여 ^에 추가 for regex in regexes: self.regexes_compiled.append(re.compile(regex)) # regexes_compiled 리스트에 컴파일한 regexes요소들을 추가
def init_whitelist(self): """Initialize the whitelist only once for performance""" regexes = [ '^/tmp/.X0-lock$', '^/tmp/.truecrypt_aux_mnt.*/(control|volume)$', '^/tmp/.vbox-[^/]+-ipc/lock$', '^/tmp/.wine-[0-9]+/server-.*/lock$', '^/tmp/gconfd-[^/]+/lock/ior$', '^/tmp/fsa/', # fsarchiver '^/tmp/kde-', '^/tmp/kdesudo-', '^/tmp/ksocket-', '^/tmp/orbit-[^/]+/bonobo-activation-register[a-z0-9-]*.lock$', '^/tmp/orbit-[^/]+/bonobo-activation-server-[a-z0-9-]*ior$', '^/tmp/pulse-[^/]+/pid$', '^/var/tmp/kdecache-', '^' + expanduser('~/.cache/wallpaper/'), # Clean Firefox cache from Firefox cleaner (LP#1295826) '^' + expanduser('~/.cache/mozilla'), # Clean Google Chrome cache from Google Chrome cleaner (LP#656104) '^' + expanduser('~/.cache/google-chrome'), '^' + expanduser('~/.cache/gnome-control-center/'), # iBus Pinyin # https://bugs.launchpad.net/bleachbit/+bug/1538919 '^' + expanduser('~/.cache/ibus/'), # Linux Bluetooth daemon obexd '^' + expanduser('~/.cache/obexd/')] for regex in regexes: self.regexes_compiled.append(re.compile(regex))
def clean_operation(self, operation): """Perform a single cleaning operation""" operation_options = self.operations[operation] assert(isinstance(operation_options, list)) logger.debug("clean_operation('%s'), options = '%s'", operation, operation_options) if not operation_options: raise StopIteration if self.really_delete and backends[operation].is_running(): # TRANSLATORS: %s expands to a name such as 'Firefox' or 'System'. err = _("%s cannot be cleaned because it is currently running. Close it, and try again.") \ % backends[operation].get_name() self.ui.append_text(err + "\n", 'error') self.total_errors += 1 return import time self.yield_time = time.time() total_size = 0 for option_id in operation_options: self.size = 0 assert(isinstance(option_id, (str, unicode))) # normal scan for cmd in backends[operation].get_commands(option_id): for ret in self.execute(cmd, '%s.%s' % (operation, option_id)): if True == ret: # Return control to PyGTK idle loop to keep # it responding allow the user to abort self.yield_time = time.time() yield True if time.time() - self.yield_time > 0.25: if self.really_delete: self.ui.update_total_size(self.total_bytes) yield True self.yield_time = time.time() self.ui.update_item_size(operation, option_id, self.size) total_size += self.size # deep scan for ds in backends[operation].get_deep_scan(option_id): if '' == ds['path']: ds['path'] = expanduser('~') if 'delete' != ds['command']: raise NotImplementedError( 'Deep scan only supports deleting now') if ds['path'] not in self.deepscans: self.deepscans[ds['path']] = [] self.deepscans[ds['path']].append(ds) self.ui.update_item_size(operation, -1, total_size)
def __init__(self, uac=True, shred_paths=None, exit=False): if uac and 'nt' == os.name and Windows.elevate_privileges(): # privileges escalated in other process sys.exit(0) if not exit: from bleachbit import RecognizeCleanerML RecognizeCleanerML.RecognizeCleanerML() register_cleaners() self.create_window() gobject.threads_init() # Redirect logging to the GUI. bb_logger = logging.getLogger('bleachbit') gtklog = GtkLoggerHandler(self.append_text) bb_logger.addHandler(gtklog) if 'nt' == os.name and 'windows_exe' == getattr(sys, 'frozen', None): # On Microsoft Windows this avoids py2exe redirecting stderr to # bleachbit.exe.log. # sys.frozen = console_exe means the console is shown from bleachbit import logger_sh bb_logger.removeHandler(logger_sh) 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 bleachbit.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." # https://www.bleachbit.org/forum/074-fails-errors try: import sqlite3 except ImportError: logger.exception(_("Error loading the SQLite module: the antivirus software may be blocking it.")) if 'posix' == os.name and bleachbit.expanduser('~') == '/root': self.append_text( _('You are running BleachBit with administrative privileges for cleaning shared parts of the system, and references to the user profile folder will clean only the root account.')) if 'nt' == os.name and options.get('shred'): from win32com.shell.shell import IsUserAnAdmin if not IsUserAnAdmin(): self.append_text( _('Run BleachBit with administrator privileges to improve the accuracy of overwriting the contents of files.')) self.append_text('\n') if exit: # This is used for automated testing of whether the GUI can start. gobject.idle_add( lambda: gtk.main_quit(), priority=gobject.PRIORITY_LOW)
def test_DeepScan(self): """Unit test for class DeepScan. Preview real files.""" ds = DeepScan() path = expanduser('~') ds.add_search(path, '^Makefile$') ds.add_search(path, '~$') ds.add_search(path, 'bak$') ds.add_search(path, '^Thumbs.db$') ds.add_search(path, '^Thumbs.db:encryptable$') for ret in ds.scan(): if True == ret: # it's yielding control to the GTK idle loop continue self.assertLExists(ret)
def test_DeepScan(self): """Unit test for class DeepScan. Preview real files.""" ds = DeepScan() path = expanduser('~') ds.add_search(path, '^Makefile$') ds.add_search(path, '~$') ds.add_search(path, 'bak$') ds.add_search(path, '^Thumbs.db$') ds.add_search(path, '^Thumbs.db:encryptable$') for ret in ds.scan(): if True == ret: # it's yielding control to the GTK idle loop continue self.assertLExists(ret)
def test_expanduser(self): """Unit test for function expanduser()""" # already absolute test_input = '/home/user/foo' test_output = expanduser(test_input) self.assertEqual(test_input, test_output) # tilde not at beginning test_input = '/home/user/~' test_output = expanduser(test_input) self.assertEqual(test_input, test_output) # should be expanded if os.name == 'nt': test_inputs = ('~', r'~\ntuser.dat') if os.name == 'posix': test_inputs = ('~', '~/.profile') for test_input in test_inputs: test_output = expanduser(test_input) self.assertNotEqual(test_input, test_output) self.assertExists(test_output) if os.name == 'posix': self.assert_(os.path.samefile( test_output, os.path.expanduser(test_input)))
def listdir(directory): """Return full path of files in directory. Path may be a tuple of directories.""" if type(directory) is tuple: for dirname in directory: for pathname in listdir(dirname): yield pathname return dirname = expanduser(directory) if not os.path.lexists(dirname): return for filename in os.listdir(dirname): yield os.path.join(dirname, filename)
def listdir(directory): """Return full path of files in directory. Path may be a tuple of directories.""" if type(directory) is tuple: for dirname in directory: for pathname in listdir(dirname): yield pathname return dirname = expanduser(directory) if not os.path.lexists(dirname): return for filename in os.listdir(dirname): yield os.path.join(dirname, filename)
def test_DeepScan(self): """Unit test for class DeepScan. Preview real files.""" ds = DeepScan() path = expanduser('~') ds.add_search(path, '^Makefile$') ds.add_search(path, '~$') ds.add_search(path, 'bak$') ds.add_search(path, '^Thumbs.db$') ds.add_search(path, '^Thumbs.db:encryptable$') try: for ret in ds.scan(): if True == ret: # it's yielding control to the GTK idle loop continue self.assertLExists(ret) except UnicodeDecodeError: # Expectedly ds.scan() throws exception # when we have unicode paths and LANG==C. self.assertTrue(os.environ['LANG'] == 'C')
def browse_file(parent, title): """Prompt user to select a single file""" if 'nt' == os.name and None == os.getenv('BB_NATIVE'): return Windows.browse_file(parent.window.handle, title) chooser = gtk.FileChooserDialog(title=title, parent=parent, action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) chooser.set_current_folder(expanduser('~')) resp = chooser.run() path = chooser.get_filename() chooser.destroy() if gtk.RESPONSE_OK != resp: # user cancelled return None return path
def browse_file(parent, title): """Prompt user to select a single file""" if 'nt' == os.name and None == os.getenv('BB_NATIVE'): return Windows.browse_file(parent.window.handle, title) chooser = Gtk.FileChooserDialog(title=title, transient_for=parent, action=Gtk.FileChooserAction.OPEN) chooser.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_Open"), Gtk.ResponseType.OK) chooser.set_default_response(Gtk.ResponseType.OK) chooser.set_current_folder(expanduser('~')) resp = chooser.run() path = chooser.get_filename() chooser.destroy() if Gtk.ResponseType.OK != resp: # user cancelled return None return path
def test_whitelist(self): tests = [ ('/tmp/.truecrypt_aux_mnt1/control', True), ('/tmp/.truecrypt_aux_mnt1/volume', True), ('/tmp/.vbox-foo-ipc/lock', True), ('/tmp/.wine-500/server-806-102400f/lock', True), ('/tmp/gconfd-foo/lock/ior', True), ('/tmp/ksocket-foo/Arts_SoundServerV2', True), ('/tmp/ksocket-foo/secret-cookie', True), ('/tmp/orbit-foo/bonobo-activation-server-ior', True), ('/tmp/orbit-foo/bonobo-activation-register.lock', True), ('/tmp/orbit-foo/bonobo-activation-server-a9cd6cc4973af098918b154c4957a93f-ior', True), ('/tmp/orbit-foo/bonobo-activation-register-a9cd6cc4973af098918b154c4957a93f.lock', True), ('/tmp/pulse-foo/pid', True), ('/tmp/tmpsDOBFd', False), (os.path.expanduser('~/.cache/obexd'), True), (os.path.expanduser('~/.cache/obexd/'), True), (os.path.expanduser('~/.cache/obexd/foo'), True), (os.path.expanduser('~/.cache/obex'), False), (os.path.expanduser('~/.cache/obexd-foo'), False) ] list(register_cleaners()) for test in tests: self.assertEqual(backends['system'].whitelisted(test[0]), test[1], test[0]) # Make sure directory ~/.cache/obexd is ignored # https://github.com/bleachbit/bleachbit/issues/572 from bleachbit import expanduser obexd_dir = expanduser('~/.cache/obexd') if not os.path.exists(obexd_dir): os.makedirs(obexd_dir) obexd_fn = os.path.join(obexd_dir, 'bleachbit-test') common.touch_file(obexd_fn) found_canary = False for cmd in backends['system'].get_commands('cache'): for result in cmd.execute(really_delete=False): self.assertNotEqual(cmd.path, obexd_fn) self.assertFalse('/.cache/obexd/' in cmd.path) from bleachbit.FileUtilities import delete delete(obexd_fn, ignore_missing=True)
def browse_file(parent, title): """Prompt user to select a single file""" if os.name == 'nt' and not os.getenv('BB_NATIVE'): return Windows.browse_file(parent, title) chooser = Gtk.FileChooserDialog(title=title, transient_for=parent, action=Gtk.FileChooserAction.OPEN) chooser.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_Open"), Gtk.ResponseType.OK) chooser.set_default_response(Gtk.ResponseType.OK) chooser.set_current_folder(expanduser('~')) resp = chooser.run() path = chooser.get_filename() chooser.destroy() if Gtk.ResponseType.OK != resp: # user cancelled return None return path
def browse_files(parent, title): """Prompt user to select multiple files to delete""" if 'nt' == os.name and None == os.getenv('BB_NATIVE'): return Windows.browse_files(parent.window.handle, title) chooser = Gtk.FileChooserDialog(title=title, transient_for=parent, action=Gtk.FileChooserAction.OPEN) chooser.add_buttons(_("_Cancel"), Gtk.ResponseType.CANCEL, _("_Delete"), Gtk.ResponseType.OK) chooser.set_default_response(Gtk.ResponseType.OK) chooser.set_select_multiple(True) chooser.set_current_folder(expanduser('~')) resp = chooser.run() paths = chooser.get_filenames() chooser.destroy() if Gtk.ResponseType.OK != resp: # user cancelled return None return paths
def test_expanduser(self): """Unit test for expanduser.""" # Return Unicode when given str. self.assertIsUnicodeString(bleachbit.expanduser('~')) # Return Unicode when given Unicode. self.assertIsUnicodeString(bleachbit.expanduser(u'~')) # Blank input should give blank output. self.assertEqual(bleachbit.expanduser(''), u'') # An absolute path should not be altered. abs_dirs = {'posix': '$HOME', 'nt': '%USERPROFILE%'} abs_dir = os.path.expandvars(abs_dirs[os.name]) self.assertExists(abs_dir) self.assertEqual(bleachbit.expanduser(abs_dir), abs_dir) # Path with tilde should be expanded self.assertEqual(os.path.normpath(bleachbit.expanduser('~')), os.path.normpath(os.path.expanduser('~'))) # A relative path (without a reference to the home directory) # should not be expanded. self.assertEqual(bleachbit.expanduser('common'), 'common')
def get_commands(self, option_id): # cache if 'posix' == os.name and 'cache' == option_id: dirname = expanduser("~/.cache/") for filename in children_in_directory(dirname, True): if not self.whitelisted(filename): yield Command.Delete(filename) # custom if 'custom' == option_id: for (c_type, c_path) in options.get_custom_paths(): if 'file' == c_type: yield Command.Delete(c_path) elif 'folder' == c_type: yield Command.Delete(c_path) for path in children_in_directory(c_path, True): yield Command.Delete(path) else: raise RuntimeError( 'custom folder has invalid type %s' % c_type) # menu menu_dirs = ['~/.local/share/applications', '~/.config/autostart', '~/.gnome/apps/', '~/.gnome2/panel2.d/default/launchers', '~/.gnome2/vfolders/applications/', '~/.kde/share/apps/RecentDocuments/', '~/.kde/share/mimelnk', '~/.kde/share/mimelnk/application/ram.desktop', '~/.kde2/share/mimelnk/application/', '~/.kde2/share/applnk'] if 'posix' == os.name and 'desktop_entry' == option_id: for dirname in menu_dirs: for filename in [fn for fn in children_in_directory(dirname, False) if fn.endswith('.desktop')]: if Unix.is_broken_xdg_desktop(filename): yield Command.Delete(filename) # unwanted locales if 'posix' == os.name and 'localizations' == option_id: for path in Unix.locales.localization_paths(locales_to_keep=options.get_languages()): if os.path.isdir(path): for f in FileUtilities.children_in_directory(path, True): yield Command.Delete(f) yield Command.Delete(path) # Windows logs if 'nt' == os.name and 'logs' == option_id: paths = ( '$ALLUSERSPROFILE\\Application Data\\Microsoft\\Dr Watson\\*.log', '$ALLUSERSPROFILE\\Application Data\\Microsoft\\Dr Watson\\user.dmp', '$LocalAppData\\Microsoft\\Windows\\WER\\ReportArchive\\*\\*', '$LocalAppData\\Microsoft\\Windows\WER\\ReportQueue\\*\\*', '$programdata\\Microsoft\\Windows\\WER\\ReportArchive\\*\\*', '$programdata\\Microsoft\\Windows\\WER\\ReportQueue\\*\\*', '$localappdata\\Microsoft\\Internet Explorer\\brndlog.bak', '$localappdata\\Microsoft\\Internet Explorer\\brndlog.txt', '$windir\\*.log', '$windir\\imsins.BAK', '$windir\\OEWABLog.txt', '$windir\\SchedLgU.txt', '$windir\\ntbtlog.txt', '$windir\\setuplog.txt', '$windir\\REGLOCS.OLD', '$windir\\Debug\\*.log', '$windir\\Debug\\Setup\\UpdSh.log', '$windir\\Debug\\UserMode\\*.log', '$windir\\Debug\\UserMode\\ChkAcc.bak', '$windir\\Debug\\UserMode\\userenv.bak', '$windir\\Microsoft.NET\Framework\*\*.log', '$windir\\pchealth\\helpctr\\Logs\\hcupdate.log', '$windir\\security\\logs\\*.log', '$windir\\security\\logs\\*.old', '$windir\\SoftwareDistribution\\*.log', '$windir\\SoftwareDistribution\\DataStore\\Logs\\*', '$windir\\system32\\TZLog.log', '$windir\\system32\\config\\systemprofile\\Application Data\\Microsoft\\Internet Explorer\\brndlog.bak', '$windir\\system32\\config\\systemprofile\\Application Data\\Microsoft\\Internet Explorer\\brndlog.txt', '$windir\\system32\\LogFiles\\AIT\\AitEventLog.etl.???', '$windir\\system32\\LogFiles\\Firewall\\pfirewall.log*', '$windir\\system32\\LogFiles\\Scm\\SCM.EVM*', '$windir\\system32\\LogFiles\\WMI\\Terminal*.etl', '$windir\\system32\\LogFiles\\WMI\\RTBackup\EtwRT.*etl', '$windir\\system32\\wbem\\Logs\\*.lo_', '$windir\\system32\\wbem\\Logs\\*.log', ) for path in paths: expanded = expandvars(path) for globbed in glob.iglob(expanded): yield Command.Delete(globbed) # memory if sys.platform.startswith('linux') and 'memory' == option_id: yield Command.Function(None, Memory.wipe_memory, _('Memory')) # memory dump # how to manually create this file # http://www.pctools.com/guides/registry/detail/856/ if 'nt' == os.name and 'memory_dump' == option_id: fname = expandvars('$windir\\memory.dmp') if os.path.exists(fname): yield Command.Delete(fname) for fname in glob.iglob(expandvars('$windir\\Minidump\\*.dmp')): yield Command.Delete(fname) # most recently used documents list if 'posix' == os.name and 'recent_documents' == option_id: ru_fn = expanduser("~/.recently-used") if os.path.lexists(ru_fn): yield Command.Delete(ru_fn) # GNOME 2.26 (as seen on Ubuntu 9.04) will retain the list # in memory if it is simply deleted, so it must be shredded # (or at least truncated). # # GNOME 2.28.1 (Ubuntu 9.10) and 2.30 (10.04) do not re-read # the file after truncation, but do re-read it after # shredding. # # https://bugzilla.gnome.org/show_bug.cgi?id=591404 def gtk_purge_items(): """Purge GTK items""" gtk.RecentManager().purge_items() yield 0 for pathname in ["~/.recently-used.xbel", "~/.local/share/recently-used.xbel"]: pathname = expanduser(pathname) if os.path.lexists(pathname): yield Command.Shred(pathname) if HAVE_GTK: # Use the Function to skip when in preview mode yield Command.Function(None, gtk_purge_items, _('Recent documents list')) if 'posix' == os.name and 'rotated_logs' == option_id: for path in Unix.rotated_logs(): yield Command.Delete(path) # temporary files if 'posix' == os.name and 'tmp' == option_id: dirnames = ['/tmp', '/var/tmp'] for dirname in dirnames: for path in children_in_directory(dirname, True): is_open = FileUtilities.openfiles.is_open(path) ok = not is_open and os.path.isfile(path) and \ not os.path.islink(path) and \ FileUtilities.ego_owner(path) and \ not self.whitelisted(path) if ok: yield Command.Delete(path) # temporary files if 'nt' == os.name and 'tmp' == option_id: dirname1 = expandvars( "$USERPROFILE\\Local Settings\\Temp\\") dirname2 = expandvars(r'%temp%') dirname3 = expandvars("%windir%\\temp\\") dirnames = [] if Windows.get_windows_version() >= 6.0: # Windows Vista or later dirnames.append(dirname2) else: # Windows XP dirnames.append(dirname1) dirnames.append(dirname3) # whitelist the folder %TEMP%\Low but not its contents # https://bugs.launchpad.net/bleachbit/+bug/1421726 for dirname in dirnames: low = os.path.join(dirname, 'low').lower() for filename in children_in_directory(dirname, True): if not low == filename.lower(): yield Command.Delete(filename) # trash if 'posix' == os.name and 'trash' == option_id: dirname = expanduser("~/.Trash") for filename in children_in_directory(dirname, False): yield Command.Delete(filename) # fixme http://www.ramendik.ru/docs/trashspec.html # http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html # ~/.local/share/Trash # * GNOME 2.22, Fedora 9 # * KDE 4.1.3, Ubuntu 8.10 dirname = expanduser("~/.local/share/Trash/files") for filename in children_in_directory(dirname, True): yield Command.Delete(filename) dirname = expanduser("~/.local/share/Trash/info") for filename in children_in_directory(dirname, True): yield Command.Delete(filename) dirname = expanduser("~/.local/share/Trash/expunged") # [email protected] tells me that the trash # backend puts files in here temporary, but in some situations # the files are stuck. for filename in children_in_directory(dirname, True): yield Command.Delete(filename) # clipboard if HAVE_GTK and 'clipboard' == option_id: def clear_clipboard(): gtk.gdk.threads_enter() clipboard = gtk.clipboard_get() clipboard.set_text("") gtk.gdk.threads_leave() return 0 yield Command.Function(None, clear_clipboard, _('Clipboard')) # overwrite free space shred_drives = options.get_list('shred_drives') if 'free_disk_space' == option_id and shred_drives: for pathname in shred_drives: # TRANSLATORS: 'Free' means 'unallocated.' # %s expands to a path such as C:\ or /tmp/ display = _("Overwrite free disk space %s") % pathname def wipe_path_func(): for ret in FileUtilities.wipe_path(pathname, idle=True): # Yield control to GTK idle because this process # is very slow. Also display progress. yield ret yield 0 yield Command.Function(None, wipe_path_func, display) # MUICache if 'nt' == os.name and 'muicache' == option_id: keys = ( 'HKCU\\Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache', 'HKCU\\Software\\Classes\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\MuiCache') for key in keys: yield Command.Winreg(key, None) # prefetch if 'nt' == os.name and 'prefetch' == option_id: for path in glob.iglob(expandvars('$windir\\Prefetch\\*.pf')): yield Command.Delete(path) # recycle bin if 'nt' == os.name and 'recycle_bin' == option_id: # This method allows shredding recycled_any = False for path in Windows.get_recycle_bin(): recycled_any = True yield Command.Delete(path) # If there were any files deleted, Windows XP will show the # wrong icon for the recycle bin indicating it is not empty. # The icon will be incorrect until logging in to Windows again # or until it is emptied using the Windows API call for emptying # the recycle bin. # Windows 10 refreshes the recycle bin icon when the user # opens the recycle bin folder. # This is a hack to refresh the icon. def empty_recycle_bin_func(): import tempfile tmpdir = tempfile.mkdtemp() Windows.move_to_recycle_bin(tmpdir) try: Windows.empty_recycle_bin(None, True) except: logging.getLogger(__name__).info('error in empty_recycle_bin()', exc_info=True) yield 0 # Using the Function Command prevents emptying the recycle bin # when in preview mode. if recycled_any: yield Command.Function(None, empty_recycle_bin_func, _('Empty the recycle bin')) # Windows Updates if 'nt' == os.name and 'updates' == option_id: for wu in Windows.delete_updates(): yield wu
def add_search(self, dirname, regex): parent.assertEqual(dirname, expanduser('~')) parent.assertIn(regex, ['^Thumbs\\.db$', '^Thumbs\\.db:encryptable$'])
def diagnostic_info(): """Return diagnostic information as a string""" # this section is for application and library versions s = "BleachBit version %s" % bleachbit.APP_VERSION try: from bleachbit.Revision import revision s += '\nGit revision %s' % revision except: pass try: from gi.repository import Gtk s += '\nGTK version {0}.{1}.{2}'.format(Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version()) except: pass import sqlite3 s += "\nSQLite version %s" % sqlite3.sqlite_version # this section is for variables defined in __init__.py s += "\nFSE = %s" % bleachbit.FSE s += "\nlocal_cleaners_dir = %s" % bleachbit.local_cleaners_dir s += "\nlocale_dir = %s" % bleachbit.locale_dir s += "\noptions_dir = %s" % bleachbit.options_dir.decode(bleachbit.FSE) s += "\npersonal_cleaners_dir = %s" % bleachbit.personal_cleaners_dir.decode( bleachbit.FSE) s += "\nsystem_cleaners_dir = %s" % bleachbit.system_cleaners_dir # this section is for information about the system environment s += "\nlocale.getdefaultlocale = %s" % str(locale.getdefaultlocale()) if 'posix' == os.name: envs = ('DESKTOP_SESSION', 'LOGNAME', 'USER', 'SUDO_UID') if 'nt' == os.name: envs = ('APPDATA', 'LocalAppData', 'LocalAppDataLow', 'Music', 'USERPROFILE', 'ProgramFiles', 'ProgramW6432', 'TMP') for env in envs: if os.getenv(env): s += "\nos.getenv('%s') = %s" % (env, os.getenv(env).decode(bleachbit.FSE)) else: s += "\nos.getenv('%s') = %s" % (env, os.getenv(env)) s += "\nos.path.expanduser('~') = %s" % bleachbit.expanduser( '~').decode(bleachbit.FSE) if sys.platform.startswith('linux'): if hasattr(platform, 'linux_distribution'): s += "\nplatform.linux_distribution() = %s" % str( platform.linux_distribution()) else: s += "\nplatform.dist() = %s" % str(platform.dist()) # Mac Version Name - Dictionary macosx_dict = {'5': 'Leopard', '6': 'Snow Leopard', '7': 'Lion', '8': 'Mountain Lion', '9': 'Mavericks', '10': 'Yosemite', '11': 'El Capitan', '12': 'Sierra'} if sys.platform.startswith('darwin'): if hasattr(platform, 'mac_ver'): for key in macosx_dict: if (platform.mac_ver()[0].split('.')[1] == key): s += "\nplatform.mac_ver() = %s" % str( platform.mac_ver()[0] + " (" + macosx_dict[key] + ")") else: s += "\nplatform.dist() = %s" % str(platform.dist()) if 'nt' == os.name: s += "\nplatform.win32_ver[1]() = %s" % platform.win32_ver()[1] s += "\nplatform.platform = %s" % platform.platform() s += "\nplatform.version = %s" % platform.version() s += "\nsys.argv = %s" % sys.argv s += "\nsys.executable = %s" % sys.executable s += "\nsys.version = %s" % sys.version if 'nt' == os.name: s += "\nwin32com.shell.shell.IsUserAnAdmin() = %s" % shell.IsUserAnAdmin( ) s += "\n__file__ = %s" % __file__ return s