init checkin
This commit is contained in:
commit
bd7301eae5
|
@ -0,0 +1,44 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
|
@ -0,0 +1,210 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
import re
|
||||
import json
|
||||
import sqlite3
|
||||
|
||||
delimeter = "|"
|
||||
|
||||
|
||||
def scanDB(filein):
|
||||
# try to open sqlite file
|
||||
try:
|
||||
(conn, cur) = __openDB(filein)
|
||||
except Exception, e:
|
||||
return -2
|
||||
|
||||
# read database schema
|
||||
try:
|
||||
dbSchema = DBSchema()
|
||||
dbSchema.readDatabase(cur)
|
||||
except Exception, e:
|
||||
print e
|
||||
return -3
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def __openDB(filein):
|
||||
conn = sqlite3.connect(filein)
|
||||
cur = conn.cursor()
|
||||
return (conn, cur)
|
||||
|
||||
|
||||
def writeFingerprint():
|
||||
return
|
||||
|
||||
|
||||
def printDB():
|
||||
return
|
||||
|
||||
def getErrorString(errorCode):
|
||||
retval = "ERROR: unknown error code: " + str(errorCode)
|
||||
if (errorCode == -2):
|
||||
retval = "ERROR: problem opening file, or not sqlite database"
|
||||
elif (errorCode == -3):
|
||||
retval = "ERROR: problem reading database"
|
||||
return retval
|
||||
|
||||
|
||||
#
|
||||
# Database Schema
|
||||
#
|
||||
class DBSchema:
|
||||
"""
|
||||
This class represents a complete database schema
|
||||
"""
|
||||
|
||||
sqlmaster = "SELECT name, sql FROM sqlite_master WHERE type='table'"
|
||||
|
||||
def __init__(self):
|
||||
self.tableNames = []
|
||||
self.tables = {}
|
||||
return
|
||||
|
||||
def readDatabase(self, cur):
|
||||
for row in cur.execute(self.sqlmaster):
|
||||
newTable = TableDefinition()
|
||||
newTable.loadTable(row[0], row[1])
|
||||
self.tableNames.append(newTable.name())
|
||||
self.tables[newTable.name()] = newTable
|
||||
return
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
class TableDefinition:
|
||||
"""
|
||||
This class represents the definition of database table
|
||||
"""
|
||||
|
||||
tableschemaregex = r'\((.*)\)'
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.tableName = ""
|
||||
self.sqlStr = ""
|
||||
self.fields = {}
|
||||
# self.pkeys = []
|
||||
return
|
||||
|
||||
def loadTable(self, tableName, sqlStr):
|
||||
self.tableName = tableName
|
||||
self.sqlStr = sqlStr
|
||||
results = re.search(self.tableschemaregex, sqlStr)
|
||||
if results:
|
||||
colstr = results.group(1)
|
||||
print "[[ TABLE: <" + tableName + "> ]]"
|
||||
print "FIELDS: " + colstr
|
||||
columns = colstr.split(',')
|
||||
for col in columns:
|
||||
newField = self.__parseCreateStr(col.strip())
|
||||
if newField:
|
||||
self.fields[newField['name']] = newField
|
||||
|
||||
return
|
||||
|
||||
|
||||
# Table Definition
|
||||
#
|
||||
# CREATE TABLE contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT,name_raw_contact_id INTEGER REFERENCES raw_contacts(_id),
|
||||
# photo_id INTEGER REFERENCES data(_id),photo_file_id INTEGER REFERENCES photo_files(_id),
|
||||
# custom_ringtone TEXT,send_to_voicemail INTEGER NOT NULL DEFAULT 0,
|
||||
# times_contacted INTEGER NOT NULL DEFAULT 0,last_time_contacted INTEGER,
|
||||
# starred INTEGER NOT NULL DEFAULT 0,pinned INTEGER NOT NULL DEFAULT 2147483647,
|
||||
# has_phone_number INTEGER NOT NULL DEFAULT 0,lookup TEXT,
|
||||
# status_update_id INTEGER REFERENCES data(_id),contact_last_updated_timestamp INTEGER)
|
||||
#
|
||||
# CREATE TABLE sent_files_v2 (uid INTEGER, phone TEXT, sphone TEXT, deleted INTEGER,
|
||||
# PRIMARY KEY (uid, phone)
|
||||
def __parseCreateStr(self, sqltext):
|
||||
try:
|
||||
newField = {}
|
||||
# photo_id INTEGER REFERENCES data(_id)
|
||||
# name_raw_contact_id INTEGER REFERENCES raw_contacts(_id)
|
||||
results = re.match(r'(\w+)\s+(\w+)\s+REFERENCESS\s+(\W+)', sqltext)
|
||||
if results:
|
||||
newField['name'] = results.group(1)
|
||||
newField['datatype'] = results.group(2)
|
||||
newField['references'] = 1
|
||||
newField['referencesdata'] = results.group(3)
|
||||
return newField
|
||||
# pinned INTEGER NOT NULL DEFAULT 2147483647
|
||||
# send_to_voicemail INTEGER NOT NULL DEFAULT 0
|
||||
results = re.match(r'(\w+)\s+(\w+)\s+NOT NULL\s+DEFAULT\s+(\w+)', sqltext)
|
||||
if results:
|
||||
newField['name'] = results.group(1)
|
||||
newField['datatype'] = results.group(2)
|
||||
newField['notnull'] = True
|
||||
newField['default'] = results.group(3)
|
||||
return newField
|
||||
# _id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
results = re.match(r'(\w+)\s+(\w+)\s+PRIMARY KEY\s+AUTOINCREMENT', sqltext)
|
||||
if results:
|
||||
newField['name'] = results.group(1)
|
||||
newField['datatype'] = results.group(2)
|
||||
newField['primarykey'] = True
|
||||
newField['autoincrement'] = True
|
||||
return newField
|
||||
# FileID INTEGER NOT NULL
|
||||
results = re.match(r'(\w+)\s+(\w+)\s+NOT NULL', sqltext)
|
||||
if results:
|
||||
newField['name'] = results.group(1)
|
||||
newField['datatype'] = results.group(2)
|
||||
newField['notnull'] = True
|
||||
return newField
|
||||
# custom_ringtone TEXT
|
||||
results = re.match(r'(\w+)\s+(\w+)', sqltext)
|
||||
if results:
|
||||
newField['name'] = results.group(1)
|
||||
newField['datatype'] = results.group(2)
|
||||
return newField
|
||||
# PRIMARY KEY (field_name,
|
||||
results = re.match(r'PRIMARY KEY \((\w+)\,', sqltext)
|
||||
if results:
|
||||
field = self.fields[results.group(1)]
|
||||
field['primarykey'] = True
|
||||
return False
|
||||
# field_name)
|
||||
results = re.match(r'(\w+)\)', sqltext)
|
||||
if results:
|
||||
field = self.fields[results.group(1)]
|
||||
field['primarykey'] = True
|
||||
return False
|
||||
|
||||
print 'INFO: field definition not recognized: "' + sqltext + '"'
|
||||
# photo_id INTEGER REFERENCES data(_id)
|
||||
# results = re.match(r'', sqltext)
|
||||
# if results:
|
||||
# newField['name'] = results.group(1)
|
||||
# newField['datatype'] = results.group(2)
|
||||
# return newField
|
||||
except Exception, e:
|
||||
return None
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def toJSON(self):
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
global delimeter
|
||||
retstr = ""
|
||||
|
||||
return retstr
|
||||
|
||||
def name(self):
|
||||
return self.tableName
|
||||
|
||||
|
||||
|
||||
class TableField:
|
||||
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
#
|
||||
#
|
||||
import argparse
|
||||
import time
|
||||
|
||||
from libs import fingerprint
|
||||
|
||||
|
||||
def main():
|
||||
(filein, verbose) = parseArgs()
|
||||
retVal = fingerprint.scanDB(filein)
|
||||
if (retVal > 0):
|
||||
fingerprint.writeFingerprint()
|
||||
else:
|
||||
print fingerprint.getErrorString(retVal)
|
||||
|
||||
|
||||
def parseArgs():
|
||||
verbose = False
|
||||
timestr = time.strftime('%Y-%m-%d_%H%M%S', time.localtime(time.time()))
|
||||
#outfile = "telegram-data_" + timestr
|
||||
parser = argparse.ArgumentParser(description='Fingerprint a sqlite database based on its schema')
|
||||
parser.add_argument('-f', '--file', required=True)
|
||||
parser.add_argument('-v', '--verbose', action='store_true')
|
||||
args = parser.parse_args()
|
||||
if (args.verbose):
|
||||
verbose = args.verbose
|
||||
return (args.file, verbose)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue