/* Telegram_Backup
* Copyright (C) 2016 Fabian Schlenz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see . */
package de.fabianonline.telegram_backup
import com.github.badoualy.telegram.tl.api.*
import com.github.badoualy.telegram.tl.core.TLVector
import com.github.badoualy.telegram.api.TelegramClient
import org.slf4j.LoggerFactory
import org.slf4j.Logger
import java.sql.Connection
import java.sql.DriverManager
import java.sql.Statement
import java.sql.SQLException
import java.sql.ResultSet
import java.sql.ResultSetMetaData
import java.sql.PreparedStatement
import java.sql.Types
import java.sql.Time
import java.io.File
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.util.LinkedList
import java.util.LinkedHashMap
import java.util.HashMap
import java.util.Date
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.nio.file.FileAlreadyExistsException
import java.text.SimpleDateFormat
import de.fabianonline.telegram_backup.mediafilemanager.AbstractMediaFileManager
import de.fabianonline.telegram_backup.mediafilemanager.FileManagerFactory
class Database constructor(val file_base: String, val user_manager: UserManager) {
val conn: Connection
val stmt: Statement
val logger = LoggerFactory.getLogger(Database::class.java)
init {
println("Opening database...")
try {
Class.forName("org.sqlite.JDBC")
} catch (e: ClassNotFoundException) {
throw RuntimeException("Could not load jdbc-sqlite class.")
}
val path = "jdbc:sqlite:${file_base}${Config.FILE_NAME_DB}"
try {
conn = DriverManager.getConnection(path)!!
stmt = conn.createStatement()
} catch (e: SQLException) {
throw RuntimeException("Could not connect to SQLITE database.")
}
// Run updates
val updates = DatabaseUpdates(conn, this)
updates.doUpdates()
println("Database is ready.")
}
fun getTopMessageID(): Int = queryInt("SELECT MAX(message_id) FROM messages WHERE source_type IN ('group', 'dialog')")
fun getMessageCount(): Int = queryInt("SELECT COUNT(*) FROM messages")
fun getChatCount(): Int = queryInt("SELECT COUNT(*) FROM chats")
fun getUserCount(): Int = queryInt("SELECT COUNT(*) FROM users")
fun getMissingIDs(): LinkedList {
try {
val missing = LinkedList()
val max = getTopMessageID()
val rs = stmt.executeQuery("SELECT message_id FROM messages WHERE source_type IN ('group', 'dialog') ORDER BY id")
rs.next()
var id = rs.getInt(1)
for (i in 1..max) {
if (i == id) {
rs.next()
if (rs.isClosed()) {
id = Integer.MAX_VALUE
} else {
id = rs.getInt(1)
}
} else if (i < id) {
missing.add(i)
}
}
rs.close()
return missing
} catch (e: SQLException) {
e.printStackTrace()
throw RuntimeException("Could not get list of ids.")
}
}
fun getMessagesWithMedia(): LinkedList {
try {
val list = LinkedList()
val rs = stmt.executeQuery("SELECT data FROM messages WHERE has_media=1")
while (rs.next()) {
list.add(bytesToTLMessage(rs.getBytes(1)))
}
rs.close()
return list
} catch (e: Exception) {
e.printStackTrace()
throw RuntimeException("Exception occured. See above.")
}
}
fun getMessagesFromUserCount() = queryInt("SELECT COUNT(*) FROM messages WHERE sender_id=" + user_manager.id)
fun getMessageTypesWithCount(): HashMap = getMessageTypesWithCount(GlobalChat())
fun getMessageMediaTypesWithCount(): HashMap = getMessageMediaTypesWithCount(GlobalChat())
fun getMessageSourceTypeWithCount(): HashMap {
val map = HashMap()
try {
val rs = stmt.executeQuery("SELECT COUNT(id), source_type FROM messages GROUP BY source_type ORDER BY source_type")
while (rs.next()) {
val source_type = rs.getString(2) ?: "null"
map.put("count.messages.source_type.${source_type}", rs.getInt(1))
}
rs.close()
return map
} catch (e:Exception) {
throw RuntimeException(e)
}
}
fun getMessageApiLayerWithCount(): HashMap {
val map = HashMap()
try {
val rs = stmt.executeQuery("SELECT COUNT(id), api_layer FROM messages GROUP BY api_layer ORDER BY api_layer")
while (rs.next()) {
var layer = rs.getInt(2)
map.put("count.messages.api_layer.$layer", rs.getInt(1))
}
rs.close()
return map
} catch (e: Exception) {
throw RuntimeException(e)
}
}
fun getMessageAuthorsWithCount(): HashMap = getMessageAuthorsWithCount(GlobalChat())
fun getMessageTimesMatrix(): Array = getMessageTimesMatrix(GlobalChat())
fun getEncoding(): String = queryString("PRAGMA encoding")
fun getListOfChatsForExport(): LinkedList {
val list = LinkedList()
val rs = stmt.executeQuery("SELECT chats.id, chats.name, COUNT(messages.id) as c " +
"FROM chats, messages WHERE messages.source_type IN('group', 'supergroup', 'channel') AND messages.source_id=chats.id " +
"GROUP BY chats.id ORDER BY c DESC")
while (rs.next()) {
list.add(Chat(rs.getInt(1), rs.getString(2), rs.getInt(3)))
}
rs.close()
return list
}
fun getListOfDialogsForExport(): LinkedList