WIP: Started rewriting the code to make it more null-safe.

This commit is contained in:
Fabian Schlenz 2018-03-14 06:08:07 +01:00
parent 6276651b84
commit 97cf26b46d
5 changed files with 125 additions and 122 deletions

View File

@ -0,0 +1,5 @@
package de.fabianonline.telegram_backup
class Account(val file_base: String, val phone_number: String) {
}

View File

@ -31,7 +31,11 @@ import org.slf4j.Logger
class CommandLineController { class CommandLineController {
private val storage: ApiStorage private val storage: ApiStorage
var app: TelegramApp val app: TelegramApp
val target_dir: String
val file_base: String
val phone_number: String
val account_file_base: String
private fun getLine(): String { private fun getLine(): String {
if (System.console() != null) { if (System.console() != null) {
@ -53,25 +57,45 @@ class CommandLineController {
init { init {
logger.info("CommandLineController started. App version {}", Config.APP_APPVER) logger.info("CommandLineController started. App version {}", Config.APP_APPVER)
this.printHeader() printHeader()
if (CommandLineOptions.cmd_version) { if (CommandLineOptions.cmd_version) {
System.exit(0) System.exit(0)
} else if (CommandLineOptions.cmd_help) { } else if (CommandLineOptions.cmd_help) {
this.show_help() show_help()
System.exit(0) System.exit(0)
} else if (CommandLineOptions.cmd_license) { } else if (CommandLineOptions.cmd_license) {
CommandLineController.show_license() show_license()
System.exit(0) System.exit(0)
} }
this.setupFileBase()
// Setup file_base
logger.debug("Target dir from Config: {}", Config.TARGET_DIR.anonymize())
target_dir = CommandLineOptions.val_target ?: Config.TARGET_DIR
logger.debug("Target dir after options: {}", target_dir)
println("Base directory for files: ${target_dir.anonymize()}")
if (CommandLineOptions.cmd_list_accounts) { if (CommandLineOptions.cmd_list_accounts) {
this.list_accounts() Utils.print_accounts(target_dir)
System.exit(0) System.exit(0)
} }
logger.trace("Checking accounts")
try {
phone_number = selectAccount(target_dir, CommandLineOptions.val_account)
} catch(e: AccountNotFoundException) {
show_error("The specified account could not be found.")
} catch(e: NoAccountsException) {
println("No accounts found. Starting login process...")
CommandLineOptions.cmd_login = true
}
file_base = target_dir + File.separatorChar + account_to_use
account = Account(file_base, account_to_use)
logger.debug("Initializing TelegramApp") logger.debug("Initializing TelegramApp")
app = TelegramApp(Config.APP_ID, Config.APP_HASH, Config.APP_MODEL, Config.APP_SYSVER, Config.APP_APPVER, Config.APP_LANG) app = TelegramApp(Config.APP_ID, Config.APP_HASH, Config.APP_MODEL, Config.APP_SYSVER, Config.APP_APPVER, Config.APP_LANG)
logger.trace("Checking accounts")
val account = this.selectAccount()
logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login) logger.debug("CommandLineOptions.cmd_login: {}", CommandLineOptions.cmd_login)
logger.info("Initializing ApiStorage") logger.info("Initializing ApiStorage")
storage = ApiStorage(account) storage = ApiStorage(account)
@ -196,44 +220,22 @@ class CommandLineController {
println() println()
} }
private fun setupFileBase() { private fun selectAccount(file_base: String, requested_account: String?): String {
logger.debug("Target dir at startup: {}", Config.FILE_BASE.anonymize()) val found_account: String?
if (CommandLineOptions.val_target != null) { val accounts = Utils.getAccounts(file_base)
Config.FILE_BASE = CommandLineOptions.val_target!! if (requested_account != null) {
} logger.debug("Account requested: {}", requested_account.anonymize())
logger.debug("Target dir after options: {}", Config.FILE_BASE.anonymize())
System.out.println("Base directory for files: " + Config.FILE_BASE.anonymize())
}
private fun selectAccount(): String? {
var account = "none"
val accounts = Utils.getAccounts()
if (CommandLineOptions.cmd_login) {
logger.debug("Login requested, doing nothing.")
// do nothing
} else if (CommandLineOptions.val_account != null) {
logger.debug("Account requested: {}", CommandLineOptions.val_account!!.anonymize())
logger.trace("Checking accounts for match.") logger.trace("Checking accounts for match.")
var found = false found_account = accounts.find{it == requested_account}
for (acc in accounts) {
logger.trace("Checking {}", acc.anonymize()) if (found_account == null) {
if (acc == CommandLineOptions.val_account) { throw AccountNotFoundException()
found = true
logger.trace("Matches.")
break
}
} }
if (!found) {
show_error("Couldn't find account '" + CommandLineOptions.val_account!!.anonymize() + "'. Maybe you want to use '--login' first?")
}
account = CommandLineOptions.val_account!!
} else if (accounts.size == 0) { } else if (accounts.size == 0) {
println("No accounts found. Starting login process...") throw NoAccountsException()
CommandLineOptions.cmd_login = true
return null
} else if (accounts.size == 1) { } else if (accounts.size == 1) {
account = accounts.firstElement() found_account = accounts.firstElement()
System.out.println("Using only available account: " + account.anonymize()) println("Using only available account: " + account.anonymize())
} else { } else {
show_error(("You didn't specify which account to use.\n" + show_error(("You didn't specify which account to use.\n" +
"Use '--account <x>' to use account <x>.\n" + "Use '--account <x>' to use account <x>.\n" +
@ -241,8 +243,8 @@ class CommandLineController {
System.exit(1) System.exit(1)
} }
logger.debug("accounts.size: {}", accounts.size) logger.debug("accounts.size: {}", accounts.size)
logger.debug("account: {}", account.anonymize()) logger.debug("account: {}", found_account.anonymize())
return account return found_account!!
} }
private fun cmd_stats() { private fun cmd_stats() {
@ -315,20 +317,6 @@ class CommandLineController {
println(" --list-channels Lists all channels together with their ID") println(" --list-channels Lists all channels together with their ID")
} }
private fun list_accounts() {
println("List of available accounts:")
val accounts = Utils.getAccounts()
if (accounts.size > 0) {
for (str in accounts) {
System.out.println(" " + str.anonymize())
}
println("Use '--account <x>' to use one of those accounts.")
} else {
println("NO ACCOUNTS FOUND")
println("Use '--login' to login to a telegram account.")
}
}
companion object { companion object {
private val logger = LoggerFactory.getLogger(CommandLineController::class.java) private val logger = LoggerFactory.getLogger(CommandLineController::class.java)
@ -342,4 +330,7 @@ class CommandLineController {
println("TODO: Print the GPL.") println("TODO: Print the GPL.")
} }
} }
class AccountNotFoundException() : Exception("Account not found") {}
class NoAccountsException() : Exception("No accounts found") {}
} }

View File

@ -29,7 +29,7 @@ object Config {
val APP_APPVER: String val APP_APPVER: String
val APP_LANG = "en" val APP_LANG = "en"
var FILE_BASE = System.getProperty("user.home") + File.separatorChar + ".telegram_backup" var TARGET_DIR = System.getProperty("user.home") + File.separatorChar + ".telegram_backup"
val FILE_NAME_AUTH_KEY = "auth.dat" val FILE_NAME_AUTH_KEY = "auth.dat"
val FILE_NAME_DC = "dc.dat" val FILE_NAME_DC = "dc.dat"
val FILE_NAME_DB = "database.sqlite" val FILE_NAME_DB = "database.sqlite"

View File

@ -51,6 +51,33 @@ class Database private constructor(var client: TelegramClient) {
private var conn: Connection? = null private var conn: Connection? = null
private var stmt: Statement? = null private var stmt: Statement? = null
var user_manager: UserManager var user_manager: UserManager
init {
private val logger = LoggerFactory.getLogger(Database::class.java)
this.user_manager = UserManager.getInstance()
System.out.println("Opening database...")
try {
Class.forName("org.sqlite.JDBC")
} catch (e: ClassNotFoundException) {
CommandLineController.show_error("Could not load jdbc-sqlite class.")
}
val path = "jdbc:sqlite:${user_manager.fileBase}${Config.FILE_NAME_DB}"
try {
conn = DriverManager.getConnection(path)
stmt = conn!!.createStatement()
} catch (e: SQLException) {
CommandLineController.show_error("Could not connect to SQLITE database.")
}
// Run updates
val updates = DatabaseUpdates(conn!!, this)
updates.doUpdates()
System.out.println("Database is ready.")
}
fun getTopMessageID(): Int { fun getTopMessageID(): Int {
try { try {
@ -69,34 +96,32 @@ class Database private constructor(var client: TelegramClient) {
fun getChatCount(): Int = queryInt("SELECT COUNT(*) FROM chats") fun getChatCount(): Int = queryInt("SELECT COUNT(*) FROM chats")
fun getUserCount(): Int = queryInt("SELECT COUNT(*) FROM users") fun getUserCount(): Int = queryInt("SELECT COUNT(*) FROM users")
val missingIDs: LinkedList<Int> fun getMissingIDs: LinkedList<Int>()
get() { try {
try { val missing = LinkedList<Int>()
val missing = LinkedList<Int>() val max = getTopMessageID()
val max = getTopMessageID() val rs = stmt!!.executeQuery("SELECT message_id FROM messages WHERE source_type IN ('group', 'dialog') ORDER BY id")
val rs = stmt!!.executeQuery("SELECT message_id FROM messages WHERE source_type IN ('group', 'dialog') ORDER BY id") rs.next()
rs.next() var id = rs.getInt(1)
var id = rs.getInt(1) for (i in 1..max) {
for (i in 1..max) { if (i == id) {
if (i == id) { rs.next()
rs.next() if (rs.isClosed()) {
if (rs.isClosed()) { id = Integer.MAX_VALUE
id = Integer.MAX_VALUE } else {
} else { id = rs.getInt(1)
id = rs.getInt(1)
}
} else if (i < id) {
missing.add(i)
} }
} else if (i < id) {
missing.add(i)
} }
rs.close()
return missing
} catch (e: SQLException) {
e.printStackTrace()
throw RuntimeException("Could not get list of ids.")
} }
rs.close()
return missing
} catch (e: SQLException) {
e.printStackTrace()
throw RuntimeException("Could not get list of ids.")
} }
}
fun getMessagesWithMedia(): LinkedList<TLMessage?> { fun getMessagesWithMedia(): LinkedList<TLMessage?> {
try { try {
@ -207,30 +232,7 @@ class Database private constructor(var client: TelegramClient) {
} }
init {
this.user_manager = UserManager.getInstance()
System.out.println("Opening database...")
try {
Class.forName("org.sqlite.JDBC")
} catch (e: ClassNotFoundException) {
CommandLineController.show_error("Could not load jdbc-sqlite class.")
}
val path = "jdbc:sqlite:${user_manager.fileBase}${Config.FILE_NAME_DB}"
try {
conn = DriverManager.getConnection(path)
stmt = conn!!.createStatement()
} catch (e: SQLException) {
CommandLineController.show_error("Could not connect to SQLITE database.")
}
// Run updates
val updates = DatabaseUpdates(conn!!, this)
updates.doUpdates()
System.out.println("Database is ready.")
}
fun backupDatabase(currentVersion: Int) { fun backupDatabase(currentVersion: Int) {
val filename = String.format(Config.FILE_NAME_DB_BACKUP, currentVersion) val filename = String.format(Config.FILE_NAME_DB_BACKUP, currentVersion)
@ -550,7 +552,9 @@ class Database private constructor(var client: TelegramClient) {
fun fetchSetting(key: String): String? { fun fetchSetting(key: String): String? {
val rs = stmt!!.executeQuery("SELECT value FROM settings WHERE key='${key}'") val rs = stmt!!.executeQuery("SELECT value FROM settings WHERE key='${key}'")
rs.next() rs.next()
return rs.getString(1) val result = rs.getString(1)
rs.close()
return result
} }
fun saveSetting(key: String, value: String?) { fun saveSetting(key: String, value: String?) {
@ -562,6 +566,7 @@ class Database private constructor(var client: TelegramClient) {
ps.setString(2, value) ps.setString(2, value)
} }
ps.execute() ps.execute()
ps.close()
} }
fun getIdsFromQuery(query: String): LinkedList<Int> { fun getIdsFromQuery(query: String): LinkedList<Int> {
@ -787,18 +792,6 @@ class Database private constructor(var client: TelegramClient) {
} }
companion object { companion object {
private val logger = LoggerFactory.getLogger(Database::class.java)
internal var instance: Database? = null
fun init(c: TelegramClient) {
instance = Database(c)
}
fun getInstance(): Database {
if (instance == null) throw RuntimeException("Database is not initialized but getInstance() was called.")
return instance!!
}
fun bytesToTLMessage(b: ByteArray?): TLMessage? { fun bytesToTLMessage(b: ByteArray?): TLMessage? {
try { try {
if (b == null) return null if (b == null) return null

View File

@ -34,9 +34,23 @@ object Utils {
private val logger = LoggerFactory.getLogger(Utils::class.java) as Logger private val logger = LoggerFactory.getLogger(Utils::class.java) as Logger
fun getAccounts(): Vector<String> { fun print_accounts(file_base: String) {
println("List of available accounts:")
val accounts = getAccounts(file_base)
if (accounts.size > 0) {
for (str in accounts) {
println(" " + str.anonymize())
}
println("Use '--account <x>' to use one of those accounts.")
} else {
println("NO ACCOUNTS FOUND")
println("Use '--login' to login to a telegram account.")
}
}
fun getAccounts(file_base: String): Vector<String> {
val accounts = Vector<String>() val accounts = Vector<String>()
val folder = File(Config.FILE_BASE) val folder = File(file_base)
val files = folder.listFiles() val files = folder.listFiles()
if (files != null) if (files != null)
for (f in files) { for (f in files) {