273 lines
13 KiB
Python
273 lines
13 KiB
Python
# Wizard Kit: Copy user data to the system over the network
|
|
|
|
import os
|
|
import re
|
|
from operator import itemgetter
|
|
|
|
# Init
|
|
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
|
os.system('title Wizard Kit: Data 1')
|
|
from functions import *
|
|
vars_wk = init_vars_wk()
|
|
vars_wk.update(init_vars_os())
|
|
vars_wk['LogFile'] = '{LogDir}\\Data 1.log'.format(**vars_wk)
|
|
os.makedirs('{LogDir}'.format(**vars_wk), exist_ok=True)
|
|
vars_wk['TransferDir'] = '{ClientDir}\\Transfer'.format(**vars_wk)
|
|
vars_wk['FastCopy'] = '{BinDir}\\FastCopy\\FastCopy.exe'.format(**vars_wk)
|
|
vars_wk['FastCopyArgs'] = [
|
|
'/cmd=noexist_only',
|
|
'/logfile={LogFile}'.format(**vars_wk),
|
|
'/utf8',
|
|
'/skip_empty_dir',
|
|
'/linkdest',
|
|
'/no_ui',
|
|
'/auto_close',
|
|
'/exclude=\\*.esd;\\*.swm;\\*.wim;\\*.dd;\\*.dd.tgz;\\*.dd.txz;\\*.map;\\*.dmg;\\*.image;$RECYCLE.BIN;$Recycle.Bin;.AppleDB;.AppleDesktop;.AppleDouble;.com.apple.timemachine.supported;.dbfseventsd;.DocumentRevisions-V100*;.DS_Store;.fseventsd;.PKInstallSandboxManager;.Spotlight*;.SymAV*;.symSchedScanLockxz;.TemporaryItems;.Trash*;.vol;.VolumeIcon.icns;desktop.ini;Desktop?DB;Desktop?DF;hiberfil.sys;lost+found;Network?Trash?Folder;pagefile.sys;Recycled;RECYCLER;System?Volume?Information;Temporary?Items;Thumbs.db']
|
|
vars_wk['Notepad2'] = '{BinDir}\\Notepad2\\Notepad2-Mod.exe'.format(**vars_wk)
|
|
vars_wk['wimlib-imagex'] = '{BinDir}\\wimlib\\x32\\wimlib-imagex.exe'.format(**vars_wk)
|
|
if vars_wk['Arch'] == 64:
|
|
vars_wk['FastCopy'] = vars_wk['FastCopy'].replace('FastCopy.exe', 'FastCopy64.exe')
|
|
vars_wk['Notepad2'] = vars_wk['Notepad2'].replace('Notepad2-Mod.exe', 'Notepad2-Mod64.exe')
|
|
vars_wk['wimlib-imagex'] = vars_wk['wimlib-imagex'].replace('x32', 'x64')
|
|
re_included_root_items = re.compile(r'^\\(AdwCleaner|(My\s*|)(Doc(uments?( and Settings|)|s?)|Downloads|WK(-?Info|-?Transfer|)|Media|Music|Pic(ture|)s?|Vid(eo|)s?)|(ProgramData|Recovery|Temp.*|Users)$|.*\.(log|txt|rtf|qb\w*|avi|m4a|m4v|mp4|mkv|jpg|png|tiff?)$)', flags=re.IGNORECASE)
|
|
re_excluded_root_items = re.compile(r'^\\(boot(mgr|nxt)$|(eula|globdata|install|vc_?red)|.*.sys$|System Volume Information|RECYCLER|\$Recycle\.bin|\$?Win(dows(.old|\.~BT|)$|RE_)|\$GetCurrent|PerfLogs|Program Files|.*\.(esd|swm|wim|dd|map|dmg|image)$|SYSTEM.SAV|Windows10Upgrade)', flags=re.IGNORECASE)
|
|
re_excluded_items = re.compile(r'^(\.(AppleDB|AppleDesktop|AppleDouble|com\.apple\.timemachine\.supported|dbfseventsd|DocumentRevisions-V100.*|DS_Store|fseventsd|PKInstallSandboxManager|Spotlight.*|SymAV.*|symSchedScanLockxz|TemporaryItems|Trash.*|vol|VolumeIcon\.icns)|desktop\.(ini|.*DB|.*DF)|(hiberfil|pagefile)\.sys|lost\+found|Network\.*Trash\.*Folder|Recycle[dr]|System\.*Volume\.*Information|Temporary\.*Items|Thumbs\.db)$', flags=re.IGNORECASE)
|
|
wim_included_extra_items = [
|
|
'AdwCleaner\\*log',
|
|
'AdwCleaner\\*txt',
|
|
'\\Windows.old*\\Doc*',
|
|
'\\Windows.old*\\Download*',
|
|
'\\Windows.old*\\WK*',
|
|
'\\Windows.old*\\Media*',
|
|
'\\Windows.old*\\Music*',
|
|
'\\Windows.old*\\Pic*',
|
|
'\\Windows.old*\\ProgramData',
|
|
'\\Windows.old*\\Recovery',
|
|
'\\Windows.old*\\Temp*',
|
|
'\\Windows.old*\\Users',
|
|
'\\Windows.old*\\Vid*',
|
|
'\\Windows.old*\\Windows\\System32\\OEM',
|
|
'\\Windows.old*\\Windows\\System32\\config',
|
|
'\\Windows\\System32\\OEM',
|
|
'\\Windows\\System32\\config']
|
|
|
|
def abort():
|
|
print_warning('Aborted.', vars_wk['LogFile'])
|
|
exit_script()
|
|
|
|
def cleanup_transfer():
|
|
"""Fix permissions and walk through transfer folder (from the bottom) and remove extraneous items."""
|
|
run_program('attrib -a -h -r -s "{TransferDir}"'.format(**vars_wk), check=False)
|
|
if os.path.exists(vars_wk['TransferDir']):
|
|
for root, dirs, files in os.walk(vars_wk['TransferDir'], topdown=False):
|
|
for name in dirs:
|
|
# Remove empty directories and junction points
|
|
try:
|
|
os.rmdir(os.path.join(root, name))
|
|
except OSError:
|
|
pass
|
|
for name in files:
|
|
# Remove files based on exclusion regex
|
|
if re_excluded_items.search(name):
|
|
try:
|
|
os.remove(os.path.join(root, name))
|
|
except OSError:
|
|
pass
|
|
|
|
def exit_script():
|
|
umount_backup_shares()
|
|
pause("Press Enter to exit...")
|
|
extract_item('Notepad2', vars_wk, silent=True)
|
|
subprocess.Popen('"{Notepad2}" "{LogFile}"'.format(**vars_wk))
|
|
quit()
|
|
|
|
def is_valid_image(item):
|
|
_valid = item.is_file() and re.search(r'\.wim$', item.name, flags=re.IGNORECASE)
|
|
if _valid:
|
|
try:
|
|
_cmd = [vars_wk['wimlib-imagex'], 'info', '{image}'.format(image=item.path)]
|
|
run_program(_cmd)
|
|
except subprocess.CalledProcessError:
|
|
_valid = False
|
|
print_warning('WARNING: Image "{image}" damaged.'.format(image=item.name), vars_wk['LogFile'])
|
|
time.sleep(2)
|
|
return _valid
|
|
|
|
def transfer_file_based(source_path, subdir=None):
|
|
# Set Destination
|
|
if subdir is None:
|
|
dest_path = vars_wk['TransferDir']
|
|
else:
|
|
dest_path = '{TransferDir}\\{subdir}'.format(subdir=subdir, **vars_wk)
|
|
os.makedirs(dest_path, exist_ok=True)
|
|
|
|
# Main copy
|
|
selected_items = []
|
|
for item in os.scandir(source_path):
|
|
if re_included_root_items.search(item.name):
|
|
selected_items.append(item.path)
|
|
elif not re_excluded_root_items.search(item.name):
|
|
if ask('Copy: "{name}" item?'.format(name=item.name)):
|
|
selected_items.append(item.path)
|
|
if len(selected_items) > 0:
|
|
_args = vars_wk['FastCopyArgs'].copy() + selected_items
|
|
_args.append('/to={dest_path}\\'.format(dest_path=dest_path))
|
|
try:
|
|
print_standard('Copying main user data...', vars_wk['LogFile'])
|
|
run_program(vars_wk['FastCopy'], _args, check=True)
|
|
except subprocess.CalledProcessError:
|
|
print_warning('WARNING: Errors encountered while copying main user data', vars_wk['LogFile'])
|
|
else:
|
|
print_error('ERROR: No files selected for transfer?', vars_wk['LogFile'])
|
|
abort()
|
|
|
|
# Fonts
|
|
selected_items = []
|
|
if os.path.exists('{source}\\Windows\\Fonts'.format(source=source_path)):
|
|
selected_items.append('{source}\\Windows\\Fonts'.format(source=source_path))
|
|
if len(selected_items) > 0:
|
|
_args = vars_wk['FastCopyArgs'].copy() + selected_items
|
|
_args.append('/to={dest_path}\\Windows\\'.format(dest_path=dest_path))
|
|
try:
|
|
print_standard('Copying Fonts...', vars_wk['LogFile'])
|
|
run_program(vars_wk['FastCopy'], _args, check=True)
|
|
except subprocess.CalledProcessError:
|
|
print_warning('WARNING: Errors encountered while copying Fonts', vars_wk['LogFile'])
|
|
|
|
# Registry
|
|
selected_items = []
|
|
if os.path.exists('{source}\\Windows\\System32\\config'.format(source=source_path)):
|
|
selected_items.append('{source}\\Windows\\System32\\config'.format(source=source_path))
|
|
if os.path.exists('{source}\\Windows\\System32\\OEM'.format(source=source_path)):
|
|
selected_items.append('{source}\\Windows\\System32\\OEM'.format(source=source_path))
|
|
if len(selected_items) > 0:
|
|
_args = vars_wk['FastCopyArgs'].copy() + selected_items
|
|
_args.append('/to={dest_path}\\Windows\\System32\\'.format(dest_path=dest_path))
|
|
try:
|
|
print_standard('Copying Registry...', vars_wk['LogFile'])
|
|
run_program(vars_wk['FastCopy'], _args, check=True)
|
|
except subprocess.CalledProcessError:
|
|
print_warning('WARNING: Errors encountered while copying registry', vars_wk['LogFile'])
|
|
|
|
def transfer_image_based(source_path):
|
|
print_standard('Assessing image...', vars_wk['LogFile'])
|
|
os.makedirs(vars_wk['TransferDir'], exist_ok=True)
|
|
|
|
# Scan source
|
|
_args = [
|
|
'dir',
|
|
'{source}'.format(source=source_path), '1']
|
|
try:
|
|
_list = run_program(vars_wk['wimlib-imagex'], _args, check=True)
|
|
except subprocess.CalledProcessError as err:
|
|
print_error('ERROR: Failed to get file list.', vars_wk['LogFile'])
|
|
print(err)
|
|
abort()
|
|
|
|
# Add items to list
|
|
selected_items = []
|
|
root_items = [i.strip() for i in _list.stdout.decode('utf-8', 'ignore').splitlines() if i.count('\\') == 1 and i.strip() != '\\']
|
|
for item in root_items:
|
|
if re_included_root_items.search(item):
|
|
selected_items.append(item)
|
|
elif not re_excluded_root_items.search(item):
|
|
if ask('Extract: "{name}" item?'.format(name=item)):
|
|
selected_items.append(item)
|
|
|
|
# Extract files
|
|
if len(selected_items) > 0:
|
|
# Write files.txt
|
|
with open('{TmpDir}\\wim_files.txt'.format(**vars_wk), 'w') as f:
|
|
# Defaults
|
|
for item in wim_included_extra_items:
|
|
f.write('{item}\n'.format(item=item))
|
|
for item in selected_items:
|
|
f.write('{item}\n'.format(item=item))
|
|
try:
|
|
print_standard('Extracting user data...', vars_wk['LogFile'])
|
|
_args = [
|
|
'extract',
|
|
'{source}'.format(source=source_path), '1',
|
|
'@{TmpDir}\\wim_files.txt'.format(**vars_wk),
|
|
'--dest-dir={TransferDir}\\'.format(**vars_wk),
|
|
'--no-acls',
|
|
'--nullglob']
|
|
run_program(vars_wk['wimlib-imagex'], _args, check=True)
|
|
except subprocess.CalledProcessError:
|
|
print_warning('WARNING: Errors encountered while extracting user data', vars_wk['LogFile'])
|
|
else:
|
|
print_error('ERROR: No files selected for extraction?', vars_wk['LogFile'])
|
|
abort()
|
|
|
|
if __name__ == '__main__':
|
|
stay_awake(vars_wk)
|
|
# Check for existing TransferDir
|
|
if os.path.exists(vars_wk['TransferDir']):
|
|
print_warning('Folder "{TransferDir}" exists. This script will rename the existing folder in order to avoid overwriting data.'.format(**vars_wk), vars_wk['LogFile'])
|
|
if (ask('Rename existing folder and proceed?', vars_wk['LogFile'])):
|
|
_old_transfer = '{TransferDir}.old'.format(**vars_wk)
|
|
_i = 1;
|
|
while os.path.exists(_old_transfer):
|
|
_old_transfer = '{TransferDir}.old{i}'.format(i=_i, **vars_wk)
|
|
_i += 1
|
|
print_info('Renaming "{TransferDir}" to "{old_transfer}"'.format(old_transfer=_old_transfer, **vars_wk), vars_wk['LogFile'])
|
|
os.rename(vars_wk['TransferDir'], _old_transfer)
|
|
else:
|
|
abort()
|
|
|
|
# Set ticket number
|
|
ticket = None
|
|
while ticket is None:
|
|
tmp = input('Enter ticket number: ')
|
|
if re.match(r'^([0-9]+([-_]?\w+|))$', tmp):
|
|
ticket = tmp
|
|
|
|
# Get backup
|
|
backup_source = select_backup(ticket)
|
|
if backup_source is None:
|
|
abort()
|
|
|
|
# Determine restore method
|
|
extract_item('wimlib', vars_wk, silent=True)
|
|
restore_source = None
|
|
restore_options = []
|
|
_file_based = False
|
|
for item in os.scandir(backup_source.path):
|
|
if item.is_dir():
|
|
_file_based = True
|
|
restore_options.append({'Name': 'File-Based:\t{source}'.format(source=item.name), 'Source': item})
|
|
for _subitem in os.scandir(item.path):
|
|
if is_valid_image(_subitem):
|
|
restore_options.append({'Name': 'Image-Based: {dir}\\{source}'.format(dir=item.name, source=_subitem.name), 'Source': _subitem})
|
|
elif is_valid_image(item):
|
|
restore_options.append({'Name': 'Image-Based:\t{source}'.format(source=item.name), 'Source': item})
|
|
if _file_based:
|
|
restore_options.append({'Name': 'File-Based:\t.', 'Source': backup_source})
|
|
restore_options = sorted(restore_options, key=itemgetter('Name'))
|
|
actions = [{'Name': 'Quit', 'Letter': 'Q'}]
|
|
if len(restore_options) > 0:
|
|
selection = menu_select('Which backup are we using? (Path: {path})'.format(path=backup_source.path), restore_options, actions)
|
|
if selection == 'Q':
|
|
abort()
|
|
else:
|
|
restore_source = restore_options[int(selection)-1]['Source']
|
|
else:
|
|
print_error('ERROR: No restoration options detected.', vars_wk['LogFile'])
|
|
abort()
|
|
|
|
# Start transfer
|
|
print_info('Using backup: {path}'.format(path=restore_source.path), vars_wk['LogFile'])
|
|
if restore_source.is_dir():
|
|
transfer_file_based(restore_source.path)
|
|
# Check for Windows.old*
|
|
for item in os.scandir(restore_source.path):
|
|
if item.is_dir() and re.search(r'^Windows.old', item.name, re.IGNORECASE):
|
|
transfer_file_based(item.path, subdir=item.name)
|
|
if restore_source.is_file():
|
|
transfer_image_based(restore_source.path)
|
|
cleanup_transfer()
|
|
|
|
# Done
|
|
print_success('Done.', vars_wk['LogFile'])
|
|
kill_process('caffeine.exe')
|
|
exit_script()
|