def __init__(self, cursor, path, daemon = False): QtGui.QWidget.__init__(self) self.ui = Ui_SMS() self.ui.setupUi(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.cursor = cursor self.backup_path = path self.cursor = cursor self.filename = os.path.join(self.backup_path, plugins_utils.realFileName(cursor, filename="sms.db", domaintype="HomeDomain")) # check if files exist if (not os.path.isfile(self.filename)): raise Exception("Messages database not found: \"%s\""%self.filename) if (daemon == False): self.populateUI() QtCore.QObject.connect(self.ui.threadsTree, QtCore.SIGNAL("itemSelectionChanged()"), self.onTreeClick) self.ui.threadsTree.setColumnHidden(0,True) # attach context menu to rightclick on message attachment self.ui.messageTable.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.connect(self.ui.messageTable, QtCore.SIGNAL('customContextMenuRequested(QPoint)'), self.ctxMenu) # search label self.connect(self.ui.searchLabel, QtCore.SIGNAL('textChanged(QString)'), self.search)
def __init__(self, cursor, path, daemon=False): QtGui.QWidget.__init__(self) self.ui = Ui_SMS() self.ui.setupUi(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.cursor = cursor self.backup_path = path self.cursor = cursor self.filename = os.path.join( self.backup_path, plugins_utils.realFileName(cursor, filename="sms.db", domaintype="HomeDomain")) # check if files exist if (not os.path.isfile(self.filename)): raise Exception("Messages database not found: \"%s\"" % self.filename) if (daemon == False): self.populateUI() QtCore.QObject.connect(self.ui.threadsTree, QtCore.SIGNAL("itemSelectionChanged()"), self.onTreeClick) self.ui.threadsTree.setColumnHidden(0, True) # attach context menu to rightclick on message attachment self.ui.messageTable.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.connect(self.ui.messageTable, QtCore.SIGNAL('customContextMenuRequested(QPoint)'), self.ctxMenu) # search label self.connect(self.ui.searchLabel, QtCore.SIGNAL('textChanged(QString)'), self.search)
class SMSWidget(QtGui.QWidget): def __init__(self, cursor, path, daemon=False): QtGui.QWidget.__init__(self) self.ui = Ui_SMS() self.ui.setupUi(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.cursor = cursor self.backup_path = path self.cursor = cursor self.filename = os.path.join( self.backup_path, plugins_utils.realFileName(cursor, filename="sms.db", domaintype="HomeDomain")) # check if files exist if (not os.path.isfile(self.filename)): raise Exception("Messages database not found: \"%s\"" % self.filename) if (daemon == False): self.populateUI() QtCore.QObject.connect(self.ui.threadsTree, QtCore.SIGNAL("itemSelectionChanged()"), self.onTreeClick) self.ui.threadsTree.setColumnHidden(0, True) # attach context menu to rightclick on message attachment self.ui.messageTable.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.connect(self.ui.messageTable, QtCore.SIGNAL('customContextMenuRequested(QPoint)'), self.ctxMenu) # search label self.connect(self.ui.searchLabel, QtCore.SIGNAL('textChanged(QString)'), self.search) def populateUI(self): self.ui.threadsTree.setColumnWidth(1, 50) # opening database tempdb = sqlite3.connect(self.filename) tempdb.row_factory = sqlite3.Row tempcur = tempdb.cursor() # populating tree with SMS groups query = "SELECT ROWID, chat_identifier, service_name FROM chat;" tempcur.execute(query) groups = tempcur.fetchall() for group in groups: groupid = group['ROWID'] address = group['chat_identifier'].replace(' ', '') newElement = QtGui.QTreeWidgetItem(None) newElement.setText(0, str(groupid)) newElement.setText(1, str(group['service_name'])) newElement.setText(2, address) self.ui.threadsTree.addTopLevelItem(newElement) statistics = [ ["Messages from last reset:"], ["Incoming", "counter_in_all"], ["Outgoing", "counter_out_all"], ["Lifetime messages:"], ["Incoming", "counter_in_lifetime"], ["outgoing", "counter_out_lifetime"], ["Counter:"], ["Last reset", "counter_last_reset"], ] for statistic in statistics: if (len(statistic) == 1): self.ui.dataText.append("<strong>%s</strong>" % (statistic[0])) else: text = statistic[0] key = statistic[1] query = "SELECT value FROM _SqliteDatabaseProperties WHERE key = \"%s\"" % key tempcur.execute(query) data = tempcur.fetchall() if (len(data) > 0): value = data[0][0] self.ui.dataText.append("%s: <strong>%s</strong>" % (text, value)) # closing database tempdb.close() def search(self, text): allItems = self.ui.threadsTree.findItems( "", QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive, 2) matching = self.ui.threadsTree.findItems( text, QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive, 2) for element in allItems: element.setHidden(True) self.ui.messageTable.clear() first = True for element in matching: element.setHidden(False) if (first): first = False self.ui.threadsTree.setCurrentItem(None) self.ui.threadsTree.setCurrentItem(element) if (len(matching) == 0): self.ui.threadsTree.setCurrentItem(None) def ctxMenu(self, pos): cell = self.ui.messageTable.itemAt(pos) self.link = cell.data(QtCore.Qt.UserRole) self.name = cell.data(QtCore.Qt.UserRole + 1) if (self.link != None): menu = QtGui.QMenu() action1 = QtGui.QAction("Open in standard viewer", self) action1.triggered.connect(self.openWithViewer) menu.addAction(action1) action1 = QtGui.QAction("Export", self) action1.triggered.connect(self.exportSelectedFile) menu.addAction(action1) menu.exec_(self.ui.messageTable.mapToGlobal(pos)) def openWithViewer(self): if sys.platform.startswith('linux'): subprocess.call(["xdg-open", self.link]) else: os.startfile(self.link) def exportSelectedFile(self): filename = QtGui.QFileDialog.getSaveFileName(self, "Export attachment", self.name) filename = filename[0] if (len(filename) == 0): return try: shutil.copy(self.link, filename) QtGui.QMessageBox.about(self, "Confirm", "Attachment saved as %s." % filename) except: QtGui.QMessageBox.about(self, "Error", "Error while saving attachment") def onTreeClick(self): # retrieving selected network currentSelectedElement = self.ui.threadsTree.currentItem() if (currentSelectedElement): pass else: return currentChat = int(currentSelectedElement.text(0)) self.ui.threadLabel.setText(currentSelectedElement.text(1)) # opening database tempdb = sqlite3.connect(self.filename) tempdb.row_factory = sqlite3.Row tempcur = tempdb.cursor() query = 'SELECT ROWID, text, date, is_from_me, cache_has_attachments, service FROM message INNER JOIN chat_message_join ON message.ROWID = chat_message_join.message_id WHERE chat_id = ?;' tempcur.execute(query, (currentChat, )) messages = tempcur.fetchall() self.ui.messageTable.clear() # prepare table with enough rows # each message is a row, but each attachment also counts as one # so, we make an educated guess maxRows = len(messages) * 3 if (maxRows < 100): maxRows += 300 self.ui.messageTable.setRowCount(maxRows) self.ui.messageTable.setColumnCount(2) self.ui.messageTable.setHorizontalHeaderLabels(["Date", "Text"]) row = 0 lastDate = "" for message in messages: documentTimestampUnix = message['date'] + 978307200 #JAN 1 1970 documentTimestamp = datetime.fromtimestamp( documentTimestampUnix).strftime('%Y-%m-%d %H:%M:%S') # separator on date change actualDate = datetime.fromtimestamp( documentTimestampUnix).strftime("%Y-%m-%d") if (actualDate != lastDate): lastDate = actualDate newItem = QtGui.QTableWidgetItem(actualDate) newItem.setBackground(QtCore.Qt.yellow) self.ui.messageTable.setItem(row, 0, newItem) newItem = QtGui.QTableWidgetItem() #newItem.setBackground(QtCore.Qt.yellow) self.ui.messageTable.setItem(row, 1, newItem) row += 1 if (message['is_from_me'] == 1): documentTimestamp = "Sent on:\n" + documentTimestamp else: documentTimestamp = "Received on:\n" + documentTimestamp newItem = QtGui.QTableWidgetItem(documentTimestamp) self.ui.messageTable.setItem(row, 0, newItem) newItem = QtGui.QTableWidgetItem(message['text']) if (message['is_from_me'] == 1): if (message['service'] == "SMS"): newItem.setBackground(QtCore.Qt.green) else: newItem.setBackground(QtCore.Qt.cyan) else: newItem.setBackground(QtCore.Qt.gray) self.ui.messageTable.setItem(row, 1, newItem) row += 1 if (message['cache_has_attachments'] == 1): query = 'SELECT ROWID, filename, mime_type FROM attachment INNER JOIN message_attachment_join ON message_attachment_join.attachment_id = attachment.ROWID WHERE message_attachment_join.message_id = ?;' tempcur.execute(query, (message['ROWID'], )) for attachment in tempcur.fetchall(): attachmentFileName = attachment['filename'] attachmentType = attachment['mime_type'] # seems paths have changed from iOS 5 to iOS 6, must find # path from "Library" onwards attachmentPathParts = os.path.dirname( attachmentFileName).split("/") libraryPosition = 0 index = 0 for element in attachmentPathParts: if (element == "Library"): libraryPosition = index break else: index += 1 attachmentPath = "/".join( os.path.dirname(attachmentFileName).split("/") [libraryPosition:]) attachmentName = os.path.basename(attachmentFileName) attachmentRealFilename = os.path.join( self.backup_path, plugins_utils.realFileName(self.cursor, filename=attachmentName, path=attachmentPath, domaintype="MediaDomain")) if (not os.path.isfile(attachmentRealFilename)): newItem = QtGui.QTableWidgetItem( "Attached file %s (id: %i) not found." % (attachmentFileName, attachment['ROWID'])) else: if (attachmentType.split("/")[0] == "image"): newItem = QtGui.QTableWidgetItem() icon = QtGui.QIcon(attachmentRealFilename) newItem.setIcon(icon) else: newItem = QtGui.QTableWidgetItem( "Right click to open attached file\n%s (%s)" % (attachmentName, attachment['mime_type'])) newItem.setForeground(QtCore.Qt.red) newItem.setData(QtCore.Qt.UserRole, attachmentRealFilename) newItem.setData(QtCore.Qt.UserRole + 1, attachmentName) if (message['is_from_me'] == 1): if (message['service'] == "SMS"): newItem.setBackground(QtCore.Qt.green) else: newItem.setBackground(QtCore.Qt.cyan) else: newItem.setBackground(QtCore.Qt.gray) self.ui.messageTable.setItem(row, 1, newItem) row += 1 self.ui.messageTable.setRowCount(row) self.ui.messageTable.setIconSize(QtCore.QSize(200, 200)) self.ui.messageTable.resizeColumnsToContents() self.ui.messageTable.setColumnWidth(1, 200) self.ui.messageTable.horizontalHeader().setStretchLastSection(True) self.ui.messageTable.resizeRowsToContents() # closing database tempdb.close()
class SMSWidget(QtGui.QWidget): def __init__(self, cursor, path, daemon = False): QtGui.QWidget.__init__(self) self.ui = Ui_SMS() self.ui.setupUi(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.cursor = cursor self.backup_path = path self.cursor = cursor self.filename = os.path.join(self.backup_path, plugins_utils.realFileName(cursor, filename="sms.db", domaintype="HomeDomain")) # check if files exist if (not os.path.isfile(self.filename)): raise Exception("Messages database not found: \"%s\""%self.filename) if (daemon == False): self.populateUI() QtCore.QObject.connect(self.ui.threadsTree, QtCore.SIGNAL("itemSelectionChanged()"), self.onTreeClick) self.ui.threadsTree.setColumnHidden(0,True) # attach context menu to rightclick on message attachment self.ui.messageTable.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.connect(self.ui.messageTable, QtCore.SIGNAL('customContextMenuRequested(QPoint)'), self.ctxMenu) # search label self.connect(self.ui.searchLabel, QtCore.SIGNAL('textChanged(QString)'), self.search) def populateUI(self): self.ui.threadsTree.setColumnWidth(1, 50) # opening database tempdb = sqlite3.connect(self.filename) tempdb.row_factory = sqlite3.Row tempcur = tempdb.cursor() # populating tree with SMS groups query = "SELECT ROWID, chat_identifier, service_name FROM chat;" tempcur.execute(query) groups = tempcur.fetchall() for group in groups: groupid = group['ROWID'] address = group['chat_identifier'].replace(' ', '') newElement = QtGui.QTreeWidgetItem(None) newElement.setText(0, str(groupid)) newElement.setText(1, str(group['service_name'])) newElement.setText(2, address) self.ui.threadsTree.addTopLevelItem(newElement) statistics = [ ["Messages from last reset:"], ["Incoming", "counter_in_all"], ["Outgoing", "counter_out_all"], ["Lifetime messages:"], ["Incoming", "counter_in_lifetime"], ["outgoing", "counter_out_lifetime"], ["Counter:"], ["Last reset", "counter_last_reset"], ] for statistic in statistics: if (len(statistic) == 1): self.ui.dataText.append("<strong>%s</strong>"%(statistic[0])) else: text = statistic[0] key = statistic[1] query = "SELECT value FROM _SqliteDatabaseProperties WHERE key = \"%s\""%key tempcur.execute(query) data = tempcur.fetchall() if (len(data) > 0): value = data[0][0] self.ui.dataText.append("%s: <strong>%s</strong>"%(text, value)) # closing database tempdb.close() def search(self, text): allItems = self.ui.threadsTree.findItems("", QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive, 2) matching = self.ui.threadsTree.findItems(text, QtCore.Qt.MatchContains | QtCore.Qt.MatchRecursive, 2) for element in allItems: element.setHidden(True) self.ui.messageTable.clear() first = True for element in matching: element.setHidden(False) if (first): first = False self.ui.threadsTree.setCurrentItem(None) self.ui.threadsTree.setCurrentItem(element) if (len(matching) == 0): self.ui.threadsTree.setCurrentItem(None) def ctxMenu(self, pos): cell = self.ui.messageTable.itemAt(pos) self.link = cell.data(QtCore.Qt.UserRole) self.name = cell.data(QtCore.Qt.UserRole + 1) if (self.link != None): menu = QtGui.QMenu() action1 = QtGui.QAction("Open in standard viewer", self) action1.triggered.connect(self.openWithViewer) menu.addAction(action1) action1 = QtGui.QAction("Export", self) action1.triggered.connect(self.exportSelectedFile) menu.addAction(action1) menu.exec_(self.ui.messageTable.mapToGlobal(pos)); def openWithViewer(self): if sys.platform.startswith('linux'): subprocess.call(["xdg-open", self.link]) else: os.startfile(self.link) def exportSelectedFile(self): filename = QtGui.QFileDialog.getSaveFileName(self, "Export attachment", self.name) filename = filename[0] if (len(filename) == 0): return try: shutil.copy(self.link, filename) QtGui.QMessageBox.about(self, "Confirm", "Attachment saved as %s."%filename) except: QtGui.QMessageBox.about(self, "Error", "Error while saving attachment") def onTreeClick(self): # retrieving selected network currentSelectedElement = self.ui.threadsTree.currentItem() if (currentSelectedElement): pass else: return currentChat = int(currentSelectedElement.text(0)) self.ui.threadLabel.setText(currentSelectedElement.text(1)) # opening database tempdb = sqlite3.connect(self.filename) tempdb.row_factory = sqlite3.Row tempcur = tempdb.cursor() query = 'SELECT ROWID, text, date, is_from_me, cache_has_attachments, service FROM message INNER JOIN chat_message_join ON message.ROWID = chat_message_join.message_id WHERE chat_id = ?;' tempcur.execute(query, (currentChat,)) messages = tempcur.fetchall() self.ui.messageTable.clear() # prepare table with enough rows # each message is a row, but each attachment also counts as one # so, we make an educated guess maxRows = len(messages) * 3 if (maxRows < 100): maxRows += 300 self.ui.messageTable.setRowCount(maxRows) self.ui.messageTable.setColumnCount(2) self.ui.messageTable.setHorizontalHeaderLabels(["Date", "Text"]) row = 0 lastDate = "" for message in messages: documentTimestampUnix = message['date'] + 978307200 #JAN 1 1970 documentTimestamp = datetime.fromtimestamp(documentTimestampUnix).strftime('%Y-%m-%d %H:%M:%S') # separator on date change actualDate = datetime.fromtimestamp(documentTimestampUnix).strftime("%Y-%m-%d") if (actualDate != lastDate): lastDate = actualDate newItem = QtGui.QTableWidgetItem(actualDate) newItem.setBackground(QtCore.Qt.yellow) self.ui.messageTable.setItem(row, 0, newItem) newItem = QtGui.QTableWidgetItem() #newItem.setBackground(QtCore.Qt.yellow) self.ui.messageTable.setItem(row, 1, newItem) row += 1 if (message['is_from_me'] == 1): documentTimestamp = "Sent on:\n" + documentTimestamp else: documentTimestamp = "Received on:\n" + documentTimestamp newItem = QtGui.QTableWidgetItem(documentTimestamp) self.ui.messageTable.setItem(row, 0, newItem) newItem = QtGui.QTableWidgetItem(message['text']) if (message['is_from_me'] == 1): if (message['service'] == "SMS"): newItem.setBackground(QtCore.Qt.green) else: newItem.setBackground(QtCore.Qt.cyan) else: newItem.setBackground(QtCore.Qt.gray) self.ui.messageTable.setItem(row, 1, newItem) row += 1 if (message['cache_has_attachments'] == 1): query = 'SELECT ROWID, filename, mime_type FROM attachment INNER JOIN message_attachment_join ON message_attachment_join.attachment_id = attachment.ROWID WHERE message_attachment_join.message_id = ?;' tempcur.execute(query, (message['ROWID'], )) for attachment in tempcur.fetchall(): attachmentFileName = attachment['filename'] attachmentType = attachment['mime_type'] # seems paths have changed from iOS 5 to iOS 6, must find # path from "Library" onwards attachmentPathParts = os.path.dirname(attachmentFileName).split("/") libraryPosition = 0 index = 0 for element in attachmentPathParts: if (element == "Library"): libraryPosition = index break else: index += 1 attachmentPath = "/".join(os.path.dirname(attachmentFileName).split("/")[libraryPosition:]) attachmentName = os.path.basename(attachmentFileName) attachmentRealFilename = os.path.join(self.backup_path, plugins_utils.realFileName(self.cursor, filename=attachmentName, path=attachmentPath, domaintype="MediaDomain")) if (not os.path.isfile(attachmentRealFilename)): newItem = QtGui.QTableWidgetItem("Attached file %s (id: %i) not found."%(attachmentFileName, attachment['ROWID'])) else: if (attachmentType.split("/")[0] == "image"): newItem = QtGui.QTableWidgetItem() icon = QtGui.QIcon(attachmentRealFilename) newItem.setIcon(icon) else: newItem = QtGui.QTableWidgetItem("Right click to open attached file\n%s (%s)"%(attachmentName, attachment['mime_type'])) newItem.setForeground(QtCore.Qt.red) newItem.setData(QtCore.Qt.UserRole, attachmentRealFilename) newItem.setData(QtCore.Qt.UserRole + 1, attachmentName) if (message['is_from_me'] == 1): if (message['service'] == "SMS"): newItem.setBackground(QtCore.Qt.green) else: newItem.setBackground(QtCore.Qt.cyan) else: newItem.setBackground(QtCore.Qt.gray) self.ui.messageTable.setItem(row, 1, newItem) row += 1 self.ui.messageTable.setRowCount(row) self.ui.messageTable.setIconSize(QtCore.QSize(200,200)) self.ui.messageTable.resizeColumnsToContents() self.ui.messageTable.setColumnWidth(1, 200) self.ui.messageTable.horizontalHeader().setStretchLastSection(True) self.ui.messageTable.resizeRowsToContents() # closing database tempdb.close()