-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsqlite.go
More file actions
153 lines (131 loc) · 3.61 KB
/
Copy pathsqlite.go
File metadata and controls
153 lines (131 loc) · 3.61 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
package main
import (
"database/sql"
"fmt"
tgconv "github.com/mtgo-labs/session-converter"
_ "modernc.org/sqlite"
)
// SQLiteFormat identifies which library produced a SQLite session file.
type SQLiteFormat string
const (
SQLiteTelethon SQLiteFormat = "telethon"
SQLitePyrogram SQLiteFormat = "pyrogram"
)
// ReadSQLite auto-detects whether a SQLite file is a Telethon or Pyrogram
// session file and extracts the session data.
func ReadSQLite(path string) (*tgconv.Session, SQLiteFormat, error) {
db, err := sql.Open("sqlite", path)
if err != nil {
return nil, "", fmt.Errorf("open sqlite: %w", err)
}
defer db.Close()
format, err := detectSQLiteFormat(db)
if err != nil {
return nil, "", err
}
switch format {
case SQLiteTelethon:
s, err := readTelethonDB(db)
return s, SQLiteTelethon, err
case SQLitePyrogram:
s, err := readPyrogramDB(db)
return s, SQLitePyrogram, err
default:
return nil, "", fmt.Errorf("unrecognized sqlite session format")
}
}
func detectSQLiteFormat(db *sql.DB) (SQLiteFormat, error) {
rows, err := db.Query(`SELECT name FROM sqlite_master WHERE type='table'`)
if err != nil {
return "", fmt.Errorf("query tables: %w", err)
}
defer rows.Close()
tables := make(map[string]bool)
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
return "", err
}
tables[name] = true
}
if tables["sessions"] && tables["entities"] && tables["version"] {
return SQLiteTelethon, nil
}
if tables["sessions"] && tables["peers"] && tables["version"] {
return SQLitePyrogram, nil
}
return "", fmt.Errorf("unrecognized sqlite schema (tables: %v)", tables)
}
func readTelethonDB(db *sql.DB) (*tgconv.Session, error) {
var s tgconv.Session
var authKey []byte
err := db.QueryRow(
`SELECT dc_id, server_address, port, auth_key FROM sessions LIMIT 1`,
).Scan(&s.DCID, &s.ServerAddress, &s.Port, &authKey)
if err != nil {
return nil, fmt.Errorf("telethon sqlite: query sessions: %w", err)
}
if len(authKey) != 256 {
return nil, fmt.Errorf("telethon sqlite: auth_key must be 256 bytes, got %d", len(authKey))
}
s.AuthKey = authKey
var userID int64
err = db.QueryRow(
`SELECT id FROM entities WHERE id != 0 ORDER BY date DESC LIMIT 1`,
).Scan(&userID)
if err == nil {
s.UserID = userID
}
s.FillDefaults()
return &s, nil
}
func readPyrogramDB(db *sql.DB) (*tgconv.Session, error) {
var s tgconv.Session
var authKey []byte
var testMode, isBot int
var userID int64
var apiID sql.NullInt32
var hasAPIID bool
rows, err := db.Query(`PRAGMA table_info(sessions)`)
if err != nil {
return nil, fmt.Errorf("pyrogram sqlite: pragma: %w", err)
}
for rows.Next() {
var cid int
var name, ctype string
var notnull, pk int
var dflt sql.NullString
if err := rows.Scan(&cid, &name, &ctype, ¬null, &dflt, &pk); err != nil {
rows.Close()
return nil, err
}
if name == "api_id" {
hasAPIID = true
}
}
rows.Close()
if hasAPIID {
err = db.QueryRow(
`SELECT dc_id, api_id, test_mode, auth_key, user_id, is_bot FROM sessions LIMIT 1`,
).Scan(&s.DCID, &apiID, &testMode, &authKey, &userID, &isBot)
} else {
err = db.QueryRow(
`SELECT dc_id, test_mode, auth_key, user_id, is_bot FROM sessions LIMIT 1`,
).Scan(&s.DCID, &testMode, &authKey, &userID, &isBot)
}
if err != nil {
return nil, fmt.Errorf("pyrogram sqlite: query sessions: %w", err)
}
if len(authKey) != 256 {
return nil, fmt.Errorf("pyrogram sqlite: auth_key must be 256 bytes, got %d", len(authKey))
}
s.AuthKey = authKey
s.TestMode = testMode != 0
s.UserID = userID
s.IsBot = isBot != 0
if apiID.Valid {
s.AppID = apiID.Int32
}
s.FillDefaults()
return &s, nil
}