├── OneDriveExplorer ├── ode │ ├── __init__.py │ ├── views │ │ ├── __init__.py │ │ └── multiselect.py │ ├── helpers │ │ ├── __init__.py │ │ ├── Manual │ │ │ └── Images │ │ │ │ ├── chat.png │ │ │ │ ├── find.png │ │ │ │ ├── live.png │ │ │ │ ├── logs.png │ │ │ │ ├── note.png │ │ │ │ ├── ode.png │ │ │ │ ├── view.png │ │ │ │ ├── blog2.png │ │ │ │ ├── columns.png │ │ │ │ ├── context.png │ │ │ │ ├── details.png │ │ │ │ ├── email.png │ │ │ │ ├── event.png │ │ │ │ ├── logs2.png │ │ │ │ ├── loose.png │ │ │ │ ├── meeting.png │ │ │ │ ├── odlogs.png │ │ │ │ ├── recbin.png │ │ │ │ ├── startup.png │ │ │ │ ├── status.png │ │ │ │ ├── add_info.png │ │ │ │ ├── cstructs.png │ │ │ │ ├── file_menu.png │ │ │ │ ├── messages.png │ │ │ │ ├── userhive.png │ │ │ │ ├── import_csv.png │ │ │ │ ├── odsettings.png │ │ │ │ ├── options_menu.png │ │ │ │ ├── preferences.png │ │ │ │ ├── preferences1.png │ │ │ │ ├── sharepoint.png │ │ │ │ ├── import_saved_data.png │ │ │ │ ├── messages_number.png │ │ │ │ ├── onedrive_folders.png │ │ │ │ └── onedrive_folders2.png │ │ ├── ScrollableNotebookpatch.py │ │ ├── schema │ │ ├── AnimatedGif.py │ │ ├── mft.py │ │ ├── ScrollableNotebook.py │ │ └── pandastablepatch.py │ ├── parsers │ │ ├── __init__.py │ │ ├── Nucleus │ │ │ ├── filesondemand.py │ │ │ ├── fileusagesync.py │ │ │ └── listsync.py │ │ ├── recbin.py │ │ └── csv_file.py │ ├── renderers │ │ ├── __init__.py │ │ ├── json.py │ │ ├── csv_file.py │ │ ├── html.py │ │ └── project.py │ └── utils.py ├── Images │ ├── ode.ico │ ├── splash.png │ ├── colors │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ ├── 9.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 13.png │ │ ├── 14.png │ │ ├── 15.png │ │ ├── 10_big.png │ │ ├── 11_big.png │ │ ├── 12_big.png │ │ ├── 13_big.png │ │ ├── 14_big.png │ │ ├── 15_big.png │ │ ├── 1_big.png │ │ ├── 2_big.png │ │ ├── 3_big.png │ │ ├── 4_big.png │ │ ├── 5_big.png │ │ ├── 6_big.png │ │ ├── 7_big.png │ │ ├── 8_big.png │ │ └── 9_big.png │ ├── gui │ │ ├── hdd.png │ │ ├── info.png │ │ ├── load.gif │ │ ├── note.png │ │ ├── ode.png │ │ ├── Icon109.png │ │ ├── Icon114.png │ │ ├── Icon59.png │ │ ├── email.png │ │ ├── error.png │ │ ├── tools.png │ │ ├── warning.png │ │ ├── calendar.png │ │ ├── magnifier.png │ │ ├── onedrive.png │ │ ├── question.png │ │ ├── registry.png │ │ ├── trashcan.png │ │ ├── Sharepoint.png │ │ ├── chat-bubble.png │ │ ├── error_small.png │ │ ├── info_small.png │ │ ├── information.png │ │ ├── table_sort_asc.png │ │ ├── table_sort_desc.png │ │ └── file_yellow_trashcan.png │ ├── menu │ │ ├── no.png │ │ ├── PDF-16.png │ │ ├── PNG-16.png │ │ ├── saved.png │ │ ├── skin.png │ │ ├── table.png │ │ ├── tables.png │ │ ├── controls.png │ │ ├── profile.png │ │ ├── cloud_cyan.png │ │ ├── delete_red.png │ │ ├── ode_small.png │ │ ├── table_new.png │ │ ├── format_normal.png │ │ ├── language_blue.png │ │ ├── table_column.png │ │ ├── table_delete.png │ │ ├── computer_desktop.png │ │ ├── directory_open.png │ │ ├── question_small.png │ │ ├── arrow_plain_green_S.png │ │ ├── files_yellow_combine.png │ │ ├── floppy_35inch_black.png │ │ ├── floppy_35inch_green.png │ │ └── file_yellow_hierarchy1_expanded.png │ ├── splashv.png │ ├── folders │ │ ├── 67.png │ │ ├── link.png │ │ ├── sync.png │ │ ├── not_link.png │ │ ├── not_sync.png │ │ ├── unknown.png │ │ ├── directory.png │ │ ├── not_link_folder.png │ │ ├── sync_directory.png │ │ ├── directory_closed.png │ │ ├── directory_unknown.png │ │ └── not_sync_directory.png │ ├── popup │ │ ├── copy.png │ │ ├── Icon11.ico │ │ ├── Icon136.ico │ │ ├── Icon282.ico │ │ ├── hierarchy1.png │ │ ├── table_new.png │ │ ├── language_window.png │ │ ├── file_yellow_open.png │ │ ├── hierarchy1_expanded.png │ │ └── file_yellow_empty_new.png │ ├── search │ │ ├── hdd.png │ │ ├── cloud_big.png │ │ ├── link_big.png │ │ ├── sync_big.png │ │ ├── unknown.png │ │ ├── vault_big.png │ │ ├── cloud_p_big.png │ │ ├── locked_big.png │ │ ├── online_big.png │ │ ├── shared_big.png │ │ ├── unknown_big.png │ │ ├── available_big.png │ │ ├── building_big.png │ │ ├── directory_big.png │ │ ├── excluded_big.png │ │ ├── not_link_big.png │ │ ├── not_sync_big.png │ │ ├── vault_open_big.png │ │ ├── checked_out_big.png │ │ ├── file_yellow_big.png │ │ ├── online_not_link_big.png │ │ ├── online_not_sync_big.png │ │ ├── always_available_big.png │ │ ├── available_offline_big.png │ │ └── file_yellow_delete_big.png │ ├── status │ │ ├── locked.png │ │ ├── online.png │ │ ├── shared.png │ │ ├── available.png │ │ ├── excluded.png │ │ ├── checked_out.png │ │ ├── online-link.png │ │ ├── online_sync.png │ │ ├── always_available.png │ │ ├── online_not_link.png │ │ ├── online_not_sync.png │ │ └── available_offline.png │ ├── titles │ │ ├── table.ico │ │ ├── OneDrive.ico │ │ ├── controls.ico │ │ ├── cstruct.ico │ │ ├── favicon.ico │ │ ├── question.ico │ │ ├── trashcan.ico │ │ ├── file_yellow.ico │ │ ├── window_info.ico │ │ ├── language_blue.ico │ │ ├── windows_tile.ico │ │ ├── file_yellow_delete.ico │ │ └── files_yellow_combine.ico │ ├── files │ │ ├── file_yellow.png │ │ ├── not_link_file.png │ │ ├── not_sync_file.png │ │ └── file_yellow_delete.png │ └── COPYING ├── requirements.txt ├── build.spec └── OneDriveExplorer.py ├── Images ├── csv.png ├── gui.png ├── odl.png ├── hive.png ├── html.png ├── json.png ├── skins.png ├── cmd_help.png ├── message.png ├── project.png ├── rc_menu1.png ├── rc_menu2.png ├── search.png ├── file_menu.png ├── preference.png └── message_indicator.png ├── Docs └── manual │ ├── ode.png │ ├── blog2.png │ ├── chat.png │ ├── email.png │ ├── event.png │ ├── find.png │ ├── live.png │ ├── logs.png │ ├── logs2.png │ ├── loose.png │ ├── note.png │ ├── view.png │ ├── add_info.png │ ├── columns.png │ ├── context.png │ ├── cstructs.png │ ├── details.png │ ├── meeting.png │ ├── messages.png │ ├── odlogs.png │ ├── recbin.png │ ├── startup.png │ ├── status.png │ ├── userhive.png │ ├── file_menu.png │ ├── import_csv.png │ ├── odsettings.png │ ├── sharepoint.png │ ├── options_menu.png │ ├── preferences.png │ ├── preferences1.png │ ├── messages_number.png │ ├── import_saved_data.png │ ├── onedrive_folders.png │ └── onedrive_folders2.png ├── LICENSE └── README.md /OneDriveExplorer/ode/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/parsers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/renderers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Images/csv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/csv.png -------------------------------------------------------------------------------- /Images/gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/gui.png -------------------------------------------------------------------------------- /Images/odl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/odl.png -------------------------------------------------------------------------------- /Images/hive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/hive.png -------------------------------------------------------------------------------- /Images/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/html.png -------------------------------------------------------------------------------- /Images/json.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/json.png -------------------------------------------------------------------------------- /Images/skins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/skins.png -------------------------------------------------------------------------------- /Docs/manual/ode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/ode.png -------------------------------------------------------------------------------- /Images/cmd_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/cmd_help.png -------------------------------------------------------------------------------- /Images/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/message.png -------------------------------------------------------------------------------- /Images/project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/project.png -------------------------------------------------------------------------------- /Images/rc_menu1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/rc_menu1.png -------------------------------------------------------------------------------- /Images/rc_menu2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/rc_menu2.png -------------------------------------------------------------------------------- /Images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/search.png -------------------------------------------------------------------------------- /Docs/manual/blog2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/blog2.png -------------------------------------------------------------------------------- /Docs/manual/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/chat.png -------------------------------------------------------------------------------- /Docs/manual/email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/email.png -------------------------------------------------------------------------------- /Docs/manual/event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/event.png -------------------------------------------------------------------------------- /Docs/manual/find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/find.png -------------------------------------------------------------------------------- /Docs/manual/live.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/live.png -------------------------------------------------------------------------------- /Docs/manual/logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/logs.png -------------------------------------------------------------------------------- /Docs/manual/logs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/logs2.png -------------------------------------------------------------------------------- /Docs/manual/loose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/loose.png -------------------------------------------------------------------------------- /Docs/manual/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/note.png -------------------------------------------------------------------------------- /Docs/manual/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/view.png -------------------------------------------------------------------------------- /Images/file_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/file_menu.png -------------------------------------------------------------------------------- /Images/preference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/preference.png -------------------------------------------------------------------------------- /Docs/manual/add_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/add_info.png -------------------------------------------------------------------------------- /Docs/manual/columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/columns.png -------------------------------------------------------------------------------- /Docs/manual/context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/context.png -------------------------------------------------------------------------------- /Docs/manual/cstructs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/cstructs.png -------------------------------------------------------------------------------- /Docs/manual/details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/details.png -------------------------------------------------------------------------------- /Docs/manual/meeting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/meeting.png -------------------------------------------------------------------------------- /Docs/manual/messages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/messages.png -------------------------------------------------------------------------------- /Docs/manual/odlogs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/odlogs.png -------------------------------------------------------------------------------- /Docs/manual/recbin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/recbin.png -------------------------------------------------------------------------------- /Docs/manual/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/startup.png -------------------------------------------------------------------------------- /Docs/manual/status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/status.png -------------------------------------------------------------------------------- /Docs/manual/userhive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/userhive.png -------------------------------------------------------------------------------- /Docs/manual/file_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/file_menu.png -------------------------------------------------------------------------------- /Docs/manual/import_csv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/import_csv.png -------------------------------------------------------------------------------- /Docs/manual/odsettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/odsettings.png -------------------------------------------------------------------------------- /Docs/manual/sharepoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/sharepoint.png -------------------------------------------------------------------------------- /Docs/manual/options_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/options_menu.png -------------------------------------------------------------------------------- /Docs/manual/preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/preferences.png -------------------------------------------------------------------------------- /Docs/manual/preferences1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/preferences1.png -------------------------------------------------------------------------------- /Images/message_indicator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Images/message_indicator.png -------------------------------------------------------------------------------- /Docs/manual/messages_number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/messages_number.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/ode.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/ode.ico -------------------------------------------------------------------------------- /Docs/manual/import_saved_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/import_saved_data.png -------------------------------------------------------------------------------- /Docs/manual/onedrive_folders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/onedrive_folders.png -------------------------------------------------------------------------------- /Docs/manual/onedrive_folders2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/Docs/manual/onedrive_folders2.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/splash.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/1.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/2.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/3.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/4.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/5.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/6.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/7.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/8.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/9.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/hdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/hdd.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/info.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/load.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/load.gif -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/note.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/ode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/ode.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/no.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/splashv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/splashv.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/10.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/11.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/12.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/13.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/14.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/15.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/67.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/67.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/Icon109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/Icon109.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/Icon114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/Icon114.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/Icon59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/Icon59.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/email.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/error.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/tools.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/warning.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/PDF-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/PDF-16.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/PNG-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/PNG-16.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/saved.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/saved.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/skin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/skin.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/table.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/tables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/tables.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/copy.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/hdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/hdd.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/10_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/10_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/11_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/11_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/12_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/12_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/13_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/13_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/14_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/14_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/15_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/15_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/1_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/1_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/2_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/2_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/3_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/3_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/4_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/4_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/5_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/5_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/6_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/6_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/7_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/7_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/8_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/8_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/colors/9_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/colors/9_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/link.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/sync.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/calendar.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/magnifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/magnifier.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/onedrive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/onedrive.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/question.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/registry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/registry.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/trashcan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/trashcan.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/controls.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/profile.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/Icon11.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/Icon11.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/Icon136.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/Icon136.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/Icon282.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/Icon282.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/locked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/locked.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/online.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/shared.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/shared.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/table.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/table.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/not_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/not_link.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/not_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/not_sync.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/unknown.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/Sharepoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/Sharepoint.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/chat-bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/chat-bubble.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/error_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/error_small.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/info_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/info_small.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/information.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/information.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/cloud_cyan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/cloud_cyan.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/delete_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/delete_red.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/ode_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/ode_small.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/table_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/table_new.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/hierarchy1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/hierarchy1.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/table_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/table_new.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/cloud_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/cloud_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/link_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/link_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/sync_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/sync_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/unknown.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/vault_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/vault_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/available.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/available.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/excluded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/excluded.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/OneDrive.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/OneDrive.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/controls.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/controls.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/cstruct.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/cstruct.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/favicon.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/question.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/question.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/trashcan.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/trashcan.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/files/file_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/files/file_yellow.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/directory.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/table_sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/table_sort_asc.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/format_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/format_normal.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/language_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/language_blue.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/table_column.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/table_column.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/table_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/table_delete.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/cloud_p_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/cloud_p_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/locked_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/locked_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/online_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/online_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/shared_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/shared_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/unknown_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/unknown_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/checked_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/checked_out.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/online-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/online-link.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/online_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/online_sync.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/file_yellow.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/file_yellow.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/window_info.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/window_info.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/files/not_link_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/files/not_link_file.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/files/not_sync_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/files/not_sync_file.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/table_sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/table_sort_desc.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/computer_desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/computer_desktop.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/directory_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/directory_open.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/question_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/question_small.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/language_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/language_window.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/available_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/available_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/building_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/building_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/directory_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/directory_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/excluded_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/excluded_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/not_link_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/not_link_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/not_sync_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/not_sync_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/vault_open_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/vault_open_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/language_blue.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/language_blue.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/windows_tile.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/windows_tile.ico -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/not_link_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/not_link_folder.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/sync_directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/sync_directory.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/file_yellow_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/file_yellow_open.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/checked_out_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/checked_out_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/file_yellow_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/file_yellow_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/always_available.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/always_available.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/online_not_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/online_not_link.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/online_not_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/online_not_sync.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/chat.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/find.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/find.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/live.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/live.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/logs.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/note.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/ode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/ode.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/view.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/files/file_yellow_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/files/file_yellow_delete.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/directory_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/directory_closed.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/directory_unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/directory_unknown.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/folders/not_sync_directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/folders/not_sync_directory.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/gui/file_yellow_trashcan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/gui/file_yellow_trashcan.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/arrow_plain_green_S.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/arrow_plain_green_S.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/files_yellow_combine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/files_yellow_combine.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/floppy_35inch_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/floppy_35inch_black.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/floppy_35inch_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/floppy_35inch_green.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/hierarchy1_expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/hierarchy1_expanded.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/online_not_link_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/online_not_link_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/online_not_sync_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/online_not_sync_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/status/available_offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/status/available_offline.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/file_yellow_delete.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/file_yellow_delete.ico -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/blog2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/blog2.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/columns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/columns.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/context.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/details.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/email.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/event.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/event.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/logs2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/logs2.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/loose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/loose.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/meeting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/meeting.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/odlogs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/odlogs.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/recbin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/recbin.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/startup.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/status.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/popup/file_yellow_empty_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/popup/file_yellow_empty_new.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/always_available_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/always_available_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/available_offline_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/available_offline_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/titles/files_yellow_combine.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/titles/files_yellow_combine.ico -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/add_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/add_info.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/cstructs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/cstructs.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/file_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/file_menu.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/messages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/messages.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/userhive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/userhive.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/search/file_yellow_delete_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/search/file_yellow_delete_big.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/import_csv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/import_csv.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/odsettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/odsettings.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/options_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/options_menu.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/preferences.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/preferences1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/preferences1.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/sharepoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/sharepoint.png -------------------------------------------------------------------------------- /OneDriveExplorer/Images/menu/file_yellow_hierarchy1_expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/Images/menu/file_yellow_hierarchy1_expanded.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/import_saved_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/import_saved_data.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/messages_number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/messages_number.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/onedrive_folders.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/onedrive_folders.png -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/Manual/Images/onedrive_folders2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Beercow/OneDriveExplorer/HEAD/OneDriveExplorer/ode/helpers/Manual/Images/onedrive_folders2.png -------------------------------------------------------------------------------- /OneDriveExplorer/requirements.txt: -------------------------------------------------------------------------------- 1 | Cerberus==1.3.7 2 | dissect.cstruct==3.14 3 | keyboard==0.13.5 4 | numpy==2.2.6 5 | openpyxl==3.1.5 6 | pandas==2.2.3 7 | pandastable==0.14.0 8 | pillow==11.2.1 9 | psutil==7.0.0 10 | pycryptodome==3.23.0 11 | python-registry==1.3.1 12 | pytsk3==20250312 13 | pytz==2025.2 14 | quickxorhash==1.0.5 15 | ruamel.yaml==0.18.11 16 | ttkthemes==3.2.2 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Brian Maloney 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OneDriveExplorer/Images/COPYING: -------------------------------------------------------------------------------- 1 | Toolbar Icons is made available under the terms of the MIT License. 2 | See http://toolbaricons.sourceforge.net/ for more information. 3 | 4 | Copyright (c) 2010 Florian Haag 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/ScrollableNotebookpatch.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2022 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | from ode.helpers.ScrollableNotebook import ScrollableNotebook 26 | 27 | 28 | class MyScrollableNotebook(ScrollableNotebook): 29 | """Custom table class inherits from Table. You can then override required methods""" 30 | def __init__(self, parent=None, **kwargs): 31 | ScrollableNotebook.__init__(self, parent, **kwargs) 32 | return 33 | 34 | def _rightSlide(self, event): 35 | try: 36 | self.notebookTab.select((self.notebookTab.index(self.notebookTab.select())) + 1) 37 | except Exception: 38 | pass 39 | if self.notebookTab.winfo_width() > self.notebookContent.winfo_width()-self.menuSpace: 40 | if (self.notebookContent.winfo_width()-(self.notebookTab.winfo_width()+self.notebookTab.winfo_x())) <= self.menuSpace+5: 41 | self.xLocation -= 20 42 | self.notebookTab.place(x=self.xLocation, y=0) 43 | 44 | def _leftSlide(self, event): 45 | try: 46 | self.notebookTab.select((self.notebookTab.index(self.notebookTab.select())) - 1) 47 | except Exception: 48 | pass 49 | if not self.notebookTab.winfo_x() == 0: 50 | self.xLocation += 20 51 | self.notebookTab.place(x=self.xLocation, y=0) 52 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/renderers/json.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import os 26 | import json 27 | import logging 28 | 29 | log = logging.getLogger(__name__) 30 | 31 | 32 | def print_json(cache, name, fus, pretty, json_path): 33 | log.info('Started writing JSON file') 34 | 35 | if fus: 36 | cache["FileUsageSync"] = fus 37 | 38 | if not os.path.exists(json_path): 39 | os.makedirs(json_path) 40 | 41 | if pretty: 42 | json_object = json.dumps(cache, 43 | sort_keys=False, 44 | indent=4, 45 | separators=(',', ': ') 46 | ) 47 | else: 48 | json_object = json.dumps(cache) 49 | 50 | file_extension = os.path.splitext(name)[1][1:] 51 | 52 | if file_extension == 'previous': 53 | output = open(json_path + '\\' + os.path.basename(name).split('.')[0]+"_"+file_extension+"_OneDrive.json", 'w') 54 | 55 | elif cache['Name'] == 'Microsoft.ListSync.db': 56 | output = open(json_path + '\\' + os.path.basename(name).split('.')[0]+"_OneDrive_list_sync.json", 'w') 57 | 58 | elif cache['Name'] == 'Microsoft.FilesOnDemand.db': 59 | output = open(json_path + '\\' + os.path.basename(name).split('.')[0]+"_OneDrive_fod.json", 'w') 60 | 61 | else: 62 | output = open(json_path + '\\' + os.path.basename(name).split('.')[0]+"_OneDrive.json", 'w') 63 | 64 | output.write(json_object) 65 | output.close() 66 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/schema: -------------------------------------------------------------------------------- 1 | # cstruct schema version 2.0 2 | cstruct = { 3 | 'Description': { 4 | 'required': True, 5 | 'type': 'string' 6 | }, 7 | 'Author': { 8 | 'required': True, 9 | 'type': 'string' 10 | }, 11 | 'Version': { 12 | 'required': True, 13 | 'type': 'float' 14 | }, 15 | 'Id': { 16 | 'required': True, 17 | 'type': 'string', 18 | 'regex': '^(?:\\{{0,1}(?:[0-9a-fA-F]){8}-(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){12}\\}{0,1})$' 19 | }, 20 | 'Code_File': { 21 | 'required': True, 22 | 'type': 'string' 23 | }, 24 | 'Functions': { 25 | 'required': True, 26 | 'type': 'list', 27 | 'schema': { 28 | 'type': 'dict', 29 | 'schema': { 30 | 'Description': { 31 | 'required': True, 32 | 'type': 'string' 33 | }, 34 | 'Flags': { 35 | 'required': True, 36 | 'type': 'list' 37 | }, 38 | 'Function': { 39 | 'required': True, 40 | 'type': 'string' 41 | }, 42 | 'Structure': { 43 | 'required': True, 44 | 'type': 'string', 45 | 'regex': '^(?m)(#define %s_des "%s"\nstruct %s {).*$' 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | # menu_data schema version 2.3 53 | menu_data = { 54 | 'theme': { 55 | 'required': True, 56 | 'type': 'string' 57 | }, 58 | 'json': { 59 | 'required': True, 60 | 'type': 'boolean' 61 | }, 62 | 'pretty': { 63 | 'required': True, 64 | 'type': 'boolean' 65 | }, 66 | 'csv': { 67 | 'required': True, 68 | 'type': 'boolean' 69 | }, 70 | 'html': { 71 | 'required': True, 72 | 'type': 'boolean' 73 | }, 74 | 'path': { 75 | 'required': True, 76 | 'type': 'string' 77 | }, 78 | 'hive': { 79 | 'required': True, 80 | 'type': 'boolean' 81 | }, 82 | 'odl': { 83 | 'required': True, 84 | 'type': 'boolean' 85 | }, 86 | 'odl_cor': { 87 | 'required': True, 88 | 'type': 'boolean' 89 | }, 90 | 'odl_save': { 91 | 'required': True, 92 | 'type': 'boolean' 93 | }, 94 | 'legacy': { 95 | 'required': True, 96 | 'type': 'boolean' 97 | }, 98 | 'font': { 99 | 'required': True, 100 | 'nullable': True, 101 | 'type': 'string' 102 | }, 103 | 'Date_created': { 104 | 'required': True, 105 | 'type': 'boolean' 106 | }, 107 | 'Date_accessed': { 108 | 'required': True, 109 | 'type': 'boolean' 110 | }, 111 | 'Date_modified': { 112 | 'required': True, 113 | 'type': 'boolean' 114 | }, 115 | 'Size': { 116 | 'required': True, 117 | 'type': 'boolean' 118 | } 119 | } -------------------------------------------------------------------------------- /OneDriveExplorer/ode/renderers/csv_file.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import os 26 | import json 27 | import pandas as pd 28 | import logging 29 | 30 | log = logging.getLogger(__name__) 31 | 32 | 33 | def print_csv(df, rbin_df, name, csv_path, comment, fus): 34 | log.info('Started writing CSV file') 35 | data_dict = json.loads(comment) 36 | 37 | if not os.path.exists(csv_path): 38 | os.makedirs(csv_path) 39 | 40 | if not df.empty: 41 | parent_col = 'parentResourceID' if 'parentResourceID' in df.columns else 'ParentFileSystemId' 42 | df = df.sort_values(by=['Level', parent_col, 'Type', 'FileSort', 'FolderSort', 'libraryType'], 43 | ascending=[False, False, False, True, False, False]) 44 | 45 | df = df.drop(['Level', 'FileSort', 'FolderSort'], axis=1) 46 | 47 | if not rbin_df.empty: 48 | df = pd.concat([df, rbin_df], ignore_index=True, axis=0) 49 | 50 | csv_file = os.path.basename(name).split('.')[0]+"_OneDrive.csv" 51 | fus_file = os.path.basename(name).split('.')[0]+"_FileUsageSync.csv" 52 | 53 | if data_dict["Name"] == 'Microsoft.ListSync.db': 54 | csv_file = os.path.basename(name).split('.')[0]+"_OneDrive_list_sync.csv" 55 | 56 | if data_dict["Name"] == 'Microsoft.FilesOnDemand.db': 57 | csv_file = os.path.basename(name).split('.')[0]+"_OneDrive_fod.csv" 58 | 59 | file_extension = os.path.splitext(name)[1][1:] 60 | 61 | if file_extension == 'previous': 62 | csv_file = os.path.basename(name).split('.')[0]+"_"+file_extension+"_OneDrive.csv" 63 | fus_file = os.path.basename(name).split('.')[0]+"_"+file_extension+"_FileUsageSync.csv" 64 | 65 | if not df.empty: 66 | with open(csv_path + '/' + csv_file, 'w', encoding='utf-8', newline='') as f: 67 | f.write(f'#{comment}\n') # Add your comment here 68 | df.to_csv(f, index=False, encoding='utf-8') 69 | 70 | if not fus.empty: 71 | with open(csv_path + '/' + fus_file, 'w', encoding='utf-8', newline='') as f: 72 | fus.to_csv(f, index=False, encoding='utf-8') 73 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/renderers/html.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import os 26 | import pandas as pd 27 | import logging 28 | 29 | log = logging.getLogger(__name__) 30 | 31 | 32 | def print_html(df, rbin_df, name, html_path, db_name, fus): 33 | log.info('Started writing HTML file') 34 | 35 | if not os.path.exists(html_path): 36 | os.makedirs(html_path) 37 | 38 | if not df.empty: 39 | parent_col = 'parentResourceID' if 'parentResourceID' in df.columns else 'ParentFileSystemId' 40 | df = df.sort_values(by=['Level', parent_col, 'Type', 'FileSort', 'FolderSort', 'libraryType'], 41 | ascending=[False, False, False, True, False, False]) 42 | 43 | df = df.drop(['Level', 'FileSort', 'FolderSort'], axis=1) 44 | 45 | if not rbin_df.empty: 46 | df = pd.concat([df, rbin_df], ignore_index=True, axis=0) 47 | 48 | if not df.empty: 49 | df = df.apply(lambda x: x.fillna(0) if x.dtype == 'Int64' else x.fillna('') if x.dtype == 'object' else x) 50 | 51 | html_file = os.path.basename(name).split('.')[0]+"_OneDrive.html" 52 | fus_file = os.path.basename(name).split('.')[0]+"_FileUsageSync.html" 53 | file_extension = os.path.splitext(name)[1][1:] 54 | 55 | if db_name == 'Microsoft.ListSync.db': 56 | html_file = os.path.basename(name).split('.')[0]+"_OneDrive_list_sync.html" 57 | 58 | if db_name == 'Microsoft.FilesOnDemand.db': 59 | html_file = os.path.basename(name).split('.')[0]+"_OneDrive_fod.html" 60 | 61 | if file_extension == 'previous': 62 | html_file = os.path.basename(name).split('.')[0]+"_"+file_extension+"_OneDrive.html" 63 | fus_file = os.path.basename(name).split('.')[0]+"_"+file_extension+"_FileUsageSync.html" 64 | 65 | if not df.empty: 66 | output = open(html_path + '/' + html_file, 'w', encoding='utf-8') 67 | output.write(df.to_html(index=False)) 68 | output.close() 69 | 70 | if not fus.empty: 71 | output = open(html_path + '/' + fus_file, 'w', encoding='utf-8') 72 | output.write(fus.to_html(index=False)) 73 | output.close() 74 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/AnimatedGif.py: -------------------------------------------------------------------------------- 1 | """ AnimatedGIF - a class to show an animated gif without blocking the tkinter mainloop() 2 | 3 | Copyright (c) 2016 Ole Jakob Skjelten 4 | Released under the terms of the MIT license (https://opensource.org/licenses/MIT) as described in LICENSE.md 5 | 6 | """ 7 | import sys 8 | import time 9 | import tkinter as tk # for Python3 10 | from tkinter import ttk 11 | 12 | 13 | class AnimatedGif(tk.Label): 14 | """ 15 | Class to show animated GIF file in a label 16 | Use start() method to begin animation, and set the stop flag to stop it 17 | """ 18 | def __init__(self, root, gif_file, delay=0.04, bg=''): 19 | """ 20 | :param root: tk.parent 21 | :param gif_file: filename (and path) of animated gif 22 | :param delay: delay between frames in the gif animation (float) 23 | """ 24 | ttk.Label.__init__(self, root) 25 | self.root = root 26 | self.configure(background=bg) 27 | self.gif_file = gif_file 28 | self.delay = delay # Animation delay - try low floats, like 0.04 (depends on the gif in question) 29 | self.stop = False # Thread exit request flag 30 | 31 | self._num = 0 32 | 33 | def start(self): 34 | """ Starts non-threaded version that we need to manually update() """ 35 | self.start_time = time.time() # Starting timer 36 | self._animate() 37 | 38 | def stop(self): 39 | """ This stops the after loop that runs the animation, if we are using the after() approach """ 40 | self.stop = True 41 | 42 | def _animate(self): 43 | try: 44 | self.gif = tk.PhotoImage(file=self.gif_file, format='gif -index {}'.format(self._num)) # Looping through the frames 45 | self.configure(image=self.gif) 46 | self._num += 1 47 | except tk.TclError: # When we try a frame that doesn't exist, we know we have to start over from zero 48 | self._num = 0 49 | if not self.stop: # If the stop flag is set, we don't repeat 50 | self.root.after(int(self.delay*1000), self._animate) 51 | 52 | def start_thread(self): 53 | """ This starts the thread that runs the animation, if we are using a threaded approach """ 54 | from threading import Thread # We only import the module if we need it 55 | self._animation_thread = Thread() 56 | self._animation_thread = Thread(target=self._animate_thread).start() # Forks a thread for the animation 57 | 58 | def stop_thread(self): 59 | """ This stops the thread that runs the animation, if we are using a threaded approach """ 60 | self.stop = True 61 | 62 | def _animate_thread(self): 63 | """ Updates animation, if it is running as a separate thread """ 64 | while self.stop is False: # Normally this would block mainloop(), but not here, as this runs in separate thread 65 | try: 66 | time.sleep(self.delay) 67 | self.gif = tk.PhotoImage(file=self.gif_file, format='gif -index {}'.format(self._num)) # Looping through the frames 68 | self.configure(image=self.gif) 69 | self._num += 1 70 | except tk.TclError: # When we try a frame that doesn't exist, we know we have to start over from zero 71 | self._num = 0 72 | except RuntimeError: 73 | sys.exit() 74 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/mft.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2022 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import re 26 | import io 27 | import logging 28 | import psutil 29 | import pytsk3 30 | 31 | log = logging.getLogger(__name__) 32 | dirPath = '/' 33 | partitionList = psutil.disk_partitions() 34 | filedata = '' 35 | 36 | 37 | def directoryRecurse(directoryObject, parentPath, user, filedata=False): 38 | for entryObject in directoryObject: 39 | if entryObject.info.name.name in [b".", b".."]: 40 | continue 41 | 42 | try: 43 | f_type = entryObject.info.name.type 44 | size = entryObject.info.meta.size 45 | except Exception: 46 | continue 47 | 48 | try: 49 | 50 | filepath = '/%s/%s' % ('/'.join(parentPath), entryObject.info.name.name.decode()) 51 | 52 | if f_type == pytsk3.TSK_FS_NAME_TYPE_REG and entryObject.info.meta.size != 0: 53 | searchResult = re.match(b'NTUSER.DAT$', entryObject.info.name.name) 54 | 55 | if not searchResult: 56 | continue 57 | 58 | filepath = filepath.replace('/', '\\') 59 | log.info(f'File: {parentPath}, {filepath}, {entryObject.info.name.name.decode()}, {entryObject.info.meta.size}') 60 | filedata = entryObject.read_random(0, entryObject.info.meta.size) 61 | filedata = io.BytesIO(filedata) 62 | return filedata 63 | break 64 | 65 | elif f_type == pytsk3.TSK_FS_NAME_TYPE_REG and entryObject.info.meta.size == 0: 66 | pass 67 | 68 | else: 69 | continue 70 | 71 | except IOError as e: 72 | log.error(e) 73 | continue 74 | 75 | 76 | def live_hive(user, profile_path): 77 | for partition in partitionList: 78 | imagehandle = pytsk3.Img_Info('\\\\.\\'+partition.device.strip("\\")) 79 | 80 | if 'NTFS' in partition.fstype: 81 | log.info(partition) 82 | filesystemObject = pytsk3.FS_Info(imagehandle) 83 | directoryObject = filesystemObject.open_dir(path=profile_path) 84 | data = directoryRecurse(directoryObject, [], user, filedata=filedata) 85 | return data 86 | -------------------------------------------------------------------------------- /OneDriveExplorer/build.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | 4 | block_cipher = None 5 | 6 | 7 | OneDriveExplorer_a = Analysis(['OneDriveExplorer.py'], 8 | pathex=[], 9 | binaries=[], 10 | datas=[], 11 | hiddenimports=[], 12 | hookspath=[], 13 | runtime_hooks=[], 14 | excludes=[], 15 | win_no_prefer_redirects=False, 16 | win_private_assemblies=False, 17 | cipher=block_cipher, 18 | noarchive=False) 19 | 20 | OneDriveExplorer_pyz = PYZ(OneDriveExplorer_a.pure, OneDriveExplorer_a.zipped_data, 21 | cipher=block_cipher) 22 | 23 | OneDriveExplorer_a.datas += [('ode\\helpers\\schema', './ode\\helpers\\schema', 'DATA')] 24 | OneDriveExplorer_a.datas += [('ode\\helpers\\structures', './ode\\helpers\\structures', 'DATA')] 25 | 26 | OneDriveExplorer_exe = EXE(OneDriveExplorer_pyz, 27 | OneDriveExplorer_a.scripts, 28 | OneDriveExplorer_a.binaries, 29 | OneDriveExplorer_a.zipfiles, 30 | OneDriveExplorer_a.datas, 31 | [], 32 | name='OneDriveExplorer', 33 | icon='./Images/ode.ico', 34 | debug=False, 35 | bootloader_ignore_signals=False, 36 | strip=False, 37 | upx=True, 38 | upx_exclude=[], 39 | runtime_tmpdir=None, 40 | console=True, 41 | disable_windowed_traceback=False, 42 | target_arch=None, 43 | codesign_identity=None, 44 | entitlements_file=None ) 45 | 46 | OneDriveExplorer_GUI_a = Analysis(['OneDriveExplorer_GUI.py'], 47 | pathex=[], 48 | binaries=[], 49 | datas=[], 50 | hiddenimports=[], 51 | hookspath=[], 52 | runtime_hooks=[], 53 | excludes=[], 54 | win_no_prefer_redirects=False, 55 | win_private_assemblies=False, 56 | cipher=block_cipher, 57 | noarchive=False) 58 | 59 | OneDriveExplorer_GUI_pyz = PYZ(OneDriveExplorer_GUI_a.pure, OneDriveExplorer_GUI_a.zipped_data, 60 | cipher=block_cipher) 61 | 62 | OneDriveExplorer_GUI_splash = Splash('Images\\splashv.png', 63 | binaries=OneDriveExplorer_GUI_a.binaries, 64 | datas=OneDriveExplorer_GUI_a.datas, 65 | text_pos=(15, 260), 66 | text_size=12, 67 | text_color='#0364b8', 68 | minify_script=True) 69 | 70 | OneDriveExplorer_GUI_a.datas += Tree('./Images', prefix='Images') 71 | OneDriveExplorer_GUI_a.datas += Tree('./ode/helpers/Manual', prefix='ode/helpers/Manual') 72 | OneDriveExplorer_GUI_a.datas += [('ode\\helpers\\schema', './ode\\helpers\\schema', 'DATA')] 73 | OneDriveExplorer_GUI_a.datas += [('ode\\helpers\\structures', './ode\\helpers\\structures', 'DATA')] 74 | 75 | OneDriveExplorer_GUI_exe = EXE(OneDriveExplorer_GUI_pyz, 76 | OneDriveExplorer_GUI_a.scripts, 77 | OneDriveExplorer_GUI_a.binaries, 78 | OneDriveExplorer_GUI_a.zipfiles, 79 | OneDriveExplorer_GUI_a.datas, 80 | OneDriveExplorer_GUI_splash, 81 | OneDriveExplorer_GUI_splash.binaries, 82 | [], 83 | name='OneDriveExplorer_GUI', 84 | icon='./Images/ode.ico', 85 | debug=False, 86 | bootloader_ignore_signals=False, 87 | strip=False, 88 | upx=True, 89 | upx_exclude=[], 90 | runtime_tmpdir=None, 91 | console=False, 92 | disable_windowed_traceback=False, 93 | target_arch=None, 94 | codesign_identity=None, 95 | entitlements_file=None ) -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/ScrollableNotebook.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (c) Muhammet Emin TURGUT 2020 4 | # For license see LICENSE 5 | from tkinter import * 6 | from tkinter import ttk 7 | 8 | class ScrollableNotebook(ttk.Frame): 9 | def __init__(self,parent,wheelscroll=False,tabmenu=False,*args,**kwargs): 10 | ttk.Frame.__init__(self, parent, *args) 11 | self.xLocation = 0 12 | self.notebookContent = ttk.Notebook(self,**kwargs) 13 | self.notebookContent.pack(fill="both", expand=True) 14 | self.notebookTab = ttk.Notebook(self,**kwargs) 15 | self.notebookTab.bind("<>",self._tabChanger) 16 | if wheelscroll==True: self.notebookTab.bind("", self._wheelscroll) 17 | slideFrame = ttk.Frame(self) 18 | slideFrame.place(relx=1.0, x=0, y=1, anchor=NE) 19 | self.menuSpace=30 20 | if tabmenu==True: 21 | self.menuSpace=50 22 | bottomTab = ttk.Label(slideFrame, text=" \u2630 ") 23 | bottomTab.bind("<1>",self._bottomMenu) 24 | bottomTab.pack(side=RIGHT) 25 | leftArrow = ttk.Label(slideFrame, text=" \u276E") 26 | leftArrow.bind("<1>",self._leftSlide) 27 | leftArrow.pack(side=LEFT) 28 | rightArrow = ttk.Label(slideFrame, text=" \u276F") 29 | rightArrow.bind("<1>",self._rightSlide) 30 | rightArrow.pack(side=RIGHT) 31 | self.notebookContent.bind("", self._resetSlide) 32 | 33 | def _wheelscroll(self, event): 34 | if event.delta > 0: 35 | self._leftSlide(event) 36 | else: 37 | self._rightSlide(event) 38 | 39 | def _bottomMenu(self,event): 40 | tabListMenu = Menu(self, tearoff = 0) 41 | for tab in self.notebookTab.tabs(): 42 | tabListMenu.add_command(label=self.notebookTab.tab(tab, option="text"),command= lambda temp=tab: self.select(temp)) 43 | try: 44 | tabListMenu.tk_popup(event.x_root, event.y_root) 45 | finally: 46 | tabListMenu.grab_release() 47 | 48 | def _tabChanger(self,event): 49 | try: self.notebookContent.select(self.notebookTab.index("current")) 50 | except: pass 51 | 52 | def _rightSlide(self,event): 53 | if self.notebookTab.winfo_width()>self.notebookContent.winfo_width()-self.menuSpace: 54 | if (self.notebookContent.winfo_width()-(self.notebookTab.winfo_width()+self.notebookTab.winfo_x()))<=self.menuSpace+5: 55 | self.xLocation-=20 56 | self.notebookTab.place(x=self.xLocation,y=0) 57 | def _leftSlide(self,event): 58 | if not self.notebookTab.winfo_x()== 0: 59 | self.xLocation+=20 60 | self.notebookTab.place(x=self.xLocation,y=0) 61 | 62 | def _resetSlide(self,event=None): 63 | self.notebookTab.place(x=0,y=0) 64 | self.xLocation = 0 65 | 66 | def add(self,frame,**kwargs): 67 | if len(self.notebookTab.winfo_children())!=0: 68 | self.notebookContent.add(frame, text="") 69 | else: 70 | self.notebookContent.add(frame, text="") 71 | self.notebookTab.add(ttk.Frame(self.notebookTab),**kwargs) 72 | 73 | def forget(self,tab_id): 74 | self.notebookContent.forget(self.__ContentTabID(tab_id)) 75 | self.notebookTab.forget(tab_id) 76 | 77 | def hide(self,tab_id): 78 | self.notebookContent.hide(self.__ContentTabID(tab_id)) 79 | self.notebookTab.hide(tab_id) 80 | 81 | def identify(self,x, y): 82 | return self.notebookTab.identify(x,y) 83 | 84 | def index(self,tab_id): 85 | return self.notebookTab.index(tab_id) 86 | 87 | def __ContentTabID(self,tab_id): 88 | return self.notebookContent.tabs()[self.notebookTab.tabs().index(tab_id)] 89 | 90 | def insert(self,pos,frame, **kwargs): 91 | self.notebookContent.insert(pos,frame, **kwargs) 92 | self.notebookTab.insert(pos,frame,**kwargs) 93 | 94 | def select(self,tab_id): 95 | ## self.notebookContent.select(self.__ContentTabID(tab_id)) 96 | self.notebookTab.select(tab_id) 97 | 98 | def tab(self,tab_id, option=None, **kwargs): 99 | kwargs_Content = kwargs.copy() 100 | kwargs_Content["text"] = "" # important 101 | self.notebookContent.tab(self.__ContentTabID(tab_id), option=None, **kwargs_Content) 102 | return self.notebookTab.tab(tab_id, option=None, **kwargs) 103 | 104 | def tabs(self): 105 | ## return self.notebookContent.tabs() 106 | return self.notebookTab.tabs() 107 | 108 | def enable_traversal(self): 109 | self.notebookContent.enable_traversal() 110 | self.notebookTab.enable_traversal() -------------------------------------------------------------------------------- /OneDriveExplorer/ode/parsers/Nucleus/filesondemand.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import hashlib 26 | import sqlite3 27 | import logging 28 | import pandas as pd 29 | 30 | 31 | class ParseResult: 32 | def __init__(self, df, df_scope, scopeID, account): 33 | self.df = df 34 | self.df_scope = df_scope 35 | self.scopeID = scopeID 36 | self.account = account 37 | 38 | def __repr__(self): 39 | """Custom string representation for debugging.""" 40 | return f"ParseResult(df={len(self.df)} rows, scopeID={len(self.scopeID)})" 41 | 42 | 43 | class SQLiteTableExporter: 44 | def __init__(self, db_path): 45 | self.log = logging.getLogger(__name__) 46 | self.db_path = db_path 47 | self.df_scope = pd.DataFrame() 48 | self.scopeID = [] 49 | self.df = pd.DataFrame() 50 | self.account = None 51 | self.filename = None 52 | self.directory = None 53 | self.hash = None 54 | 55 | def hash_file(self, file): 56 | BUF_SIZE = 65536 57 | sha1 = hashlib.sha1() 58 | 59 | try: 60 | with open(file, 'rb') as f: 61 | while True: 62 | data = f.read(BUF_SIZE) 63 | if not data: 64 | break 65 | sha1.update(data) 66 | 67 | return sha1.hexdigest() 68 | except Exception: 69 | return '' 70 | 71 | def get_files_on_denamd_rows(self): 72 | self.directory = self.db_path.rsplit('\\', 1)[0] 73 | self.filename = self.db_path.split('\\')[-1] 74 | self.hash = self.hash_file(self.db_path) 75 | self.account = self.db_path.replace('/', '\\').split('\\')[-3] 76 | 77 | try: 78 | # Connect to the SQLite database 79 | self.conn = sqlite3.connect(f'file:/{self.db_path}?mode=ro', uri=True) 80 | self.cursor = self.conn.cursor() 81 | 82 | try: 83 | self.df_scope = pd.read_sql_query("SELECT mountId, siteId, webId, listId, webUrl, mountPoint, libraryType from files_on_demand_mountpoints", self.conn) 84 | self.df_scope.insert(0, 'Type', 'Scope') 85 | self.df_scope.rename(columns={"siteId": "siteID"}, inplace=True) 86 | self.df_scope.rename(columns={"webId": "webID"}, inplace=True) 87 | self.df_scope.rename(columns={"listId": "listID"}, inplace=True) 88 | self.df_scope.rename(columns={"webUrl": "webURL"}, inplace=True) 89 | self.df_scope.rename(columns={"mountPoint": "MountPoint"}, inplace=True) 90 | 91 | self.scopeID = self.df_scope['mountId'].tolist() 92 | 93 | df_files = pd.read_sql_query("SELECT PinState, UniqueId, FileSystemId, CASE WHEN ParentFileSystemId = 'root' THEN mountId ELSE ParentFileSystemId END AS ParentFileSystemId, FonDLastModified, Name FROM files_on_demand_rows WHERE FileSystemId NOT IN (SELECT DISTINCT ParentFileSystemId FROM files_on_demand_rows WHERE ParentFileSystemId IS NOT NULL);", self.conn) 94 | df_files.insert(0, 'Type', 'File') 95 | 96 | df_folders = pd.read_sql_query("SELECT PinState, UniqueId, FileSystemId, CASE WHEN ParentFileSystemId = 'root' THEN mountId ELSE ParentFileSystemId END AS ParentFileSystemId, FonDLastModified, Name FROM files_on_demand_rows WHERE FileSystemId IN (SELECT DISTINCT ParentFileSystemId FROM files_on_demand_rows WHERE ParentFileSystemId IS NOT NULL);", self.conn) 97 | df_folders.insert(0, 'Type', 'Folder') 98 | 99 | self.df = pd.concat([self.df_scope, df_files, df_folders], ignore_index=True, axis=0) 100 | self.df = self.df.where(pd.notnull(self.df), None) 101 | self.df.rename(columns={"FonDLastModified": "lastChange"}, inplace=True) 102 | self.df['lastChange'] = pd.to_datetime(self.df['lastChange'], unit='ms').astype(str) 103 | self.df['PinState'] = self.df['PinState'].astype("Int64").fillna(0) 104 | self.df['size'] = '' 105 | self.df['fileStatus'] = '' 106 | self.df['folderStatus'] = '' 107 | 108 | except Exception as e: 109 | self.log.warning(f'Unable to parse {self.db_path}. {e}') 110 | self.df = pd.DataFrame() 111 | self.df_scope = pd.DataFrame() 112 | self.scopeID = [] 113 | 114 | self.conn.close() 115 | 116 | except sqlite3.OperationalError: 117 | self.log.info('Microsoft.FilesOnDemand.db does not exist') 118 | self.df = pd.DataFrame() 119 | self.df_scope = pd.DataFrame() 120 | self.scopeID = [] 121 | 122 | return ParseResult( 123 | self.df, 124 | self.df_scope, 125 | self.scopeID, 126 | self.account 127 | ) 128 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/views/multiselect.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import tkinter as tk 26 | from tkinter import ttk, filedialog 27 | import ctypes 28 | 29 | # These are your Windows-specific style constants 30 | GWL_STYLE = -16 31 | WS_MAXIMIZEBOX = 0x00010000 32 | WS_MINIMIZEBOX = 0x00020000 33 | 34 | get_parent = ctypes.windll.user32.GetParent 35 | get_window_long = ctypes.windll.user32.GetWindowLongW 36 | set_window_long = ctypes.windll.user32.SetWindowLongW 37 | 38 | 39 | class FileSelectDialog: 40 | def __init__(self, root, fields=None, callback=None, title="Select Files", icon_path=None): 41 | self.root = root 42 | self.fields = fields or [("Select .dat File", ".dat"), ("Select .sql File", ".sql")] 43 | self.callback = callback # Called with result on submit 44 | self.icon_path = icon_path 45 | self.create_dialog(title) 46 | 47 | def create_dialog(self, title): 48 | self.win = tk.Toplevel(self.root) 49 | self.win.title(title) 50 | self.win.wm_transient(self.root) 51 | if self.icon_path: 52 | self.win.iconbitmap(self.icon_path) 53 | self.win.grab_set() 54 | self.win.focus_force() 55 | self.win.resizable(False, False) 56 | self.win.protocol("WM_DELETE_WINDOW", self.on_cancel) 57 | self.setup_window_style() 58 | 59 | self.file_vars = {} 60 | self.build_widgets() 61 | self.sync_windows() 62 | self.root.bind('', self.sync_windows) 63 | self.win.bind('', self.sync_windows) 64 | 65 | def setup_window_style(self): 66 | hwnd = get_parent(self.win.winfo_id()) 67 | old_style = get_window_long(hwnd, GWL_STYLE) 68 | new_style = old_style & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX 69 | set_window_long(hwnd, GWL_STYLE, new_style) 70 | 71 | def build_widgets(self): 72 | self.row_widgets = {} 73 | self.frame = ttk.Frame(self.win) 74 | self.inner_frame = ttk.Frame(self.frame, padding=10) 75 | 76 | self.frame.grid(row=0, column=0) 77 | self.inner_frame.grid(row=0, column=0) 78 | 79 | for idx, (label_text, ext) in enumerate(self.fields): 80 | var = tk.StringVar() 81 | self.file_vars[ext] = var 82 | var.trace_add("write", lambda *args, e=ext: self.check_field_states()) 83 | 84 | label = ttk.Label(self.inner_frame, text=label_text) 85 | entry = ttk.Entry(self.inner_frame, textvariable=var, width=50) 86 | button = ttk.Button(self.inner_frame, text="Browse", command=lambda e=ext: self.browse_file(e)) 87 | 88 | label.grid(row=idx, column=0, sticky='w', pady=5) 89 | entry.grid(row=idx, column=1, pady=5, padx=(5, 5)) 90 | button.grid(row=idx, column=2, pady=5) 91 | 92 | self.row_widgets[ext] = (label, entry, button) 93 | 94 | # Buttons 95 | ttk.Button(self.inner_frame, text="Submit", command=self.on_submit).grid(row=len(self.fields), column=1, sticky='e', pady=10, padx=5) 96 | ttk.Button(self.inner_frame, text="Cancel", command=self.on_cancel).grid(row=len(self.fields), column=2, sticky='w', pady=10, padx=5) 97 | 98 | self.check_field_states() 99 | 100 | def check_field_states(self): 101 | try: 102 | sed_filled = bool(self.file_vars["SyncEngineDatabase.db"].get().strip()) 103 | ntuser_filled = bool(self.file_vars["NTUSER.DAT"].get().strip()) 104 | 105 | # --- SyncEngineDatabase.db control --- 106 | if sed_filled: 107 | # Enable SafeDelete.db and NTUSER.DAT 108 | for key in ["SafeDelete.db", "NTUSER.DAT"]: 109 | for widget in self.row_widgets[key]: 110 | widget.configure(state="normal") 111 | else: 112 | # Clear and disable SafeDelete.db, NTUSER.DAT, $Recycle.Bin 113 | for key in ["SafeDelete.db", "NTUSER.DAT", "$Recycle.Bin"]: 114 | self.file_vars[key].set("") 115 | for widget in self.row_widgets[key]: 116 | widget.configure(state="disabled") 117 | return # Nothing else to check if SED is empty 118 | 119 | # --- NTUSER.DAT control --- 120 | if ntuser_filled: 121 | # Enable $Recycle.Bin 122 | for widget in self.row_widgets["$Recycle.Bin"]: 123 | widget.configure(state="normal") 124 | else: 125 | # Clear and disable $Recycle.Bin 126 | self.file_vars["$Recycle.Bin"].set("") 127 | for widget in self.row_widgets["$Recycle.Bin"]: 128 | widget.configure(state="disabled") 129 | 130 | except Exception: 131 | pass 132 | 133 | def browse_file(self, ext): 134 | if ext == '$Recycle.Bin': 135 | path = filedialog.askdirectory(initialdir="/", title="Open") 136 | else: 137 | path = filedialog.askopenfilename(filetypes=[(f"{ext.upper()} files", f"*{ext}"), ("All files", "*.*")]) 138 | if path: 139 | self.file_vars[ext].set(path) 140 | 141 | def get_selected_paths(self): 142 | return {ext: var.get() for ext, var in self.file_vars.items()} 143 | 144 | def on_submit(self): 145 | if self.callback: 146 | self.callback(self.get_selected_paths()) 147 | self.cleanup() 148 | 149 | def on_cancel(self): 150 | self.cleanup() 151 | 152 | def cleanup(self): 153 | self.root.unbind('') 154 | self.win.destroy() 155 | 156 | def sync_windows(self, event=None): 157 | try: 158 | x = self.root.winfo_x() 159 | y = self.root.winfo_y() 160 | w = self.root.winfo_width() 161 | h = self.root.winfo_height() 162 | qw = self.win.winfo_width() 163 | qh = self.win.winfo_height() 164 | self.win.geometry("+%d+%d" % (x + w / 2 - qw / 2, y + h / 2 - qh / 2)) 165 | except Exception: 166 | pass 167 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/parsers/Nucleus/fileusagesync.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import ast 26 | import logging 27 | import sqlite3 28 | import pandas as pd 29 | import json 30 | import re 31 | 32 | 33 | class SQLiteTableExporter: 34 | def __init__(self, db_path=None): 35 | self.log = logging.getLogger(__name__) 36 | self.db_path = db_path 37 | self.conn = None 38 | self.json_data = [] 39 | self.df_data = pd.DataFrame() 40 | self.tc_data = pd.DataFrame() 41 | self.qa_data = pd.DataFrame() 42 | self.rf_data = pd.DataFrame() 43 | 44 | def set_db_path(self, db_path): 45 | self.db_path = db_path 46 | 47 | def parse_json(self, value): 48 | # needs work 49 | try: 50 | # First, decode the double-escaped string 51 | value = value.encode().decode('unicode_escape') 52 | #if isinstance(value, str) and re.search(r'\\\\[ntr"\\]', value): 53 | # value = value.encode().decode('unicode_escape') 54 | 55 | # Now, parse the cleaned JSON 56 | return json.loads(value) 57 | except Exception as e: 58 | self.log.error("JSON Parse Error:", e) 59 | return None 60 | 61 | def nest_dict(self, flat_dict): 62 | nested = {} 63 | for flat_key, value in flat_dict.items(): 64 | keys = flat_key.split('.') 65 | d = nested 66 | for key in keys[:-1]: 67 | d = d.setdefault(key, {}) 68 | d[keys[-1]] = value 69 | return nested 70 | 71 | def load_data(self, saved_data): 72 | self.json_data = saved_data 73 | self.df_data = pd.json_normalize(saved_data) 74 | 75 | def load_csv(self, saved_data): 76 | self.df_data = pd.read_csv(saved_data) 77 | self.df_data['file.AllExtensions.SharingHistory.Instances'] = self.df_data['file.AllExtensions.SharingHistory.Instances'].apply(lambda x: ast.literal_eval(x) if pd.notna(x) else x) 78 | self.json_data = [self.nest_dict(row.dropna().to_dict()) for _, row in self.df_data.iterrows()] 79 | 80 | def get_recent_files_formatted_spo(self): 81 | if not self.db_path: 82 | self.log.error("Database path not set. Use set_db_path() first.") 83 | return 84 | 85 | try: 86 | self.conn = sqlite3.connect(f'file:/{self.db_path}/Microsoft.FileUsageSync.db?mode=ro', uri=True) 87 | query = "SELECT FormattedValue FROM recent_files_formatted_spo" 88 | try: 89 | df = pd.read_sql_query(query, self.conn) 90 | self.conn.close() 91 | parsed_jsons = df["FormattedValue"].apply(self.parse_json).dropna().tolist() 92 | self.json_data = parsed_jsons 93 | self.df_data = pd.json_normalize(parsed_jsons) 94 | except Exception as e: 95 | self.log.warning(f'Unable to parse recent_files_formatted_spo in {self.db_path}/Microsoft.FileUsageSync.db. {e}') 96 | except sqlite3.OperationalError: 97 | self.log.info('Microsoft.FileUsageSync.db does not exist') 98 | 99 | def get_top_collaborators(self): 100 | if not self.db_path: 101 | self.log.error("Database path not set. Use set_db_path() first.") 102 | return 103 | 104 | try: 105 | self.conn = sqlite3.connect(f'file:/{self.db_path}/Microsoft.FileUsageSync.db?mode=ro', uri=True) 106 | query = "SELECT FormattedValue FROM top_collaborators" 107 | try: 108 | df = pd.read_sql_query(query, self.conn) 109 | self.conn.close() 110 | parsed_jsons = df["FormattedValue"].apply(self.parse_json).dropna().tolist() 111 | self.json_data = parsed_jsons 112 | self.tc_data = pd.json_normalize(parsed_jsons) 113 | except Exception as e: 114 | self.log.warning(f'Unable to parse top_collaborators in {self.db_path}/Microsoft.FileUsageSync.db. {e}') 115 | except sqlite3.OperationalError: 116 | self.log.info('Microsoft.FileUsageSync.db does not exist') 117 | 118 | def get_quick_access_formatted(self): 119 | if not self.db_path: 120 | self.log.error("Database path not set. Use set_db_path() first.") 121 | return 122 | 123 | try: 124 | self.conn = sqlite3.connect(f'file:/{self.db_path}/Microsoft.FileUsageSync.db?mode=ro', uri=True) 125 | query = "SELECT FormattedValue FROM quick_access_formatted" 126 | try: 127 | df = pd.read_sql_query(query, self.conn) 128 | self.conn.close() 129 | parsed_jsons = df["FormattedValue"].apply(self.parse_json).dropna().tolist() 130 | self.json_data = parsed_jsons 131 | self.qa_data = pd.json_normalize(parsed_jsons) 132 | except Exception as e: 133 | self.log.warning(f'Unable to parse quick_access_formatted in {self.db_path}/Microsoft.FileUsageSync.db. {e}') 134 | except sqlite3.OperationalError: 135 | self.log.info('Microsoft.FileUsageSync.db does not exist') 136 | 137 | def get_recommended_files(self): 138 | if not self.db_path: 139 | self.log.error("Database path not set. Use set_db_path() first.") 140 | return 141 | 142 | try: 143 | self.conn = sqlite3.connect(f'file:/{self.db_path}/Microsoft.FileUsageSync.db?mode=ro', uri=True) 144 | query = "SELECT file FROM recommended_files" 145 | try: 146 | df = pd.read_sql_query(query, self.conn) 147 | self.conn.close() 148 | parsed_jsons = df["file"].apply(self.parse_json).dropna().tolist() 149 | self.json_data = parsed_jsons 150 | self.rf_data = pd.json_normalize(parsed_jsons) 151 | except Exception as e: 152 | self.log.warning(f'Unable to parse recommended_files in {self.db_path}/Microsoft.FileUsageSync.db. {e}') 153 | except sqlite3.OperationalError: 154 | self.log.info('Microsoft.FileUsageSync.db does not exist') 155 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 🌐 Language 5 |
6 |
7 | English 8 | | 简体中文 9 | | 繁體中文 10 | | 日本語 11 | | 한국어 12 | | हिन्दी 13 | | ไทย 14 | | Français 15 | | Deutsch 16 | | Español 17 | | Italiano 18 | | Русский 19 | | Português 20 | | Nederlands 21 | | Polski 22 | | العربية 23 | | فارسی 24 | | Türkçe 25 | | Tiếng Việt 26 | | Bahasa Indonesia 27 | | অসমীয়া 29 |
30 |
31 |
32 | 33 | # OneDriveExplorer Summary 34 | 35 | OneDriveExplorer is a command line and GUI based application for reconstructing the folder structure of OneDrive from `.dat`, `.dat.previous` and/or SQLite databases. Also supports parsing OneDrive logs with extensions `.odl`, `.odlgz`, `.odlsent` and `.aold`. Log parsing is heavily based on the work of Yogesh Khatri [odl.py](https://github.com/ydkhatri/OneDrive). 36 | 37 | ## Usage 38 | 39 | Settings files are found in the following locations: 40 | **Windows** 41 | * \AppData\Local\Microsoft\OneDrive\settings\Business<1-9> 42 | * \AppData\Local\Microsoft\OneDrive\settings\Personal 43 | 44 | **macOS** 45 | * /Users/\/Library/Application Support/Onedrive 46 | 47 | Log files are found in the following locations: 48 | **Windows** 49 | * \AppData\Local\Microsoft\OneDrive\logs\Business<1-9> 50 | * \AppData\Local\Microsoft\OneDrive\logs\Personal 51 | 52 | **macOS** 53 | * /Users/\/Library/Logs/OneDrive 54 | 55 | Log files will be unobfuscated if the `ObfuscationStringMap.txt` and\or [`general.keystore`, `vault.keystore`] file(s) are present. 56 | 57 | ### Requirements 58 | 59 | This project requires several additional modules. You can install them with the provided requirements.txt file as follows: 60 | 61 | ```bash 62 | python3 -m pip install -r requirements.txt 63 | ``` 64 | 65 | ***Note:** pytsk3 and quickxorhash require a compiler to install. Using the executables is recommended. 66 | 67 | ### Command line 68 | 69 | ![cmd_help](./Images/cmd_help.png) 70 | 71 | To use OneDriveExplorer, simply provide the `.\.dat` file to the `-f` argument 72 | 73 | ```bash 74 | OneDriveExplorer.exe -f \AppData\Local\Microsoft\OneDrive\settings\Personal/Business<1-9>\d1a7c039-6175-4ddb-bcdb-a8de45cf1678.dat 75 | ``` 76 | 77 | Newer versions of OneDrive have switched to SQLite. Use the -s switch instead. 78 | 79 | ```bash 80 | OneDriveExplorer.exe -s \AppData\Local\Microsoft\OneDrive\settings\Personal/Business<1-9> 81 | ``` 82 | 83 | Depending on the options, OneDriveExplorer can produce JSON, CSV, or HTML files of the parsed data. The `--pretty` option can be used to output the JSON into a more human readable layout. 84 | 85 | A user registry hive can be supplied with the `-r` argument. This will resolve some of the mount points associated with OneDrive. Along with the registry hive, $Recycle.Bin can be added with the `-rb` option to look for deleted files. 86 | 87 | ### Example output 88 | 89 | #### JSON 90 | 91 | ![json](./Images/json.png) 92 | 93 | #### CVS 94 | 95 | ![csv](./Images/csv.png) 96 | 97 | #### HTML 98 | 99 | ![html](./Images/html.png) 100 | 101 | ## GUI 102 | 103 | The GUI consists of three panes: the folder structure on the left, file pane in the middle, and details on the right. By clicking on one of the entries in the left pane or file pane, the details pane will populate with various data such as name, whether it is a file or folder, UUIDs and the number of children, if any. There is also a pane on the bottom that correlates log data back to the selected item. 104 | 105 | ![gui](./Images/gui.png) 106 | 107 | The GUI is capable of parsing the live system and dat/SQLite files, along with loading JSON or CSV from a previously parsed dat/SQLite file. OneDriveExplorer GUI also supports loading multiple files. When loading a dat/SQLite file, an additional dialog will appear to allow you to supply a registry file. This can be disabled by holding down `SHIFT` or disabling it in the preferences menu. 108 | 109 | *OneDrive ODL logs can be enabled in the Preferences menu. 110 | 111 | ![file_menu](./Images/file_menu.png) ![hive](./Images/hive.png) 112 | 113 | Through the preferences menu, there are options available for saving the parsed dat/SQLite file to JSON, CSV, and HTML. There is also an option to disable the hive dialogue. 114 | 115 | ![preferences](./Images/preference.png) 116 | 117 | OneDriveExplorer GUI is also capable of performing a simple search. 118 | 119 | ![search](./Images/search.png) 120 | 121 | There are right click menus to help perform various tasks and skin options. 122 | 123 | ![right_click1](./Images/rc_menu1.png) 124 | ![right_click2](./Images/rc_menu2.png) 125 | 126 | ![skins](./Images/skins.png) 127 | 128 | The messages dialogue can be accessed by double clicking on the number in the lower right corner. From this dialogue, you can view, clear and export debug messages. 129 | 130 | ![indicator](./Images/message_indicator.png) 131 | ![messages](./Images/message.png) 132 | 133 | Projects can also be created to save your work without having to parse the files again. This has the advantage of loading the data more quickly. 134 | 135 | ![project menu](./Images/project.png) 136 | 137 | If ODL log files are enabled, additional tabs for each users logs will be created. 138 | 139 | ![ODL logs](./Images/odl.png) 140 | 141 | ## File location 142 | 143 | The default file location of the `.dat` files are: 144 | 145 | - Personal: `C:\Users\\AppData\Local\Microsoft\OneDrive\settings\Personal\` 146 | - Business: `C:\Users\\AppData\Local\Microsoft\OneDrive\settings\Business<1-9>\` 147 | 148 | ## Todo 149 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/renderers/project.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import ast 26 | import csv 27 | import os 28 | import re 29 | import zipfile 30 | from io import StringIO 31 | from PIL import ImageTk, Image 32 | import pandas as pd 33 | import logging 34 | from ode.utils import progress_gui 35 | 36 | log = logging.getLogger(__name__) 37 | 38 | 39 | def load_images(zip_name): 40 | s_image = {} 41 | try: 42 | with zipfile.ZipFile(zip_name, 'r') as archive: 43 | filenames = archive.namelist() 44 | filtered_list = [item for item in filenames if item.startswith('Images/')] 45 | sorted_list = sorted(filtered_list, key=lambda x: int(re.search(r'/(\d+)_', x).group(1))) 46 | 47 | for img in sorted_list: 48 | with archive.open(img) as data: 49 | digest = str(img).split('_')[1].split('.png')[0] 50 | image = ImageTk.PhotoImage(Image.open(data)) 51 | s_image[digest] = image 52 | 53 | return s_image 54 | 55 | except Exception as e: 56 | log.error(f'Error loading images from {zip_name.split("/")[-1]}. {e}') 57 | 58 | 59 | def load_project(zip_name, q, stop_event, tv, file_items, fus, pb, value_label): 60 | try: 61 | with zipfile.ZipFile(zip_name, 'r') as archive: 62 | filenames = archive.namelist() 63 | pb.configure(mode='determinate') 64 | pb.start() 65 | filenames = [item for item in filenames if not item.startswith('Images/')] 66 | 67 | for filename in filenames: 68 | with archive.open(filename) as data: 69 | arc_name = archive.filename.split('/')[-1] 70 | log.info(f'Importing {filename} from {arc_name} project.') 71 | detach_items = ['fileStatus', 'inRecycleBin'] 72 | 73 | if '_OneDrive.csv' in filename: 74 | value_label['text'] = "Gathering metadata. Pleas wait..." 75 | count = 0 76 | send_data = [] 77 | df = pd.read_csv(data, low_memory=False, quotechar='"') 78 | df.fillna('', inplace=True) 79 | total = len(df) 80 | 81 | for row in df.to_dict('records'): 82 | count += 1 83 | try: 84 | tags = ast.literal_eval(row['tags']) 85 | except Exception: 86 | tags = row['tags'] 87 | try: 88 | tv.insert(row['parent'], row['index'], iid=row['iid'], text=row['text'], image=ast.literal_eval(row['image'])[0], values=tuple(ast.literal_eval(row['values'])), open=row['open'], tags=tags) 89 | except Exception as e: 90 | log.error(f'Unable to load {zip_name}. The following error has occured: {e}') 91 | q.put(['done']) 92 | return 93 | if any(any(detach_item in sub for sub in tv.item(row['iid'])["values"]) for detach_item in detach_items): 94 | file_items[row['parent']].append(row['iid']) 95 | tv.detach(row['iid']) 96 | if count % 20 == 0: 97 | progress_gui(total, count, pb, value_label, status=f'Importing {filename} from {arc_name} project.') 98 | 99 | if '_FileUsageSync.csv' in filename: 100 | fus.load_csv(data) 101 | 102 | if '_logs.csv' in filename: 103 | value_label['text'] = f'Importing {filename} from {arc_name} project.' 104 | pb.configure(mode='indeterminate') 105 | pb.start() 106 | send_data = [] 107 | df = pd.read_csv(data, dtype=str) 108 | send_data.append(filename) 109 | send_data.append(df) 110 | q.put(send_data) 111 | 112 | except Exception as e: 113 | log.error(f'Error importing {zip_name.split("/")[-1]}. {e}') 114 | 115 | q.put(['done']) 116 | 117 | 118 | def save_project(tv, file_items, zip_name, user_logs, s_image, fus, pb, value_label): 119 | def find_children(count, item=''): 120 | children = tv.get_children(item) 121 | 122 | for child in children: 123 | count += 1 124 | row = [tv.parent(child), 'end', child] 125 | row.extend(list(tv.item(child).values())) 126 | csvwriter.writerow(row) 127 | 128 | if child in file_items: 129 | for i in file_items[child]: 130 | count += 1 131 | row = [child, 'end', i] 132 | row.extend(list(tv.item(i).values())) 133 | csvwriter.writerow(row) 134 | progress_gui(total, count, pb, value_label, status=f'Saving {filename} to {zip_name.split("/")[-1]}.') 135 | 136 | progress_gui(total, count, pb, value_label, status=f'Saving {filename} to {zip_name.split("/")[-1]}.') 137 | count = find_children(count, item=child) 138 | 139 | return count 140 | 141 | def get_total_items(tree, item): 142 | # Get the direct children of the current item 143 | children = tree.get_children(item) 144 | 145 | # Initialize the count with the direct children count 146 | count = len(children) 147 | 148 | # Recursively count items for each child 149 | for child in children: 150 | count += get_total_items(tree, child) 151 | 152 | return count 153 | 154 | total = get_total_items(tv, "") 155 | total += sum(len(value) for value in file_items.values()) 156 | count = 0 157 | 158 | pb.configure(mode='determinate') 159 | pb.start() 160 | 161 | try: 162 | with zipfile.ZipFile(zip_name, 'w') as archive: 163 | file_count = 1 164 | string_buffer = StringIO() 165 | d = tv.get_children() 166 | 167 | for index, (k, v) in enumerate(s_image.items()): 168 | tmp_file = 'tmp.png' 169 | v._PhotoImage__photo.write(tmp_file) 170 | with open(tmp_file, 'rb') as image_file: 171 | archive.writestr(f'Images/{index}_{k}.png', image_file.read()) 172 | os.remove(tmp_file) 173 | 174 | for i in d: 175 | filename = f"{tv.item(i)['text'].split('.')[0][1:]}_OneDrive.csv" 176 | 177 | if filename in archive.namelist(): 178 | filename = f"{tv.item(i)['text'].split('.')[0][1:]}({file_count})_OneDrive.csv" 179 | file_count += 1 180 | 181 | log.info(f'Saving {filename} to {zip_name}.') 182 | csvwriter = csv.writer(string_buffer) 183 | csvwriter.writerow(['parent', 'index', 'iid', 'text', 'image', 'values', 'open', 'tags', 'meta']) 184 | row = [tv.parent(i), 'end', i] 185 | row.extend(list(tv.item(i).values())) 186 | csvwriter.writerow(row) 187 | count = find_children(count, item=i) 188 | archive.writestr(filename, string_buffer.getvalue()) 189 | string_buffer.truncate(0) 190 | string_buffer.seek(0) 191 | 192 | pb.configure(mode='indeterminate') 193 | pb.start() 194 | 195 | if not fus.empty: 196 | fus.to_csv(string_buffer, index=False, encoding='utf-8') 197 | filename = '_FileUsageSync.csv' 198 | archive.writestr(filename, string_buffer.getvalue()) 199 | string_buffer.truncate(0) 200 | string_buffer.seek(0) 201 | 202 | for key, value in user_logs.items(): 203 | value_label['text'] = f"Saving {key} to {zip_name}. Please wait...." 204 | log.info(f'Saving {key} to {zip_name}.') 205 | df = value.model.df 206 | df.to_csv(string_buffer, index=False) 207 | 208 | if key in archive.namelist(): 209 | new_key = key.rsplit('_', 1) 210 | key = f'{new_key[0]}({file_count})_{new_key[1]}' 211 | file_count += 1 212 | 213 | archive.writestr(key, string_buffer.getvalue()) 214 | string_buffer.truncate(0) 215 | string_buffer.seek(0) 216 | 217 | except Exception as e: 218 | log.error(f'Error saving {zip_name}: {e}') 219 | pb.stop() 220 | value_label['text'] = 'Error saving project.' 221 | pb.configure(mode='determinate') 222 | return 223 | 224 | pb.stop() 225 | value_label['text'] = "Project saved successfuly." 226 | pb.configure(mode='determinate') 227 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/parsers/recbin.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import os 26 | import struct 27 | import hashlib 28 | import quickxorhash 29 | import base64 30 | from datetime import datetime, timedelta 31 | import logging 32 | from ode.utils import progress, progress_gui 33 | 34 | log = logging.getLogger(__name__) 35 | 36 | 37 | class DeleteProcessor: 38 | def __init__(self): 39 | pass 40 | 41 | @staticmethod 42 | def from_unix_sec(_): 43 | try: 44 | return datetime.utcfromtimestamp(int(_)).strftime('%Y-%m-%d %H:%M:%S') 45 | except Exception as e: 46 | log.error(e) 47 | return datetime.utcfromtimestamp(0).strftime('%Y-%m-%d %H:%M:%S') 48 | 49 | @staticmethod 50 | def hash_file(file_path): 51 | buf_size = 65536 52 | sha1 = hashlib.sha1() 53 | quick_xor_hash = quickxorhash.quickxorhash() 54 | 55 | try: 56 | with open(file_path, 'rb') as file: 57 | while True: 58 | data = file.read(buf_size) 59 | if not data: 60 | break 61 | sha1.update(data) 62 | quick_xor_hash.update(data) 63 | 64 | sha1_digest = sha1.hexdigest() 65 | quick_xor_digest = base64.b64encode(quick_xor_hash.digest()).decode("utf-8") 66 | 67 | return [f'SHA1({sha1_digest})', f'quickXor({quick_xor_digest})', '2'] 68 | 69 | except PermissionError: 70 | return ['', '', '2'] 71 | 72 | except Exception: 73 | return ['', '', ''] 74 | 75 | def if_exists(self, path, name, delete_time_stamp, rbin_df): 76 | test_date = datetime.strptime(self.from_unix_sec(delete_time_stamp), '%Y-%m-%d %H:%M:%S') 77 | 78 | for index, row in rbin_df.iterrows(): 79 | try: 80 | test_path = row.get('Path', '').split('\\', 1)[1] 81 | except IndexError: 82 | test_path = row.get('Name', '') 83 | 84 | if path.endswith(test_path) and row.get('Name') == name: 85 | delete_timestamp = datetime.strptime(row.get('notificationTime'), '%Y-%m-%d %H:%M:%S') 86 | if delete_timestamp - timedelta(seconds=1) <= test_date <= delete_timestamp + timedelta(seconds=1): 87 | return index 88 | 89 | if test_path.endswith(name): 90 | delete_timestamp = datetime.strptime(row.get('notificationTime'), '%Y-%m-%d %H:%M:%S') 91 | if delete_timestamp - timedelta(seconds=1) <= test_date <= delete_timestamp + timedelta(seconds=1): 92 | return index 93 | 94 | return -1 95 | 96 | def get_file_metadata(self, i_name, files, od_keys, localHashAlgorithm, rbin_df): 97 | with open(i_name, "rb") as file: 98 | file_record = file.read() 99 | 100 | file_header = int(str(struct.unpack_from('= 0: 123 | rbin_df.at[index, 'inRecycleBin'] = get_hash[2] 124 | rbin_df.at[index, 'DeleteTimeStamp'] = self.from_unix_sec(delete_time_stamp) 125 | rbin_df.at[index, 'size'] = file_size 126 | rbin_df.at[index, 'hash'] = hash_func 127 | parentResourceId = rbin_df.at[index, 'resourceId'] 128 | else: 129 | input_data = { 130 | 'Type': 'Deleted', 131 | 'parentResourceId': '', 132 | 'resourceId': '', 133 | 'eTag': '', 134 | 'Path': f'{path}', 135 | 'Name': name, 136 | 'inRecycleBin': get_hash[2], 137 | 'volumeId': '', 138 | 'fileId': '', 139 | 'DeleteTimeStamp': self.from_unix_sec(delete_time_stamp), 140 | 'notificationTime': '', 141 | 'size': file_size, 142 | 'hash': hash_func, 143 | 'deletingProcess': '' 144 | } 145 | yield input_data 146 | 147 | if files: 148 | for file in files: 149 | full_file_path = f'{i_name.replace("$I", "$R")}{file}' 150 | get_hash = self.hash_file(full_file_path) 151 | hash_func = get_hash[1] if localHashAlgorithm == 5 else get_hash[0] 152 | file_stat = os.stat(full_file_path) 153 | name = file.split('\\')[-1] 154 | path = file.rsplit('\\', 1)[0] 155 | index = self.if_exists(f'{file_name}{path}', name, delete_time_stamp, rbin_df) 156 | 157 | if index >= 0: 158 | rbin_df.at[index, 'inRecycleBin'] = get_hash[2] 159 | rbin_df.at[index, 'DeleteTimeStamp'] = self.from_unix_sec(delete_time_stamp) 160 | rbin_df.at[index, 'size'] = f'{file_stat.st_size // 1024 + 1} KB' 161 | rbin_df.at[index, 'hash'] = hash_func 162 | 163 | else: 164 | input_data = { 165 | 'Type': 'Deleted', 166 | 'parentResourceId': parentResourceId, 167 | 'resourceId': '', 168 | 'eTag': '', 169 | 'Path': f'{file_name}{path}', 170 | 'Name': name, 171 | 'inRecycleBin': get_hash[2], 172 | 'volumeId': '', 173 | 'fileId': '', 174 | 'DeleteTimeStamp': self.from_unix_sec(delete_time_stamp), 175 | 'notificationTime': '', 176 | 'size': f'{file_stat.st_size // 1024 + 1} KB', 177 | 'hash': hash_func, 178 | 'deletingProcess': '' 179 | } 180 | yield input_data 181 | 182 | def find_deleted(self, recbin, od_keys, localHashAlgorithm, rbin_df, gui=False, pb=False, value_label=False): 183 | recbin = recbin.replace('/', '\\') 184 | log.info(f'Started parsing {recbin}') 185 | 186 | file_info = {} 187 | 188 | for path, subdirs, files in os.walk(recbin): 189 | for name in files: 190 | if "$I" in name: 191 | file_info.setdefault(name, {'iname': os.path.join(path, name), 'files': [], 'folders': []}) 192 | 193 | for x in file_info.keys(): 194 | if x[2:] in path: 195 | for name in files: 196 | file_info[x]['files'].append(os.path.join(path, name).split(x[2:])[-1]) 197 | 198 | total = len(file_info) 199 | count = 0 200 | 201 | if gui: 202 | pb['value'] = 0 203 | 204 | deleted_items = [] 205 | 206 | for key, value in file_info.items(): 207 | iname = value.get('iname', '') 208 | filenames = value.get('files', []) 209 | 210 | match = self.get_file_metadata(iname, filenames, od_keys, localHashAlgorithm, rbin_df) 211 | if match: 212 | deleted_items.extend(match) 213 | 214 | if gui: 215 | progress_gui(total, count, pb, value_label, status='Adding deleted items. Please wait....') 216 | else: 217 | progress(count, total, status='Adding deleted items. Please wait....') 218 | count += 1 219 | 220 | log.info(f'Parsing complete {recbin}') 221 | return deleted_items 222 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/parsers/csv_file.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import json 26 | import pandas as pd 27 | import logging 28 | 29 | log = logging.getLogger(__name__) 30 | 31 | 32 | class ParseSettingResult: 33 | def __init__(self, df, rbin_df, df_scope, graphMetadata, scopeID, account, localHashAlgorithm, offline_db, comment): 34 | self.df = df 35 | self.rbin_df = rbin_df 36 | self.df_scope = df_scope 37 | self.graphMetadata = graphMetadata 38 | self.scopeID = scopeID 39 | self.account = account 40 | self.localHashAlgorithm = localHashAlgorithm 41 | self.offline_db = offline_db 42 | self.comment = comment 43 | 44 | def __repr__(self): 45 | """Custom string representation for debugging.""" 46 | return f"ParseSettingResult(df={len(self.df)} rows, rbin_df={len(self.rbin_df)} rows, scopeID={len(self.scopeID)})" 47 | 48 | 49 | class ParseOfflineResult: 50 | def __init__(self, ocr_db, df_list_sync, scopeID, account): 51 | self.ocr_db = ocr_db 52 | self.df_list_sync = df_list_sync 53 | self.scopeID = scopeID 54 | self.account = account 55 | 56 | def __repr__(self): 57 | """Custom string representation for debugging.""" 58 | return f"ParseOfflineResult(ocr_db={len(self.ocr_db)} rows, df_list_sync={len(self.df_list_sync)} rows, scopeID={len(self.scopeID)})" 59 | 60 | 61 | class ParseFODResult: 62 | def __init__(self, df, df_scope, scopeID, account): 63 | self.df = df 64 | self.df_scope = df_scope 65 | self.scopeID = scopeID 66 | self.account = account 67 | 68 | def __repr__(self): 69 | """Custom string representation for debugging.""" 70 | return f"ParseFODResult(df={len(self.df)} rows, scopeID={len(self.scopeID)})" 71 | 72 | 73 | def parse_csv(filename): 74 | file = open(filename, 'r', encoding='utf-8') 75 | columns_to_drop = ['parentResourceId', 'resourceId', 'inRecycleBin', 'volumeId', 'fileId', 76 | 'DeleteTimeStamp', 'notificationTime', 'hash', 'deletingProcess'] 77 | columns_to_drop_2 = ['MountPoint'] 78 | 79 | dtypes = {'Type': 'str', 'scopeID': 'str', 'siteID': 'str', 'webID': 'str', 80 | 'listID': 'str', 'tenantID': 'str', 'webURL': 'str', 'remotePath': 'str', 81 | 'libraryType': 'str', 'resourceID': 'str', 'parentResourceID': 'str', 82 | 'eTag': 'str', 'volumeID': 'str', 'itemIndex': 'Int64', 'localHashDigest': 'object', 83 | 'localHashAlgorithm': 'Int64', 'lastChange': 'object', 'size': 'object', 'Name': 'str', 84 | 'fileStatus': 'Int64', 'spoPermissions': 'object', 'sharedItem': 'Int64', 'SharedItem': 'Int64', 'Media': 'object', 85 | 'parentScopeID': 'str', 'folderStatus': 'Int64', 'Path': 'str', 86 | 'shortcutVolumeID': 'str', 'shortcutItemIndex': 'Int64', 'hydrationCount': 'Int64', 87 | 'folderColor': 'Int64', 'ListSync': 'str', 'Metadata': 'str', 'lastHydrationType': 'str', 'Created': 'str'} 88 | 89 | fill_values = {'Type': '', 'scopeID': '', 'siteID': '', 'webID': '', 90 | 'listID': '', 'tenantID': '', 'webURL': '', 'remotePath': '', 91 | 'libraryType': '', 'resourceID': '', 'parentResourceID': '', 92 | 'eTag': '', 'volumeID': '', 'itemIndex': 0, 'localHashDigest': '', 93 | 'localHashAlgorithm': 0, 'lastChange': '', 'size': '', 'Name': '', 94 | 'fileStatus': 0, 'spoPermissions': '', 'sharedItem': 0, 'SharedItem': 0, 'Media': '', 95 | 'parentScopeID': '', 'folderStatus': 0, 'Path': '', 96 | 'shortcutVolumeID': '', 'shortcutItemIndex': 0, 'hydrationCount': 0, 97 | 'folderColor': 0, 'ListSync': '', 'Metadata': '', 'lastHydrationType': '', 'Created': ''} 98 | 99 | try: 100 | csv_name = filename.name.replace('/', '\\') 101 | except AttributeError: 102 | csv_name = '' 103 | 104 | log.info(f'Started parsing {csv_name}') 105 | comment = file.readline() 106 | cleaned_str = comment.lstrip("#") 107 | data_dict = json.loads(cleaned_str) 108 | 109 | try: 110 | df = pd.read_csv(file, low_memory=False, quotechar='"', dtype=dtypes) 111 | df = df.fillna(value=fill_values) 112 | 113 | try: 114 | if data_dict["Name"] == "Microsoft.ListSync.db": 115 | df_scope = df.loc[df['Type'] == 'Scope', 116 | ['Type', 'scopeID', 'siteID', 'webID', 'listID', 'tenantID', 117 | 'webURL', 'libraryType']] 118 | df.drop( 119 | df[(df['Type'] == 'Scope') & (df['scopeID'].isna() | (df['scopeID'] == ''))].index, 120 | inplace=True 121 | ) 122 | 123 | elif data_dict["Name"] == "Microsoft.FilesOnDemand.db": 124 | df_scope = df.loc[df['Type'] == 'Scope', 125 | ['Type', 'mountId', 'siteID', 'webID', 'listID', 'webURL', 126 | 'MountPoint', 'libraryType']] 127 | else: 128 | df_scope = df.loc[df['Type'] == 'Scope', 129 | ['Type', 'scopeID', 'siteID', 'webID', 'listID', 'tenantID', 130 | 'webURL', 'remotePath', 'spoPermissions', 'shortcutVolumeID', 131 | 'shortcutItemIndex', 'libraryType']] 132 | columns_to_fill = df_scope.columns.difference(['libraryType']) 133 | df_scope[columns_to_fill] = df_scope[columns_to_fill].fillna('') 134 | df_scope['remotePath'] = df_scope['remotePath'].fillna('') 135 | 136 | scope_col = 'scopeID' if 'scopeID' in df_scope.columns else 'mountId' 137 | 138 | scopeID = df_scope[scope_col].tolist() 139 | except Exception as e: 140 | log.error(f'Something went wrong while reading csv: {csv_name} - Error: {e}') 141 | df_scope = pd.DataFrame() 142 | scopeID = [] 143 | 144 | if 'inRecycleBin' in df.columns: 145 | df = df.astype({'fileId': 'str', 'inRecycleBin': 'Int64'}) 146 | rbin_df = df[df['Type'] == 'Deleted'][['Type', 'parentResourceId', 'resourceId', 'eTag', 147 | 'Path', 'Name', 'inRecycleBin', 'volumeId', 'fileId', 148 | 'DeleteTimeStamp', 'notificationTime', 'size', 'hash', 149 | 'deletingProcess']] 150 | rbin_df = rbin_df.astype(object).where(pd.notna(rbin_df), '') 151 | df = df.drop(df[df['Type'] == 'Deleted'].index) 152 | df.drop(columns=columns_to_drop, inplace=True) 153 | else: 154 | rbin_df = pd.DataFrame() 155 | 156 | try: 157 | if data_dict["Name"] != "Microsoft.FilesOnDemand.db": 158 | df.drop(columns=columns_to_drop_2, inplace=True) 159 | except Exception: 160 | pass 161 | df['Path'] = df['Path'].astype(str).fillna('') 162 | df['Name'] = df['Name'].astype(str).fillna('') 163 | try: 164 | df.localHashDigest.fillna('', inplace=True) 165 | except Exception: 166 | pass 167 | 168 | for col in ['notificationTime', 'firstHydrationTime', 'lastHydrationTime', 169 | 'diskLastAccessTime', 'diskCreationTime']: 170 | if col in df: 171 | df[col].fillna('', inplace=True) 172 | 173 | if 'lastKnownPinState' in df: 174 | df.lastKnownPinState = df.lastKnownPinState.apply(lambda x: '' if pd.isna(x) else int(x)) 175 | 176 | if 'remotePath' in df: 177 | df.remotePath.fillna('', inplace=True) 178 | 179 | except Exception as e: 180 | print(e) 181 | log.error(f'Not a valid csv: {csv_name} - Error: {e}') 182 | return ParseSettingResult(pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), 183 | pd.DataFrame(), [], '', 0, pd.DataFrame(), data_dict), False, False 184 | 185 | if data_dict["Name"] == "Microsoft.ListSync.db": 186 | ocr_db = df[['resourceID', 'ListSync']] 187 | ocr_db = ocr_db[ 188 | ocr_db['ListSync'].notna() & (ocr_db['ListSync'] != '') 189 | ] 190 | 191 | return False, ParseOfflineResult( 192 | ocr_db, 193 | df, 194 | scopeID, 195 | data_dict["Account"] 196 | ), False 197 | 198 | if data_dict["Name"] == "Microsoft.FilesOnDemand.db": 199 | return False, False, ParseFODResult( 200 | df, 201 | df_scope, 202 | scopeID, 203 | data_dict["Account"] 204 | ) 205 | 206 | return ParseSettingResult(df, rbin_df, df_scope, pd.DataFrame(columns=['resourceID', 'Metadata']), 207 | scopeID, '', 0, pd.DataFrame(columns=['resourceID', 'ListSync']), data_dict), False, False 208 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/helpers/pandastablepatch.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2022 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import tkinter as tk 26 | from tkinter import ttk 27 | import tkinter.font as tkFont 28 | import numpy as np 29 | from pandastable import Table 30 | from pandastable.data import TableModel 31 | from pandastable.headers import ColumnHeader 32 | 33 | 34 | root = '' 35 | default_font = '' 36 | application_path = '' 37 | asc_img = '' 38 | desc_img = '' 39 | 40 | 41 | def MypopupMenu(self, event): 42 | df = self.table.model.df 43 | if len(df.columns) == 0: 44 | return 45 | multicols = self.table.multiplecollist 46 | colnames = list(df.columns[multicols])[:4] 47 | colnames = [str(i)[:20] for i in colnames] 48 | if len(colnames) > 2: 49 | colnames = ','.join(colnames[:2])+'+%s others' % str(len(colnames)-2) 50 | else: 51 | colnames = ','.join(colnames) 52 | popupmenu = tk.Menu(self, tearoff=0) 53 | formatmenu = tk.Menu(popupmenu, tearoff=0) 54 | highlightmenu = tk.Menu(formatmenu, tearoff=0) 55 | udmenu = tk.Menu(formatmenu, tearoff=0) 56 | 57 | formatmenu.add_cascade(label="Highlight Cell Rules", menu=highlightmenu) 58 | formatmenu.add_cascade(label="Unique/Duplicate Rules", menu=udmenu) 59 | formatmenu.add_command(label="Manage Rules...") 60 | highlightmenu.add_command(label="Greater Than...") 61 | highlightmenu.add_command(label="Less Than...") 62 | highlightmenu.add_command(label="Between...") 63 | highlightmenu.add_command(label="Equal To...") 64 | highlightmenu.add_command(label="Text That Contains...") 65 | highlightmenu.add_command(label="Custom Condition...") 66 | udmenu.add_command(label="Unique Values...") 67 | udmenu.add_command(label="Duplicate Values...") 68 | 69 | def popupFocusOut(event): 70 | popupmenu.unpost() 71 | popupmenu.add_command(label="Sort by " + colnames, image=asc_img, compound='left', 72 | command=lambda: self.table.sortTable(ascending=[1 for i in multicols])) 73 | popupmenu.add_command(label="Sort by " + colnames, image=desc_img, compound='left', 74 | command=lambda: self.table.sortTable(ascending=[0 for i in multicols])) 75 | # popupmenu.add_separator() 76 | # popupmenu.add_command(label="Group By This Column") 77 | # popupmenu.add_command(label="Hide Group By Box") 78 | # popupmenu.add_separator() 79 | # popupmenu.add_command(label="Hide This Column") 80 | # popupmenu.add_command(label="Column Chooser") 81 | # popupmenu.add_command(label="Best Fit") 82 | # popupmenu.add_command(label="Best Fit (all columns)") 83 | # popupmenu.add_separator() 84 | # popupmenu.add_command(label="Filter Editor...") 85 | # popupmenu.add_command(label="Hide Auto Filter Row") 86 | # popupmenu.add_separator() 87 | # popupmenu.add_cascade(label="Conditional Formatting", menu=formatmenu) 88 | popupmenu.bind("", popupFocusOut) 89 | popupmenu.focus_set() 90 | popupmenu.post(event.x_root, event.y_root) 91 | return popupmenu 92 | 93 | 94 | # disables row header popup 95 | def Myhandle_right_click(self, event): 96 | """respond to a right click""" 97 | return 98 | 99 | 100 | # Custom column header popup 101 | ColumnHeader.popupMenu = MypopupMenu 102 | # disables row header popup 103 | # RowHeader.handle_right_click = Myhandle_right_click 104 | 105 | 106 | class MyTable(Table): 107 | """Custom table class inherits from Table. You can then override required methods""" 108 | def __init__(self, parent=None, model=None, dataframe=None, 109 | width=None, height=None, 110 | rows=20, cols=5, showtoolbar=False, showstatusbar=False, 111 | editable=True, enable_menus=True, 112 | **kwargs): 113 | Table.__init__(self, parent, model, dataframe, 114 | width, height, 115 | rows, cols, showtoolbar, showstatusbar, 116 | editable, enable_menus, 117 | **kwargs) 118 | if dataframe is not None: 119 | self.model = MyTableModel(dataframe=dataframe) 120 | elif model is not None: 121 | self.model = model 122 | else: 123 | self.model = MyTableModel(rows=rows, columns=cols) 124 | return 125 | 126 | # removed Control-f 127 | def doBindings(self): 128 | """Bind keys and mouse clicks, this can be overriden""" 129 | 130 | self.bind("", self.handle_left_click) 131 | self.bind("", self.handle_double_click) 132 | self.bind("", self.handle_left_ctrl_click) 133 | self.bind("", self.handle_left_shift_click) 134 | 135 | self.bind("", self.handle_left_release) 136 | if self.ostyp == 'darwin': 137 | # For mac we bind Shift, left-click to right click 138 | self.bind("", self.handle_right_click) 139 | self.bind('', self.handle_right_click) 140 | else: 141 | self.bind("", self.handle_right_click) 142 | 143 | self.bind('', self.handle_mouse_drag) 144 | # self.bind('', self.handle_motion) 145 | 146 | self.bind("", self.copy) 147 | # self.bind("", self.deleteRow) 148 | # self.bind_all("", self.addRow) 149 | # self.bind("", self.clearData) 150 | self.bind("", self.paste) 151 | self.bind("", self.selectAll) 152 | # self.bind("", self.findText) 153 | 154 | self.bind("", self.handle_arrow_keys) 155 | self.bind("", self.handle_arrow_keys) 156 | self.bind("", self.handle_arrow_keys) 157 | self.bind("", self.handle_arrow_keys) 158 | self.parentframe.master.bind_all("", self.handle_arrow_keys) 159 | self.parentframe.master.bind_all("", self.handle_arrow_keys) 160 | self.parentframe.master.bind_all("", self.handle_arrow_keys) 161 | # if 'windows' in self.platform: 162 | self.bind("", self.mouse_wheel) 163 | self.bind('', self.mouse_wheel) 164 | self.bind('', self.mouse_wheel) 165 | self.focus_set() 166 | return 167 | 168 | def handle_double_click(self, event): 169 | self.root = root 170 | self.data = self.getSelectedDataFrame() 171 | self.cell = self.data.to_string(header=False, index=False, float_format=lambda x: '%.0f' % x) 172 | self.size = tk.StringVar() 173 | 174 | self.win = tk.Toplevel(self.root) 175 | self.win.geometry('800x450') 176 | self.win.minsize(800, 450) 177 | self.win.title("Cell contents") 178 | self.win.iconbitmap(application_path + '/Images/titles/windows_tile.ico') 179 | self.win.grid_rowconfigure(0, weight=1) 180 | self.win.grid_columnconfigure(0, weight=1) 181 | 182 | self.ccFrame = ttk.Frame(self.win, padding=5) 183 | self.ccFrame.grid_rowconfigure(0, weight=1) 184 | self.ccFrame.grid_columnconfigure(0, weight=1) 185 | 186 | self.scrollb = ttk.Scrollbar(self.ccFrame) 187 | self.cc = tk.Text(self.ccFrame, 188 | font=default_font, 189 | undo=False, 190 | yscrollcommand=self.scrollb.set) 191 | 192 | self.font = tkFont.Font(font=self.cc['font']).actual() 193 | self.size.set(self.font['size']) 194 | 195 | self.cc.insert(tk.END, self.cell) 196 | self.cc.config(state='disable') 197 | self.scrollb.config(command=self.cc.yview) 198 | 199 | self.fFrame = ttk.Frame(self.ccFrame) 200 | self.fLabel = ttk.Label(self.fFrame, text='Font size') 201 | self.fsize = tk.Spinbox(self.fFrame, 202 | from_=2, 203 | to=50, 204 | width=4, 205 | bd=0, 206 | state='readonly', 207 | justify=tk.RIGHT, 208 | textvariable=self.size, 209 | command=self.change_size) 210 | 211 | self.ccFrame.grid(row=0, column=0, sticky="nsew") 212 | self.cc.grid(row=0, column=0, sticky="nsew") 213 | self.scrollb.grid(row=0, column=1, sticky="ns") 214 | self.fFrame.grid(row=1, column=0, columnspan=2) 215 | self.fLabel.grid(row=0, column=0, pady=(5, 0), padx=(0, 5)) 216 | self.fsize.grid(row=0, column=1, pady=(5, 0)) 217 | 218 | self.size.trace_add("write", self.change_size) 219 | x = self.root.winfo_x() 220 | qw = self.win.winfo_width() 221 | y = self.root.winfo_y() 222 | qh = self.win.winfo_height() 223 | w = self.root.winfo_width() 224 | h = self.root.winfo_height() 225 | self.win.geometry("+%d+%d" % (x + w/2 - qw/2, y + h/2 - qh/2)) 226 | return 227 | 228 | def change_size(self, *args): 229 | self.cc.config(font=(self.font['family'], self.fsize.get())) 230 | 231 | # disables cell popup 232 | def handle_right_click(self, event): 233 | """respond to a right click""" 234 | return 235 | 236 | 237 | class MyTableModel(TableModel): 238 | """Custom tablemodel class inherits from TableModel. You can then override required methods""" 239 | def __init__(self, dataframe=None, **kwargs): 240 | TableModel.__init__(self, dataframe, **kwargs) 241 | return 242 | 243 | def getlongestEntry(self, colindex, n=500): 244 | """Get the longest string in the column for determining width. Just uses the first 245 | n rows for speed""" 246 | 247 | df = self.df 248 | col = df.columns[colindex] 249 | try: 250 | if df.dtypes[col] == 'float64': 251 | c = df[col].iloc[:n].round(3) 252 | else: 253 | c = df[col].iloc[:n] 254 | except Exception: 255 | return 1 256 | longest = c.astype('object').astype('str').str.len().max() 257 | if np.isnan(longest): 258 | return 1 259 | return longest 260 | -------------------------------------------------------------------------------- /OneDriveExplorer/OneDriveExplorer.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import os 26 | import sys 27 | import argparse 28 | import time 29 | import logging 30 | import uuid 31 | import pandas as pd 32 | import warnings 33 | import threading 34 | import traceback 35 | import queue 36 | from datetime import datetime 37 | import ode.parsers.dat as dat_parser 38 | import ode.parsers.onedrive as onedrive_parser 39 | from ode.parsers.odl import load_cparser 40 | import ode.parsers.sqlite_db as sqlite_parser 41 | from ode.utils import update_from_repo 42 | import ode.parsers.manager as manager 43 | 44 | warnings.filterwarnings("ignore", category=UserWarning) 45 | warnings.simplefilter(action='ignore', category=FutureWarning) 46 | pd.set_option('future.no_silent_downcasting', True) 47 | 48 | logging.basicConfig(level=logging.INFO, 49 | format='\n\n%(asctime)s, %(levelname)s, %(message)s\n', 50 | datefmt='%Y-%m-%d %H:%M:%S' 51 | ) 52 | 53 | __author__ = "Brian Maloney" 54 | __version__ = "2025.11.07" 55 | __email__ = "bmmaloney97@gmail.com" 56 | rbin = [] 57 | DATParser = dat_parser.DATParser() 58 | OneDriveParser = onedrive_parser.OneDriveParser() 59 | SQLiteParser = sqlite_parser.SQLiteParser() 60 | parsing_complete = threading.Event() 61 | q = queue.Queue() 62 | stop = threading.Event() 63 | running_threads = [] 64 | 65 | if getattr(sys, 'frozen', False): 66 | application_path = sys._MEIPASS 67 | else: 68 | application_path = os.path.dirname(os.path.abspath(__file__)) 69 | 70 | 71 | def log_error_and_exit(error_msg: str, title="Unhandled Exception"): 72 | log_date = datetime.now().strftime("%Y-%m-%dT%H%M%S") 73 | log_file = os.path.join(application_path, f'ODE_error_{log_date}.log') 74 | 75 | try: 76 | with open(log_file, 'a', encoding='utf-8') as f: 77 | f.write('The following error has occurred and ODE is shutting down.\n') 78 | f.write(f'For further assistance: {__email__}\n') 79 | f.write(f'OneDriveExplorer v{__version__}\n\n') 80 | f.write(error_msg) 81 | except Exception as file_err: 82 | print("Failed to write error to file:", file_err) 83 | 84 | print(f"\n\n[{title}] See {log_file} for details.\n") 85 | 86 | # Stop all tracked threads gracefully 87 | stop.set() 88 | for t in running_threads: 89 | if t.is_alive(): 90 | t.join(timeout=5) 91 | 92 | sys.exit(1) 93 | 94 | 95 | def report_callback_exception(exc, val, tb): 96 | error_msg = ''.join(traceback.format_exception(exc, val, tb)) 97 | log_error_and_exit(error_msg, title="Tkinter Exception") 98 | 99 | 100 | def thread_exception_handler(args): 101 | error_msg = ''.join(traceback.format_exception(args.exc_type, args.exc_value, args.exc_traceback)) 102 | log_error_and_exit(error_msg, title="Thread Exception") 103 | 104 | 105 | def global_exception_handler(exc_type, exc_value, exc_traceback): 106 | if issubclass(exc_type, KeyboardInterrupt): 107 | sys.__excepthook__(exc_type, exc_value, exc_traceback) 108 | return 109 | error_msg = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)) 110 | log_error_and_exit(error_msg, title="CLI Exception") 111 | 112 | 113 | # Activate global exception hooks 114 | threading.excepthook = thread_exception_handler 115 | sys.excepthook = global_exception_handler 116 | 117 | 118 | def spinning_cursor(): 119 | while True: 120 | for cursor in '|/-\\': 121 | yield cursor 122 | 123 | 124 | def guid(): 125 | print('\033[1;35mGenerating 10 random GUIDs\033[1;0m\n') 126 | count = 0 127 | while True: 128 | print(f'\033[1;37m{uuid.uuid4()}\033[1;0m') 129 | count += 1 130 | if count == 10: 131 | print() 132 | break 133 | 134 | 135 | def thread_parser(Parser): 136 | Parser.start_parsing() 137 | parsing_complete.set() # Signal that parsing is complete 138 | 139 | 140 | def main(): 141 | fields_to_check = ['SETTINGS_DAT', 'SYNC_ENGINE', 'SAFE_DEL', 'LIST_SYNC', 'FILES_ON_DEMAND', 'FILE_USAGE_SYNC', 'LOGS'] 142 | 143 | def has_settings(data): 144 | if isinstance(data, dict): 145 | if 'settings' in data: 146 | return True 147 | return any(has_settings(v) for v in data.values()) 148 | return False 149 | 150 | banner = r''' 151 | _____ ___ ___ _ 152 | ( _ ) ( _`\ _ ( _`\ (_ ) 153 | | ( ) | ___ __ | | ) | _ __ (_) _ _ __ | (_(_) _ _ | | _ _ __ __ _ __ 154 | | | | |/' _ `\ /'__`\| | | )( '__)| |( ) ( ) /'__`\| _)_ (`\/')( '_`\ | | /'_`\ ( '__)/'__`\( '__) 155 | | (_) || ( ) |( ___/| |_) || | | || \_/ |( ___/| (_( ) > < | (_) ) | | ( (_) )| | ( ___/| | 156 | (_____)(_) (_)`\____)(____/'(_) (_)`\___/'`\____)(____/'(_/\_)| ,__/'(___)`\___/'(_) `\____)(_) v{} 157 | | | by @bmmaloney97 158 | (_) 159 | '''.format(__version__) 160 | 161 | print(f'\033[1;37m{banner}\033[1;0m') 162 | parser = argparse.ArgumentParser() 163 | 164 | parser.add_argument("--LIVE", help="Directory to recursively process, looking for .dat, NTUSER hive, $Recycle.Bin, and ODL logs. This mode is primarily used with KAPE.") 165 | parser.add_argument("--PROFILE", help="Profile folder to process. Default location: %%APPDATALOCAL%%\Microsoft\OneDrive") 166 | parser.add_argument("--SETTINGS_DAT", help=".dat file to be parsed", default='') 167 | parser.add_argument("--SYNC_ENGINE", help="SyncEngineDatabase.db file to load.", default='') 168 | parser.add_argument("--SAFE_DEL", help="SafeDelete.db file to load.", default='') 169 | parser.add_argument("--LIST_SYNC", help="Microsoft.ListSync.db file to load.", default='') 170 | parser.add_argument("--FILES_ON_DEMAND", help="Microsoft.FilesOnDemand.db file to load.", default='') 171 | parser.add_argument("--FILE_USAGE_SYNC", help="Microsoft.FileUsageSync.db file to load.", default='') 172 | parser.add_argument("--REG_HIVE", help="If a registry hive is provided then the mount points of the SyncEngines will be resolved.") 173 | parser.add_argument("--RECYCLE_BIN", help="$Recycle.Bin folder to load.") 174 | parser.add_argument("--LOGS", help="Directory to recursively process for ODL logs.", nargs='?', const=True) 175 | parser.add_argument("--output-dir", help="Directory to save results to. Be sure to include the full path in double quotes.", default=".") 176 | parser.add_argument("--csv", action='store_true', help="Save CSV formatted results.") 177 | parser.add_argument("--html", action='store_true', help="Save html formatted results.") 178 | parser.add_argument("--json", action='store_true', help="Save json formatted results. Use --pretty for a more human readable layout.") 179 | parser.add_argument("--pretty", help="When exporting to json, use a more human readable layout. Default is FALSE", action='store_true') 180 | parser.add_argument("--clist", help="List available cstructs. Defaults to 'cstructs' folder where program was executed. Use --cstructs for different cstruct folder.", action='store_true') 181 | parser.add_argument("--cstructs", help="The path where ODL cstructs are located. Defaults to 'cstructs' folder where program was executed.") 182 | parser.add_argument("--sync", help="If true, OneDriveExplorer will download the latest Cstrucs from https://github.com/Beercow/ODEFiles prior to running. Default is FALSE", action='store_true') 183 | parser.add_argument("--debug", help="Show debug information during processing.", action='store_true') 184 | parser.add_argument("--guids", help="OneDriveExplorer will generate 10 GUIDs and exit. Useful when creating new Cstructs. Default is FALSE", action='store_true') 185 | parser.add_argument("--gui", help=argparse.SUPPRESS, action='store_true') 186 | 187 | if len(sys.argv) == 1: 188 | parser.print_help() 189 | parser.exit() 190 | 191 | args = parser.parse_args() 192 | spinner = spinning_cursor() 193 | 194 | if args.sync: 195 | update_from_repo(args.gui) 196 | sys.exit() 197 | 198 | if args.guids: 199 | guid() 200 | sys.exit() 201 | 202 | if args.clist: 203 | load_cparser(args.cstructs, args.clist) 204 | sys.exit() 205 | 206 | # Ensure only one mode is used 207 | # Evaluate the three groups: 208 | live_mode = bool(args.LIVE) 209 | profile_mode = bool(args.PROFILE) 210 | fields_mode = any(getattr(args, f) for f in fields_to_check) # any field chosen counts as one "mode" 211 | 212 | # Count how many of the three mode groups are active: 213 | active_modes = sum((live_mode, profile_mode, fields_mode)) 214 | 215 | if active_modes > 1: 216 | parser.error("Only one of --LIVE, --PROFILE, or any of --SETTINGS_DAT, --SYNC_ENGINE, --SAFE_DEL, --LIST_SYNC, --FILE_USAGE_SYNC can be used.") 217 | 218 | # Enforce dependency: --RECYCLE_BIN requires --REG_HIVE 219 | if args.RECYCLE_BIN and not args.REG_HIVE: 220 | parser.error("--RECYCLE_BIN requires --REG_HIVE to be specified.") 221 | 222 | # --PROFILE allows --REG_HIVE and --RECYCLE_BIN; --LIVE does not 223 | if args.LIVE and (args.REG_HIVE or args.RECYCLE_BIN): 224 | parser.error("--LIVE cannot be used with --REG_HIVE or --RECYCLE_BIN.") 225 | 226 | if not args.debug: 227 | logging.getLogger().setLevel(logging.CRITICAL) 228 | 229 | if args.output_dir: 230 | if not os.path.exists(args.output_dir): 231 | try: 232 | os.makedirs(args.output_dir) 233 | except OSError: 234 | print('Error: Remove trailing \ from directory.\nExample: --output-dir "c:\\temp" ') 235 | sys.exit() 236 | 237 | if sys.argv[-1] == '--LOGS': 238 | args.LOGS = True 239 | 240 | should_parse = args.LIVE or args.PROFILE or ( 241 | any(getattr(args, field) for field in fields_to_check) and not args.LOGS 242 | ) 243 | 244 | if should_parse: 245 | Parser = manager.ParsingManager(args, q) 246 | 247 | t = threading.Thread(target=thread_parser, 248 | args=(Parser,), 249 | daemon=True) 250 | running_threads.append(t) 251 | t.start() 252 | 253 | last_stat = '' 254 | 255 | while not parsing_complete.is_set(): 256 | try: 257 | stat = q.get_nowait() 258 | last_stat = stat 259 | except queue.Empty: 260 | pass 261 | 262 | if last_stat: 263 | sys.stdout.write(f'{last_stat} {next(spinner)}\r') 264 | sys.stdout.flush() 265 | 266 | time.sleep(0.2) 267 | 268 | sys.exit() 269 | 270 | 271 | if __name__ == '__main__': 272 | main() 273 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/utils.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | import os 26 | import re 27 | import sys 28 | import shutil 29 | import urllib.request 30 | from zipfile import ZipFile 31 | import hashlib 32 | import logging 33 | from Registry import Registry 34 | from importlib.util import spec_from_loader, module_from_spec 35 | from importlib.machinery import SourceFileLoader 36 | 37 | if getattr(sys, 'frozen', False): 38 | application_path = sys._MEIPASS 39 | else: 40 | application_path = f'{os.path.dirname(os.path.abspath(__file__))}/..' 41 | 42 | spec = spec_from_loader("schema", SourceFileLoader("schema", f"{application_path}/ode/helpers/schema")) 43 | schema = module_from_spec(spec) 44 | spec.loader.exec_module(schema) 45 | 46 | log = logging.getLogger(__name__) 47 | 48 | BASE_DIR = os.getcwd() 49 | NEW_MAPS_URL = "https://github.com/Beercow/ODEFiles/archive/master.zip" 50 | NEW_MAPS_DIR = os.path.join(BASE_DIR, "ODEFiles-master", "cstructs") 51 | OLD_MAPS_DIR = os.path.join(BASE_DIR, "cstructs") 52 | 53 | 54 | def update_from_repo(gui=False): 55 | print(f'\033[1;37mChecking for updated Cstructs at {NEW_MAPS_URL}...\n\033[1;0m') 56 | archive_path = os.path.join(BASE_DIR, "____master.zip") 57 | 58 | if os.path.exists(archive_path): 59 | os.remove(archive_path) 60 | 61 | urllib.request.urlretrieve(NEW_MAPS_URL, archive_path) 62 | 63 | with ZipFile(archive_path, 'r') as zip_ref: 64 | zip_ref.extractall(BASE_DIR) 65 | 66 | os.remove(archive_path) 67 | 68 | if not os.path.exists(OLD_MAPS_DIR): 69 | os.makedirs(OLD_MAPS_DIR) 70 | 71 | new_maps = os.listdir(NEW_MAPS_DIR) 72 | 73 | new_local_maps = [] 74 | updated_local_maps = [] 75 | 76 | for new_map in new_maps: 77 | dest = os.path.join(OLD_MAPS_DIR, new_map) 78 | 79 | if not os.path.exists(dest): 80 | # new target 81 | new_local_maps.append(new_map) 82 | else: 83 | # current destination file exists, so compare to new 84 | with open(os.path.join(NEW_MAPS_DIR, new_map), 'rb') as new_file: 85 | new_sha = hashlib.sha1(new_file.read()).hexdigest() 86 | 87 | with open(dest, 'rb') as dest_file: 88 | dest_sha = hashlib.sha1(dest_file.read()).hexdigest() 89 | 90 | if new_sha != dest_sha: 91 | # updated file 92 | updated_local_maps.append(new_map) 93 | shutil.copy2(os.path.join(NEW_MAPS_DIR, new_map), dest) 94 | 95 | if new_local_maps or updated_local_maps: 96 | print("\n\033[1;31mUpdates found!\033[1;0m") 97 | 98 | if new_local_maps: 99 | print("\n\033[1;33mNew cstructs\033[1;0m") 100 | for new_local_map in new_local_maps: 101 | print(f"\033[1;37m{os.path.splitext(new_local_map)[0]}\033[1;0m") 102 | 103 | if updated_local_maps: 104 | print("\n\033[1;33mUpdated cstructs\033[1;0m") 105 | for updated_local_map in updated_local_maps: 106 | print(f"\033[1;37m{os.path.splitext(updated_local_map)[0]}\033[1;0m") 107 | 108 | else: 109 | print("\033[1;37mNo new cstructs available\033[1;0m") 110 | 111 | shutil.rmtree(os.path.join(BASE_DIR, "ODEFiles-master")) 112 | 113 | if gui: 114 | input('\n\nPress any key to exit') 115 | 116 | 117 | def parse_reg(reghive, account, df): 118 | reg_handle = Registry.Registry(reghive) 119 | int_keys = reg_handle.open('SOFTWARE\\SyncEngines\\Providers\\OneDrive') 120 | 121 | od_keys = reg_handle.open(f'SOFTWARE\\Microsoft\\OneDrive\\Accounts\\{account}\\Tenants') 122 | 123 | for providers in int_keys.subkeys(): 124 | df.loc[(df.DriveItemId == providers.name().split('+')[0]), ['Name']] = [x.value() for x in list(providers.values()) if x.name() == 'MountPoint'][0] 125 | 126 | try: 127 | reghive.seek(0) 128 | except Exception: 129 | pass 130 | 131 | return df, od_keys 132 | 133 | 134 | def find_parent(x, id_name_dict, parent_dict): 135 | value = parent_dict.get(x, None) 136 | if value is None: 137 | return x # may need to change to '' 138 | else: 139 | # In case there is a id without name. 140 | if id_name_dict.get(value, None) is None: 141 | return find_parent(value, id_name_dict, parent_dict) + x 142 | 143 | return find_parent(value, id_name_dict, parent_dict) + "\\\\" + str(id_name_dict.get(value)) 144 | 145 | 146 | def unicode_strings(buf, ouuid): 147 | uni_re = re.compile("(?:[" 148 | "\w" 149 | "\s" 150 | u"\u0020-\u007f" 151 | u"\U0001F600-\U0001F64F" # emoticons 152 | u"\U0001F300-\U0001F5FF" # symbols & pictographs 153 | u"\U0001F680-\U0001F6FF" # transport & map symbols 154 | u"\U0001F1E0-\U0001F1FF" # flags (iOS) 155 | u"\U00002500-\U00002BEF" # chinese char 156 | u"\U00002702-\U000027B0" 157 | u"\U00002702-\U000027B0" 158 | u"\U000024C2-\U0001F251" 159 | u"\U0001f926-\U0001f937" 160 | u"\U00010000-\U0010ffff" 161 | u"\u2640-\u2642" 162 | u"\u2600-\u2B55" 163 | u"\u200d" 164 | u"\u23cf" 165 | u"\u23e9" 166 | u"\u231a" 167 | u"\ufe0f" # dingbats 168 | u"\u3030" 169 | "]{1,}\x00[\uabab|\x00\x00]?)", flags=re.UNICODE) 170 | match = uni_re.search(buf.decode("utf-16", errors='ignore')) 171 | 172 | if match: 173 | try: 174 | return match.group() 175 | except Exception as e: 176 | log.warning(e) 177 | 178 | log.error(f'An error occured trying to find the name of {ouuid}. Raw Data:{buf}') 179 | return '??????????' 180 | 181 | 182 | def change_dtype(df, df_name=None, schema_version=0): 183 | # Define mappings for dtype changes and fill values 184 | dtype_fill_map = { 185 | 'df_scope': { 186 | 'dtype_changes': {'Type': 'str', 'scopeID': 'str', 'siteID': 'str', 'webID': 'str', 'listID': 'str', 'spoPermissions': 'Int64', 'shortcutVolumeID': 'Int64', 'shortcutItemIndex': 'Int64', 'libraryType': 'str'}, 187 | 'fill_values': {'Type': 'Scope', 'scopeID': '', 'siteID': '', 'webID': '', 'listID': '', 'spoPermissions': 0, 'shortcutVolumeID': 0, 'shortcutItemIndex': 0, 'libraryType': ''} 188 | }, 189 | 'df_files': { 190 | 'dtype_changes': {'Type': 'str', 'parentResourceID': 'str', 'resourceID': 'str', 'eTag': 'str', 'Name': 'str', 'fileStatus': 'Int64', 'spoPermissions': 'Int64', 'volumeID': 'Int64', 'itemIndex': 'Int64', 'size': 'Int64', 'localHashAlgorithm': 'Int64', 'sharedItem': 'Int64', 'Width': 'Int64', 'Height': 'Int64', 'Duration': 'Int64'}, 191 | 'fill_values': {'Type': 'File', 'parentResourceID': '', 'resourceID': '', 'eTag': '', 'Name': '', 'fileStatus': 0, 'spoPermissions': 0, 'volumeID': 0, 'itemIndex': 0, 'size': 0, 'localHashAlgorithm': 0, 'sharedItem': 0, 'Width': 0, 'Height': 0, 'Duration': 0} 192 | }, 193 | 'df_folders': { 194 | 'dtype_changes': {'Type': 'str', 'parentScopeID': 'str', 'parentResourceID': 'str', 'resourceID': 'str', 'eTag': 'str', 'Name': 'str', 'folderStatus': 'Int64', 'spoPermissions': 'Int64', 'volumeID': 'Int64', 'itemIndex': 'Int64', 'sharedItem': 'Int64'}, 195 | 'fill_values': {'Type': 'Folder', 'parentScopeID': '', 'parentResourceID': '', 'resourceID': '', 'eTag': '', 'Name': '', 'folderStatus': 0, 'spoPermissions': 0, 'volumeID': 0, 'itemIndex': 0, 'sharedItem': 0} 196 | }, 197 | 'df_GraphMetadata_Records': { 198 | 'dtype_changes': {'resourceID': 'str', 'graphMetadataJSON': 'str', 'spoCompositeID': 'str', 'createdBy': 'str', 'modifiedBy': 'str', 'lastWriteCount': 'Int64'}, 199 | 'fill_values': {'resourceID': '', 'graphMetadataJSON': '', 'spoCompositeID': '', 'createdBy': '', 'modifiedBy': '', 'lastWriteCount': 0} 200 | }, 201 | 'rbin_df': { 202 | 'dtype_changes': {'Type': 'str', 'parentResourceId': 'str', 'resourceId': 'str', 'eTag': 'str', 'Path': 'str', 'Name': 'str', 'inRecycleBin': 'Int64', 'volumeId': 'Int64', 'fileId': 'str', 'DeleteTimeStamp': 'Int64', 'notificationTime': 'Int64', 'size': 'Int64', 'hash': 'str', 'deletingProcess': 'str'}, 203 | 'fill_values': {'Type': '', 'parentResourceId': '', 'resourceId': '', 'eTag': '', 'Path': '', 'Name': '', 'inRecycleBin': 0, 'volumeId': 0, 'fileId': '', 'DeleteTimeStamp': 0, 'notificationTime': 0, 'size': 0, 'hash': '', 'deletingProcess': ''} 204 | }, 205 | 'offline': { 206 | 'dtype_changes': {'resourceID': 'str', 'SharedWithDetails': 'str', 'MediaServiceMetadata': 'str'}, 207 | 'fill_values': {'resourceID': '', 'SharedWithDetails': '', 'MediaServiceMetadata': ''} 208 | } 209 | } 210 | 211 | # Handle special cases 212 | if df_name == 'df_files' and schema_version >= 27: 213 | dtype_fill_map['df_files']['dtype_changes']['hydrationCount'] = 'Int64' 214 | dtype_fill_map['df_files']['fill_values']['hydrationCount'] = 0 215 | 216 | if df_name == 'df_folders' and schema_version >= 24: 217 | dtype_fill_map['df_folders']['dtype_changes']['folderColor'] = 'Int64' 218 | dtype_fill_map['df_folders']['fill_values']['folderColor'] = 0 219 | 220 | if df_name == 'df_scope' and schema_version > 8: 221 | dtype_fill_map['df_scope']['dtype_changes']['tenantID'] = 'str' 222 | dtype_fill_map['df_scope']['fill_values']['tenantID'] = '' 223 | dtype_fill_map['df_scope']['dtype_changes']['webURL'] = 'str' 224 | dtype_fill_map['df_scope']['fill_values']['webURL'] = '' 225 | dtype_fill_map['df_scope']['dtype_changes']['remotePath'] = 'str' 226 | dtype_fill_map['df_scope']['fill_values']['remotePath'] = '' 227 | 228 | if df_name == 'df_GraphMetadata_Records' and schema_version > 13: 229 | dtype_fill_map['df_GraphMetadata_Records']['dtype_changes']['filePolicies'] = 'str' 230 | dtype_fill_map['df_GraphMetadata_Records']['fill_values']['filePolicies'] = '' 231 | dtype_fill_map['df_GraphMetadata_Records']['dtype_changes']['fileExtension'] = 'str' 232 | dtype_fill_map['df_GraphMetadata_Records']['fill_values']['fileExtension'] = '' 233 | dtype_fill_map['df_GraphMetadata_Records']['dtype_changes']['lastWriteCount'] = 'Int64' 234 | dtype_fill_map['df_GraphMetadata_Records']['fill_values']['lastWriteCount'] = 0 235 | 236 | # Apply changes if df_name is recognized 237 | if df_name in dtype_fill_map: 238 | df.fillna(dtype_fill_map[df_name]['fill_values'], inplace=True) 239 | df = df.astype(dtype_fill_map[df_name]['dtype_changes']) 240 | 241 | return df 242 | 243 | 244 | def progress(count, total, status=''): 245 | # Get terminal width 246 | terminal_width = shutil.get_terminal_size((80, 20)).columns 247 | 248 | # Reserve space for percentage and status text 249 | reserved_space = len(f'[] 100.0% ...{status}') 250 | 251 | bar_len = min(60, max(10, terminal_width - reserved_space)) 252 | filled_len = int(round(bar_len * count / float(total))) 253 | 254 | percents = round(100.0 * count / float(total), 1) 255 | bar = '=' * filled_len + '-' * (bar_len - filled_len) 256 | 257 | sys.stdout.write(f'[{bar}] {percents}% ...{status}\r') 258 | sys.stdout.flush() 259 | 260 | 261 | def progress_gui(total, count, pb, value_label, status=False): 262 | if pb['value'] != 100: 263 | pb['value'] = round(100.0 * count / float(total), 1) 264 | value_label['text'] = f"{status} {pb['value']}%" 265 | 266 | 267 | def permissions(_): 268 | perstr = [] 269 | # Lists and Documents 270 | if _ & 0x0: 271 | perstr.append("EmptyMask") 272 | if _ & 0x1: 273 | perstr.append("ViewListItems") 274 | if _ & 0x2: 275 | perstr.append("AddListItems") 276 | if _ & 0x4: 277 | perstr.append("EditListItems") 278 | if _ & 0x8: 279 | perstr.append("DeleteListItems") 280 | if _ & 0x10: 281 | perstr.append("ApproveItems") 282 | if _ & 0x20: 283 | perstr.append("OpenItems") 284 | if _ & 0x40: 285 | perstr.append("ViewVersions") 286 | if _ & 0x80: 287 | perstr.append("DeleteVersions") 288 | if _ & 0x100: 289 | perstr.append("OverrideListBehaviors") 290 | if _ & 0x200: 291 | perstr.append("ManagePersonalViews") 292 | if _ & 0x800: 293 | perstr.append("ManageLists") 294 | if _ & 0x1000: 295 | perstr.append("ViewApplicationPages") 296 | # Web Level 297 | if _ & 0x10000: 298 | perstr.append("Open") 299 | if _ & 0x20000: 300 | perstr.append("ViewPages") 301 | if _ & 0x40000: 302 | perstr.append("AddAndCustomizePages") 303 | if _ & 0x80000: 304 | perstr.append("ApplyThemAndBorder") 305 | if _ & 0x100000: 306 | perstr.append("ApplyStyleSheets") 307 | if _ & 0x200000: 308 | perstr.append("ViewAnalyticsData") 309 | if _ & 0x400000: 310 | perstr.append("UseSSCSiteCreation") 311 | if _ & 0x800000: 312 | perstr.append("CreateSubsite") 313 | if _ & 0x1000000: 314 | perstr.append("CreateGroups") 315 | if _ & 0x2000000: 316 | perstr.append("ManagePermissions") 317 | if _ & 0x4000000: 318 | perstr.append("BrowseDirectories") 319 | if _ & 0x8000000: 320 | perstr.append("BrowseUserInfo") 321 | if _ & 0x10000000: 322 | perstr.append("AddDelPersonalWebParts") 323 | if _ & 0x20000000: 324 | perstr.append("UpdatePersonalWebParts") 325 | if _ & 0x40000000: 326 | perstr.append("ManageWeb") 327 | if _ & 0x1000000000: 328 | perstr.append("UseClientIntegration") 329 | if _ & 0x2000000000: 330 | perstr.append("UseRemoteInterfaces") 331 | if _ & 0x4000000000: 332 | perstr.append("ManageAlerts") 333 | if _ & 0x8000000000: 334 | perstr.append("CreateAlerts") 335 | if _ & 0x10000000000: 336 | perstr.append("EditPersonalUserInformation") 337 | # Special Permissions 338 | if _ & 0x4000000000000000: 339 | perstr.append("EnumeratePermissions") 340 | return perstr 341 | -------------------------------------------------------------------------------- /OneDriveExplorer/ode/parsers/Nucleus/listsync.py: -------------------------------------------------------------------------------- 1 | # OneDriveExplorer 2 | # Copyright (C) 2025 3 | # 4 | # This file is part of OneDriveExplorer 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | 26 | import codecs 27 | import json 28 | import sqlite3 29 | import pandas as pd 30 | import logging 31 | from urllib.parse import unquote 32 | from ode.utils import change_dtype 33 | 34 | 35 | class ParseResult: 36 | def __init__(self, ocr_db, df_list_sync, scopeID, account): 37 | self.ocr_db = ocr_db 38 | self.df_list_sync = df_list_sync 39 | self.scopeID = scopeID 40 | self.account = account 41 | 42 | def __repr__(self): 43 | """Custom string representation for debugging.""" 44 | return f"ParseResult(ocr_db={len(self.ocr_db)} rows, df_list_sync={len(self.df_list_sync)} rows, scopeID={len(self.scopeID)})" 45 | 46 | 47 | class SQLiteTableExporter: 48 | def __init__(self, db_path): 49 | self.log = logging.getLogger(__name__) 50 | self.db_path = db_path 51 | self.df_scope = pd.DataFrame() 52 | self.scopeID = [] 53 | self.conn = None 54 | self.cursor = None 55 | self.account = None 56 | self.filename = None 57 | self.directory = None 58 | 59 | self.replacements = { 60 | "-": "", 61 | "{": "", 62 | "}": "" 63 | } 64 | 65 | def strtoll(self, ptr, base): 66 | try: 67 | stripped_ptr = ptr.strip() 68 | result = int(stripped_ptr, base) 69 | 70 | if result < -(2**63): 71 | raise OverflowError("Underflow: Value too small for long long") 72 | elif result >= 2**63: 73 | raise OverflowError("Overflow: Value too large for long long") 74 | 75 | return result 76 | 77 | except ValueError: 78 | return 0 79 | except OverflowError as e: 80 | if "Underflow" in str(e): 81 | return -(2**63) 82 | elif "Overflow" in str(e): 83 | return (2**63) - 1 84 | 85 | def populate_media_service_metadata(self, row): 86 | if 'wordDump' in row['MediaServiceMetadata']: 87 | return row['MediaServiceMetadata'].get('wordDump', '') 88 | return '' 89 | 90 | def compute_hash(self, row): 91 | if row['localHashDigest'] not in (None, ''): 92 | return f'quickXor({codecs.encode(bytes.fromhex(row["localHashDigest"][4:]), "base64").decode("utf-8").rstrip()})' 93 | else: 94 | return '' 95 | 96 | def get_permissions(self, perm_mask): 97 | if isinstance(perm_mask, str) and perm_mask.startswith("0x"): 98 | perm_mask = int(perm_mask, 16) 99 | 100 | permissions = [ 101 | ("Full Control", 63), 102 | ("Design", 5), 103 | ("Edit", 12), 104 | ("Contribute", 3), 105 | ("Read", 40), 106 | ("Restricted View", None) 107 | ] 108 | 109 | for name, bit in permissions: 110 | if bit is None: 111 | return name 112 | if perm_mask & (1 << (bit - 1)): 113 | return name 114 | 115 | def get_df_scope(self): 116 | # Get all distinct keys 117 | self.cursor.execute("SELECT DISTINCT key FROM list_collection_item_attributes;") 118 | keys = [row[0] for row in self.cursor.fetchall()] 119 | 120 | # Build dynamic pivot expressions 121 | pivot_expressions = [ 122 | f"MAX(CASE WHEN key = '{k}' THEN value END) AS '{k}'" for k in keys 123 | ] 124 | pivot_sql = ",\n ".join(pivot_expressions) 125 | 126 | # Build full query 127 | query = f""" 128 | WITH pivoted AS ( 129 | SELECT 130 | listCollectionItemID, 131 | {pivot_sql} 132 | FROM list_collection_item_attributes 133 | GROUP BY listCollectionItemID 134 | ) 135 | SELECT 136 | p.*, 137 | l.title, 138 | l.hidden, 139 | l.lastChangeToken, 140 | l.vroomSyncToken, 141 | l.permissionToken, 142 | l.effectivePermMask, 143 | l.lastLocalETag, 144 | l.localETagColumnName, 145 | l.flags, 146 | l.schemaVersion, 147 | l.lastViewChangeToken, 148 | l.timeOfLastSyncVerification, 149 | l.lastUnsyncTime, 150 | l.lastSyncTime, 151 | l.lastActiveTime, 152 | l.lastFullSyncTime, 153 | l.lastSettingsChangeToken, 154 | l.syncStartTime, 155 | l.swapSyncTokenTime, 156 | l.apiType, 157 | l.internalVersion, 158 | l.previousLastActiveTime, 159 | l.lastUnsyncReason, 160 | l.eligibilityDetails, 161 | l.lastRecordedListPermissions, 162 | l.hasResyncedFolders, 163 | l.ocdiVersion, 164 | l.teamID, 165 | l.templateType, 166 | l.lastForcedSyncSchema, 167 | l.lastResyncStartTime, 168 | l.lastResyncEndTime, 169 | l.lastResyncReason, 170 | s.conflictCount, 171 | s.changeCount, 172 | lc.name AS collectionName 173 | FROM pivoted AS p 174 | LEFT JOIN lists l 175 | ON l.listID = p.listId 176 | LEFT JOIN list_sync_details s 177 | ON s.listID = p.listId 178 | LEFT JOIN list_collection_items AS lci 179 | ON p.listCollectionItemID = lci.listCollectionItemID 180 | LEFT JOIN list_collections AS lc 181 | ON lci.listCollectionID = lc.listCollectionID 182 | ORDER BY p.listCollectionItemID; 183 | """ 184 | 185 | # Run query 186 | try: 187 | self.df_scope = pd.read_sql_query(query, self.conn) 188 | self.df_scope.rename(columns={"siteId": "siteID", "webId": "webID", "listId": "listID"}, inplace=True) 189 | self.df_scope['siteID'] = self.df_scope['siteID'].replace(self.replacements, regex=True).str.lower() 190 | self.df_scope['webID'] = self.df_scope['webID'].replace(self.replacements, regex=True).str.lower() 191 | self.df_scope['listID'] = self.df_scope['listID'].replace(self.replacements, regex=True).str.lower() 192 | self.df_scope['Type'] = 'Scope' 193 | self.df_scope.rename(columns={"listCollectionItemID": "libraryType"}, inplace=True) 194 | except Exception as e: 195 | print("Error running query:", e) 196 | self.df_scope = pd.DataFrame() 197 | 198 | def get_list_sync_data(self): 199 | self.directory = self.db_path.rsplit('\\', 1)[0] 200 | self.filename = self.db_path.split('\\')[-1] 201 | self.account = self.db_path.replace('/', '\\').split('\\')[-3] 202 | 203 | try: 204 | # Connect to the SQLite database 205 | self.conn = sqlite3.connect(f'file:/{self.db_path}?mode=ro', uri=True) 206 | self.conn.create_function("strtoll", 2, self.strtoll, deterministic=True) 207 | self.cursor = self.conn.cursor() 208 | 209 | self.get_df_scope() 210 | 211 | try: 212 | # Find tables matching a pattern 213 | self.cursor.execute("SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE '%rows'") 214 | tables = self.cursor.fetchall() 215 | 216 | # Perform a query on each matching table 217 | merged_data = [] 218 | smerged_data = [] 219 | 220 | for table in tables: 221 | table_name = table[0] 222 | 223 | self.cursor.execute(f'SELECT name FROM PRAGMA_TABLE_INFO("{table_name}") WHERE name LIKE "A2OD%" OR name = "UniqueId"') 224 | columns = self.cursor.fetchall() 225 | col_names = [r[0] for r in columns] 226 | 227 | if 'A2ODRemoteItemUniqueId' in col_names: 228 | cols = ", ".join(col_names) 229 | cols += ", COALESCE(json_extract(A2ODExtendedMetadata, '$.riwu'), '') AS webURL" 230 | cols += ", COALESCE(json_extract(A2ODExtendedMetadata, '$.riti'), '') AS tenantID" 231 | cols += ", ProgID" 232 | df_smerge = pd.read_sql_query(f'SELECT {cols} FROM "{table_name}" WHERE ProgID = "AddToOneDrive.MountPoint" AND A2ODRemoteItemUniqueId IS NOT NULL AND A2ODRemoteItemUniqueId <> ""', self.conn) 233 | df_smerge.rename(columns={"A2ODRemoteItemSiteId": "siteID"}, inplace=True) 234 | df_smerge.rename(columns={"A2ODRemoteItemWebId": "webID"}, inplace=True) 235 | df_smerge.rename(columns={"A2ODRemoteItemListId": "listID"}, inplace=True) 236 | #df_smerge.rename(columns={"UniqueId": "scopeID"}, inplace=True) 237 | df_smerge['siteID'] = df_smerge['siteID'].replace(self.replacements, regex=True).str.lower() 238 | df_smerge['webID'] = df_smerge['webID'].replace(self.replacements, regex=True).str.lower() 239 | df_smerge['listID'] = df_smerge['listID'].replace(self.replacements, regex=True).str.lower() 240 | #df_smerge['scopeID'] = df_smerge['scopeID'].replace(self.replacements, regex=True).str.lower() 241 | df_smerge.drop(columns=["A2ODMountCount", "A2ODIsMountPoint", "A2ODExtendedMetadata", "A2ODRemoteItemUniqueId"], inplace=True) 242 | smerged_data.append(df_smerge) 243 | 244 | df = pd.read_sql_query( 245 | f'SELECT ContentType, ParentUniqueId, DocConcurrencyNumber, UniqueID, FileLeafRef, EncodedAbsUrl, Created, Modified, SMTotalFileStreamSize, StreamHash, SharedWithDetails, _ColorHex, MediaServiceMetadata, PermMask, ProgId FROM "{table_name}"', 246 | self.conn 247 | ) 248 | df.rename(columns={"ContentType": "Type"}, inplace=True) 249 | df.rename(columns={"ParentUniqueId": "parentResourceID"}, inplace=True) 250 | df.rename(columns={"UniqueId": "resourceID"}, inplace=True) 251 | df.rename(columns={"FileLeafRef": "Name"}, inplace=True) 252 | df.rename(columns={"EncodedAbsUrl": "Path"}, inplace=True) 253 | df.rename(columns={"SMTotalFileStreamSize": "size"}, inplace=True) 254 | df.rename(columns={"StreamHash": "localHashDigest"}, inplace=True) 255 | df.rename(columns={"_ColorHex": "folderColor"}, inplace=True) 256 | df.rename(columns={"Modified": "lastChange"}, inplace=True) 257 | df.loc[df['ProgId'] == "AddToOneDrive.MountPoint", 'Type'] = "Folder" 258 | df.drop(columns=["ProgId"], inplace=True) 259 | merged_data.append(df) 260 | 261 | # Merge all collected data into a single DataFrame 262 | if smerged_data: 263 | smerged_df = pd.concat(smerged_data, ignore_index=True) 264 | 265 | # Decide which keys to join on – likely siteID/webID/listID 266 | self.df_scope = pd.merge( 267 | self.df_scope, 268 | smerged_df, 269 | on=["siteID", "webID", "listID"], 270 | how="outer" 271 | ) 272 | 273 | if merged_data: 274 | df_list_sync = pd.concat(merged_data, ignore_index=True) 275 | df_list_sync['fileStatus'] = '' 276 | df_list_sync['folderStatus'] = '' 277 | df_list_sync.loc[df_list_sync.Type == 'Document', ['FileSort']] = df_list_sync['Name'].str.lower() 278 | df_list_sync.loc[df_list_sync.Type == 'Folder', ['FolderSort']] = df_list_sync['Name'].str.lower() 279 | df_list_sync['folderColor'] = df_list_sync['folderColor'].astype("Int64").fillna(0) 280 | df_list_sync['eTag'] = '"{' + df_list_sync['resourceID'].astype(str) + '},' + df_list_sync['DocConcurrencyNumber'].astype(str) + '"' 281 | df_list_sync['resourceID'] = df_list_sync['resourceID'].replace(self.replacements, regex=True).str.lower() 282 | df_list_sync['parentResourceID'] = df_list_sync['parentResourceID'].replace(self.replacements, regex=True).str.lower() 283 | df_list_sync['size'] = pd.to_numeric(df_list_sync['size'], errors='coerce').fillna(0).astype(int) 284 | df_list_sync['Path'] = df_list_sync['Path'].str.rsplit('/', n=1).str[0] 285 | df_list_sync['size'] = df_list_sync['size'].apply(lambda x: '0 KB' if x == 0 else f'{x//1024 + 1:,} KB') 286 | df_list_sync['localHashDigest'] = df_list_sync.apply(self.compute_hash, axis=1) 287 | df_list_sync['SharedItem'] = df_list_sync['SharedWithDetails'].fillna("").str.strip().ne("").astype("Int64") 288 | df_list_sync = change_dtype(df_list_sync, df_name='offline') 289 | json_columns = ['SharedWithDetails', 'MediaServiceMetadata'] 290 | df_list_sync[json_columns] = df_list_sync[json_columns].map(lambda x: json.loads(x) if pd.notna(x) and x.strip() else '') 291 | df_list_sync['MediaServiceMetadata'] = df_list_sync.apply(self.populate_media_service_metadata, axis=1) 292 | df_list_sync['ListSync'] = df_list_sync[['SharedWithDetails', 'MediaServiceMetadata']].apply( 293 | lambda x: ( 294 | {'SharedWithDetails': x['SharedWithDetails'], 'MediaServiceMetadata': x['MediaServiceMetadata']} 295 | if pd.notna(x['SharedWithDetails']) and str(x['SharedWithDetails']).strip() != "" 296 | or pd.notna(x['MediaServiceMetadata']) and str(x['MediaServiceMetadata']).strip() != "" 297 | else '' 298 | ), 299 | axis=1 300 | ) 301 | df_list_sync['PermMask'] = df_list_sync['PermMask'].apply(lambda x: self.get_permissions(x)) 302 | df_list_sync.drop(columns=['SharedWithDetails', 'MediaServiceMetadata'], inplace=True) 303 | 304 | missing_ids = set(df_list_sync["parentResourceID"].dropna()) - set(df_list_sync["resourceID"].dropna()) 305 | 306 | missing_map = ( 307 | df_list_sync.loc[df_list_sync["parentResourceID"].isin(missing_ids), ["parentResourceID", "Path"]] 308 | .set_index("parentResourceID")["Path"].apply(unquote) 309 | .to_dict() 310 | ) 311 | 312 | self.scopeID = list(missing_map.keys()) 313 | scopeid_lookup = pd.DataFrame([ 314 | {'listUrl': webURL, 'scopeID': scopeID} 315 | for scopeID, webURL in missing_map.items() 316 | ]) 317 | 318 | self.df_scope = self.df_scope.merge( 319 | scopeid_lookup, 320 | on='listUrl', 321 | how='left' 322 | ) 323 | self.df_scope['MountPoint'] = '' 324 | self.df_scope.fillna('', inplace=True) 325 | 326 | df_list_sync = pd.concat([df_list_sync, self.df_scope], ignore_index=True) 327 | df_list_sync['scopeID'] = df_list_sync['scopeID'].fillna('') 328 | 329 | else: 330 | self.log.info(f'No _rows tables found in {self.db_path}.') 331 | df_list_sync = pd.DataFrame(columns=['resourceID', 'ListSync']) 332 | 333 | except Exception as e: 334 | self.log.warning(f'Unable to parse {self.db_path}. {e}') 335 | df_list_sync = pd.DataFrame(columns=['resourceID', 'ListSync']) 336 | 337 | self.conn.close() 338 | 339 | except sqlite3.OperationalError: 340 | self.log.info('Microsoft.ListSync.db does not exist') 341 | df_list_sync = pd.DataFrame(columns=['resourceID', 'ListSync']) 342 | 343 | ocr_db = df_list_sync[['resourceID', 'ListSync']] 344 | ocr_db = ocr_db[ 345 | ocr_db['ListSync'].notna() & (ocr_db['ListSync'] != '') 346 | ] 347 | 348 | return ParseResult( 349 | ocr_db, 350 | df_list_sync, 351 | self.scopeID, 352 | self.account 353 | ) 354 | --------------------------------------------------------------------------------