-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathChromeParser.py
More file actions
847 lines (691 loc) · 30.7 KB
/
ChromeParser.py
File metadata and controls
847 lines (691 loc) · 30.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
__author__ = 'marleyjaffe'
import sqlite3 as lite
import argparse
import os
import time
import glob
import platform
# Sets Global variables for verbosity and outFile
verbosity = 3
outFile = False
def ParseCommandLine():
"""
Name: ParseCommandLine
Description: Process and Validate the command line arguments
use Python Standard Library module argparse
Input: none
Actions:
Uses the standard library argparse to process the command line
"""
# Creates the argument parser object names it parser
parser = argparse.ArgumentParser('Chrome Sync Parser',
description='Pulls and Parses the relevant Chrome Sync Databases')
# Adds arguments to the parser object
# Creates a link to a database outside of the normal system path
parser.add_argument('-d', '--database', type=ValidateDatabase,
help="Full path to database in question")
# Allows the user to create a starting path for a system extraction.
# If set the program will go to this path before going into each users folder
parser.add_argument('-p', '--path', default=False,
help="Starting Path to where the database should recursively look for databases")
# Verbose CLI argument, enables error printing etc.
parser.add_argument('-v', '--verbose', type=int, default=3,
help="Makes the program print extra information to the screen")
# Allows the output from the program to be saved to a file.
# If no file is set, the program will print data to the screen.
parser.add_argument('-f', '--outFile', default=False, help="allows the output to be stored to a file")
return parser.parse_args()
# End ParseCommandLine ================================
def ValidateDatabase(filePath):
"""
Name: ValidateDatabase
Description: Checks the passed database to ensure its readable and a file
Input: The path to the file
Actions: Checks the file to ensure it is a file and is readable
"""
if os.path.isfile(filePath): # Checks to ensure the file exists
if os.access(filePath, os.R_OK): # Checks to ensure the program has read access
return filePath
else:
raise argparse.ArgumentTypeError("Error: File is not readable")
else:
raise argparse.ArgumentTypeError("Error: Path is not valid")
# End ValidateDatabase ================================
def GetDatabases(startingPath):
"""
Name: GetDatabases
Description: Runs through each users directory on a system to pull the SyncData.sqlite3 file
Also has the capability to have this search start in a new location
This is useful for file exports when keeping folder structure
Input: Starting Path, either the starting path or False
Actions: Checks the System type and release.
Uses Globing to pull each SyncData.sqlite3 file
Adds the found database paths to a list and returns the list
"""
# Creates a blank list
databaseList = []
# TODO Allow the examination of a windows export on mac system and vice versa. Done by passing platform as var
try:
# Checks if the running system is Windows
if platform.system() == "Windows":
# Checks if the system is 7 or XP
if platform.release() == "7":
# Checks if there was a starting path provided
if startingPath:
# Sets the databasePath to the the OS specific path with the starting path defined.
databasePath = startingPath + "\\Users\\*\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Sync Data\\SyncData.sqlite3"
else:
# Sets the databasePath to the the OS specific path.
databasePath = "C:\\Users\\*\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Sync Data\\SyncData.sqlite3"
elif platform.release() == "XP":
if startingPath:
# Sets the databasePath to the the OS specific path with the starting path defined.
databasePath = startingPath + "\\Documents and Settings\\*\\Application Support\\Google\\Chrome\\User Data\\Default\\Sync Data\\SyncData.sqlite3"
else:
# Sets the databasePath to the the OS specific path
databasePath = "C:\\Documents and Settings\\*\\Application Support\\Google\\Chrome\\User Data\\Default\\Sync Data\\SyncData.sqlite3"
elif platform.system() == "Darwin":
if startingPath:
# Sets the databasePath to the the OS specific path with the starting path defined.
databasePath = startingPath +"/Users/*/Library/Application Support/Google/Chrome/Default/Sync Data/SyncData.sqlite3"
else:
# Sets the databasePath to the the OS specific path
databasePath = "/Users/*/Library/Application Support/Google/Chrome/Default/Sync Data/SyncData.sqlite3"
else:
Report("ERROR: no system detected", 3)
return databaseList
except Exception as err:
Report(str(err),2)
# Performs the actual glob search using the previously defined databasePath
for file in glob.glob(databasePath):
# Adds each found database to the databaseList
databaseList.append(SyncFile(file))
# Returns the databaseList
return databaseList
# End GetDatabases ====================================
class SyncFile():
def __init__(self, database):
"""
Name: SyncFile
Description: Creates objects from passed database
Input: Path to the syncFile Database
Actions: Creates the object using set functions
Uses the sqlite3 library as lite
"""
# Sets the self.database to the database path
self.database = database
# Creates a connection to the database
self.connection = lite.connect(self.database)
# Creates a cursor object for the database
self.cursor = self.connection.cursor()
# Sets the initial database tables to nothing
self.tables = []
# Checks to see if the passed database is locked. If so, will raise the error and stop creating the object.
# Also sets the tables var
try:
# Runs the objects SQLiteTables function
self.SQLiteTables()
except lite.OperationalError as err:
raise err
# Will initiate the userAccount var
self.UserInfo()
# Gets all data from the metas table from the database
self.cursor.execute("SELECT * FROM `metas`;")
# Fill the metadata var with the contents of the metas table
self.metadata = self.cursor.fetchall()
# Used to set Object variables
self.Encrypted()
self.AttachedComputers()
self.RecoveryEmail()
self.FirstName()
self.LastName()
self.DateOfBirth()
self.RecoveryPhoneNumber()
self.Extensions()
self.HTTPSites()
self.HTTPSSites()
# End __init__ ====================================
def SQLiteTables(self):
"""
Name: SQLiteTables
Description: Sets a list of the tables in the SQLite database
Input: None
Actions: Uses the sqlite_master tables to pull all tables names
Note: should always be 5 tables
deleted_metas, metas, models, share_info, share_version
"""
try:
self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
# Will error out if database is locked due to database being open (Chrome open)
except lite.OperationalError as err:
raise err
# Sets the tables to all the records in the cursor
self.tables = self.cursor.fetchall()
# Removes the tuple and returns just the names in the list
self.tables = [i[0] for i in self.tables]
# End SQLiteTables ================================
def UserInfo(self):
"""
Name: UserInfo
Description: Pulls the name and create time from the share_info table
Input: None
Actions: Pulls the name and db_create_time from the share_info table sets the out put to self.userAccount
"""
self.userAccount = []
self.cursor.execute("SELECT `name`, `db_create_time` FROM `share_info`;")
# Returns a tuple of email account and creation time in epoch
self.userAccount = self.cursor.fetchall()
# End UserInfo ====================================
def ConvertTime(self, timeInEpoch):
"""
Name: ConvertTime
Description: Converts seconds since epoch into readable format (2014-02-24 21:49:54)
Input: Epoch Time in seconds
Actions: Uses the time library to format passed epoch time
"""
return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timeInEpoch))
# End ConvertTime =================================
def AttachedComputers(self):
"""
Name: AttachedComputers
Description: Sets the computer names that the users has logged into
Input: None
Actions: Runs through each row in the metas table and adds the found computers to a list
Sets the ComputerNames list to the values from column 19 in the metadata file
"""
self.computerNames = []
for row in self.metadata:
# b'\xd2\xb9 is the signature for a computer, remove false positives that don't have enough data
if str(row[23])[:10] == "b'\\xd2\\xb9" and len(str(row[23])) > 23:
# Adds a list item to the list with the computer name in [0] and date first signed in to [1]
# Row 7 needs to be divided by 1000 because the value is stored in milliseconds since epoch
self.computerNames.append([row[18], row[7]/1000])
# End AttachedComputers ===========================
def GetUserInfo(self):
"""
Name: GetUserInfo
Description: Returns list of list, each mini list has user email and when Chrome was first signed in
Input: None
Actions: Needs UserInfo function to be run prior to get userAccount initialized
converts time stored in userAccount's list of tuples into human readable
Uses ConvertTime function
returns the new list
"""
users = []
for user in self.userAccount:
tempList = []
tempList.append(user[0])
tempList.append(self.ConvertTime(user[1]))
users.append(tempList)
return users
# End GetUserInfo =================================
def GetAttachedComputers(self):
"""
Name: GetAttachedComputers
Description: Returns list of lists, each mini list has computer string name and date account was added
Input: None
Actions: Needs AttachedComputers function to be run prior to get computerNames initialized
converts time stored in computerNames's list of tuples into human readable
Uses ConvertTime function
returns the new list
"""
computerInfo = []
for computer in self.computerNames:
tempList = []
tempList.append(computer[0])
tempList.append(self.ConvertTime(computer[1]))
computerInfo.append(tempList)
return computerInfo
# End GetAttachedComputers ========================
def RecoveryEmail(self):
"""
Name: RecoveryEmail
Description: Sets the recoveryEmail variable
Input: None
Actions: Adds recovery emails found in the metas table into the recoveryEmail list
"""
# Sets the var to false to begin with.
self.recoveryEmail = False
for row in self.metadata:
# b'\x8a\xbf\x0f5 is the signature for a recovery email
if str(row[23])[:15] == "b'\\x8a\\xbf\\x0f5":
if self.recoveryEmail:
# Adds other found email to the list
self.recoveryEmail.append(str(row[18])[36:])
else:
# Sets the first found email to the first item in the list
self.recoveryEmail = [str(row[18])[36:]]
# End RecoveryEmail ===============================
def GetRecoveryEmail(self):
"""
Name: GetRecoveryEmail
Description: Returns the recoveryEmail var
Input: None
Actions: Returns the recoveryEmail var
"""
return self.recoveryEmail
# End GetRecoveryEmail ============================
def FirstName(self):
"""
Name: FirstName
Description: Sets the firstName variable
Input: None
Actions: Sets the firstName variable to either the found name or False
"""
self.firstName = False
for row in self.metadata:
name = str(row[18])[15:24]
if name == "FirstName":
self.firstName = str(row[18][25:])
# End FirstName ===================================
def GetFirstName(self):
"""
Name: GetFirstName
Description: Returns the firstName Var
Input: None
Actions: Returns the firstName var
"""
return self.firstName
# End GetFirstName ================================
def LastName(self):
"""
Name: LastName
Description: Sets the lastName variable
Input: None
Actions: Sets the lastName variable to either the found name or False
"""
self.lastName = False
for row in self.metadata:
name = str(row[18])[15:23]
if name == "LastName":
self.lastName = str(row[18][24:])
# End LastName ====================================
def GetLastName(self):
"""
Name: GetLastName
Description: Returns the lastName var
Input: None
Actions: Returns the lastName var
"""
return self.lastName
# End GetLastName =================================
def GetFullName(self):
"""
Name: GetFullName
Description: Concatenates the first and last name
Input: None
Actions: Checks if both the first and last name have content
Concatenates the first and last name, adding a space in-between
Returns the full name or false
"""
if self.firstName and self.lastName:
return str(self.firstName + " " + self.lastName)
else:
# If the first or last name do not exits a full name can not be found and return False
return False
# End GetFullName =================================
def DateOfBirth(self):
"""
Name: DateOfBirth
Description: Sets the Date of Birth variable if data is found else sets it to False
Input: None
Actions: Sets DOB var to False
Checks to see if date can be found, if so sets the var to it
Uses dashes to indicate blanks, only used if only day or year not found
Current data suggests only day and year are stored, code reflects as such
"""
# Sets the DOB var to False
self.DOB = False
# Loops through each line in the metadata var
for row in self.metadata:
# Sets the value of row 18 to name
name = str(row[18])[15:23]
# Checks if name is BirthDay
if name == "BirthDay":
# Checks if the DOB is false or it has been initialized
if self.DOB:
# Sets the value found to temporary date var
date = str(row[18][24:])
# Checks to see if the date is a single digit
if len(date) == 1:
# Adds a 0 before the single digit
date = "0"+ date
# Adds the day to the beginning of the DOB value
self.DOB = date + self.DOB[2:]
else:
# Creates a placeholder of dashes
self.DOB = "------"
date = str(row[18][24:])
if len(date) == 1:
date = "0"+ date
self.DOB = date + self.DOB[2:]
elif name == "BirthYea":
if self.DOB:
# Preserves the set day value adds the year to the end
self.DOB = self.DOB[:2] + str(row[18][25:])
else:
self.DOB = "------"
self.DOB = self.DOB[:2] + str(row[18][25:])
# End DateOfBirth =================================
def GetFullInfo(self):
"""
Name: GetFullInfo
Description: Returns the full name and the DOB. Returns False if either of the values do not exist
Input: None
Actions: Returns the full name and the DOB in a list.
Returns False if either of the values are set to False
"""
# Returns a list inside a list for printing unification
if self.GetFullName() and self.DOB:
return [[self.GetFullName(), self.DOB]]
else:
return False
# End GetFullInfo =================================
def RecoveryPhoneNumber(self):
"""
Name: RecoveryPhoneNumber
Description: Sets recoveryPhone var
Input: None
Actions: Sets recoveryPhone var to False
Sets the var to the number if found
"""
self.recoveryPhone = False
for row in self.metadata:
name = str(row[18])[15:28]
if name == "RecoveryPhone":
self.recoveryPhone = str(row[18][35:])
# End RecoveryPhoneNumber =========================
def GetRecoveryPhone(self):
"""
Name: GetRecoveryPhone
Description: Returns recoveryPhone Var
Input: None
Actions: Returns recoveryPhone Var
"""
return self.recoveryPhone
# End GetRecoveryPhone
def Extensions(self):
"""
Name: Extensions
Description: Returns a list of all extensions found in the metas table
Input: None
Actions: Sets var to False
Adds Extension name to list if extension is found
"""
self.extension = False
for row in self.metadata:
if str(row[23])[:15] == "b'\\xba\\xbf\\x17i":
if self.extension:
self.extension.append(str(row[18]))
else:
self.extension = [str(row[18])]
# End Extensions ==================================
def GetExtensions(self):
"""
Name: GetExtensions
Description: Returns extension var
Input: None
Actions: Returns extension var
"""
return self.extension
# End GetExtensions ===============================
def Encrypted(self):
"""
Name: Encrypted
Description: Checks to see if records are encrypted
Input: None
Actions: Checks to see if metas table values are encrypted
This will limit the amount of data obtainable
Sets var to False
If encryption is found
Set encryption var to true
Report that the database is encrypted
"""
self.encrypted = False
for row in self.metadata:
if str(row[18]) == "encrypted":
self.encrypted = True
# Report using level 1 due to some information being able to be found, just not all
Report(str("NOTE: The database located at: {0} is encrypted\n".format(self.database)), 1)
break
# End Encrypted ===================================
def HTTPSites(self):
"""
Name: HTTPSites
Description: Finds all HTTP sites and adds them to a list
Input: None
Actions: Sets HTTP var to false
If http:// is found, add site to list
"""
self.http = False
for row in self.metadata:
# Program was hanging on None values
if row[18] == None:
continue
elif str(row[18][:7]).lower() == "http://":
if self.http:
# TODO when visit time is determined add this in to the append function
self.http.append(row[18])
else:
self.http = [row[18]]
# End HTTPSites ===================================
def HTTPSSites(self):
"""
Name: HTTPSSites
Description: Finds all HTTPS sites and adds them to a list
Input: None
Actions: Sets HTTPS var to false
If https:// is found, add site to list
"""
self.https = False
for row in self.metadata:
if row[18] == None:
continue
elif str(row[18][:8]).lower() == "https://":
if self.https:
# TODO when visit time is determined add this in to the append function
self.https.append(row[18])
else:
self.https = [row[18]]
# End HTTPSSites ==================================
def GetAllSites(self):
"""
Name: GetAllSites
Description: Returns all http and https sites in one list
Input: None
Actions: Checks to see if both http and https have data
Returns the new list
If only one list has data return that one
Return False if both lists are False
"""
if self.http and self.https:
return self.http + self.https
elif not self.https and not self.http:
return False
elif self.http:
return self.http
else:
return self.https
# End GetAllSites =================================
def DisplayData(data):
"""
Name: DisplayData
Description: Prints lists that has lists of pairs, second usually being a formatted date
or prints entire passed list
Input: data, either a list of lists, a list, or a string
Actions: Checks if the passed var is a list
prints the values in the list, formats them if they are tuples of two
Prints the passed data if not a list
"""
if isinstance(data, list):
for item in data:
if len(item) == 2:
Report(str(item[0].ljust(35, ":") + " " + item[1].rjust(20, ":")))
else:
Report(str(item))
else:
Report(str(data))
# End DisplayData =====================================
def Report(msg, level=False):
"""
Name: Report
Description: Custom outputter with verbosity checker, will print to screen or file depending on arguments
Input: Message which to be displayed: String
Importance level: Int
Action: Prints the msg based on the verbosity level.
If no verbosity level passed, it will print the message.
Higher levels are more important. A 1 verbosity will print everything,
a 2 will print level 2&3, a 3 will print 3
Messages with out level are Data
Level 1 messages are statuses
Level 2 messages are errors
Level 3 messages are critical errors
"""
# Checks if a level was provided
if not level:
# Determines if a output file was set
if not outFile:
# If no output file was set, print to the screen
print(msg)
else:
# If output file was set, add data to a new line in that file
outFile.write(msg+'\n')
# Makes sure importance level is higher than the verbosity filter
elif level >= verbosity:
# Prints to screen if output file was not set
if not outFile:
print(msg)
# If output file was set, add data to a new line in that file
else:
outFile.write(msg+'\n')
# End Report ==========================================
def CheckFile(filePath):
"""
Name: CheckFile
Description: Checks if the file is create, else creates it
Input: path to desired file
Actions: If the file path is a False value, return False
Output changes if file does or does not exits
Will write over the file if it exists
"""
# If the filePath is false return it
if not filePath:
return filePath
# Checks to see if the file is already existing, returns opened file
elif os.path.exists(filePath):
f = open(filePath, "w")
Report("File {0} already exists, writing over it\n".format(filePath), 1)
return f
else:
f = open(filePath, "w")
Report("File {0} does not exist, creating it\n".format(filePath), 1)
return f
# End CheckFile =======================================
def main():
# Allows modification of global variables
global verbosity
global outFile
# Parses the command line to args object
args = ParseCommandLine()
# Sets blank list of sync files, this will be used to store all the sync file objects
syncList = []
# Sets the global verbosity level to what is passed in the args
verbosity = args.verbose
# Uses the the CheckFile function to open the argument passed file if one was passed
outFile = CheckFile(args.outFile)
# Data about the program for the user
print("ChromeParser.py")
print("Created by Marley Jaffe")
print("version = 1.00")
print()
# Checks if a single database was passed to the program
if args.database:
# Will error out if the object fails to create properly
try:
syncList.append(SyncFile(args.database))
except Exception as err:
Report(err, 3)
else:
try:
syncList = GetDatabases(args.path)
except Exception as err:
Report(err, 3)
# Loops through the syncList to run commands on each database object
for syncFile in syncList:
# Displays what the database the results are from
Report("\nDatabase: {0}\n".format(syncFile.database).center(56))
# Every syncFile will have a email account associated with it
Report("Email Account".center(35, "=")+" "+"Time added".center(20, "=")+"\n")
# Display the email account and the time it was added
DisplayData(syncFile.GetUserInfo())
Report("")
# If a full name and a DOB exist print them
if syncFile.GetFullInfo():
Report("Full Name".center(35, "=")+" "+"DOB (DDYYYY)".center(20, "=")+"\n")
DisplayData(syncFile.GetFullInfo())
Report("")
else:
# Print no full info only if verbosity level is set to see statuses
Report("Full Name".center(35, "=")+" "+"DOB (DDYYYY)".center(20, "=")+"\n", 1)
Report("No full info available", 1)
Report("", 1)
# Print the computers attached to the account
Report("Computer Name".center(35, "=")+" "+"Time added".center(20, "="))
# Print how many with verbosity of 1 (status)
Report("{0} Computer(s) were synced".format(len(syncFile.GetAttachedComputers())).center(35, "_"), 1)
Report("")
DisplayData(syncFile.GetAttachedComputers())
Report("")
# If Recovery email is set, print it
if syncFile.GetRecoveryEmail():
Report("Recovery Email".center(35, "=")+"\n")
DisplayData(syncFile.GetRecoveryEmail())
Report("")
else:
# Print no recovery email, if verbosity level is 1
Report("Recovery Email".center(35, "=")+"\n", 1)
Report("No Recovery email found", 1)
Report("", 1)
# Prints phone number if one was found
if syncFile.GetRecoveryPhone():
Report("Recovery Phone".center(35, "=")+"\n")
DisplayData(syncFile.GetRecoveryPhone())
Report("")
else:
Report("Recovery Phone".center(35, "=")+"\n", 1)
Report("No Recovery phone found", 1)
Report("", 1)
# Prints extensions if any were found
if syncFile.GetExtensions():
Report("Extensions(s)".center(35, "="))
# Prints how many extensions were found with verbosity of status
Report("{0} Extensions were Found".format(len(syncFile.GetExtensions())).center(35, "_"), 1)
Report("")
DisplayData(syncFile.GetExtensions())
Report("")
else:
Report("Extensions(s)".center(35, "=")+"\n", 1)
Report("No Extensions found", 1)
Report("", 1)
# Prints if any sites were found
if syncFile.GetAllSites():
Report("All Sites".center(35, "="))
Report("{0} Sites found".format(len(syncFile.GetAllSites())).center(35, "_"), 1)
Report("")
DisplayData(syncFile.GetAllSites())
Report("")
else:
Report("All Sites".center(35, "=")+"\n", 1)
Report("No sites were found", 1)
Report("", 1)
# If an outfile was set, close it
if outFile:
outFile.close()
# Sets outFile to false for future report functions to work
outFile = False
# Print status about file closing
Report("The out file has been closed.\n", 1)
# Report that the program is finished
Report("The Program has finished. Exiting now\n", 3)
if __name__ == '__main__':
main()