torrust_tracker/core/databases/
sqlite.rs1use std::panic::Location;
3use std::str::FromStr;
4
5use r2d2::Pool;
6use r2d2_sqlite::rusqlite::params;
7use r2d2_sqlite::rusqlite::types::Null;
8use r2d2_sqlite::SqliteConnectionManager;
9use torrust_tracker_primitives::info_hash::InfoHash;
10use torrust_tracker_primitives::{DurationSinceUnixEpoch, PersistentTorrents};
11
12use super::driver::Driver;
13use super::{Database, Error};
14use crate::core::auth::{self, Key};
15
16const DRIVER: Driver = Driver::Sqlite3;
17
18pub struct Sqlite {
19 pool: Pool<SqliteConnectionManager>,
20}
21
22impl Database for Sqlite {
23 fn new(db_path: &str) -> Result<Sqlite, Error> {
31 let cm = SqliteConnectionManager::file(db_path);
32 Pool::new(cm).map_or_else(|err| Err((err, Driver::Sqlite3).into()), |pool| Ok(Sqlite { pool }))
33 }
34
35 fn create_database_tables(&self) -> Result<(), Error> {
37 let create_whitelist_table = "
38 CREATE TABLE IF NOT EXISTS whitelist (
39 id INTEGER PRIMARY KEY AUTOINCREMENT,
40 info_hash TEXT NOT NULL UNIQUE
41 );"
42 .to_string();
43
44 let create_torrents_table = "
45 CREATE TABLE IF NOT EXISTS torrents (
46 id INTEGER PRIMARY KEY AUTOINCREMENT,
47 info_hash TEXT NOT NULL UNIQUE,
48 completed INTEGER DEFAULT 0 NOT NULL
49 );"
50 .to_string();
51
52 let create_keys_table = "
53 CREATE TABLE IF NOT EXISTS keys (
54 id INTEGER PRIMARY KEY AUTOINCREMENT,
55 key TEXT NOT NULL UNIQUE,
56 valid_until INTEGER
57 );"
58 .to_string();
59
60 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
61
62 conn.execute(&create_whitelist_table, [])?;
63 conn.execute(&create_keys_table, [])?;
64 conn.execute(&create_torrents_table, [])?;
65
66 Ok(())
67 }
68
69 fn drop_database_tables(&self) -> Result<(), Error> {
71 let drop_whitelist_table = "
72 DROP TABLE whitelist;"
73 .to_string();
74
75 let drop_torrents_table = "
76 DROP TABLE torrents;"
77 .to_string();
78
79 let drop_keys_table = "
80 DROP TABLE keys;"
81 .to_string();
82
83 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
84
85 conn.execute(&drop_whitelist_table, [])
86 .and_then(|_| conn.execute(&drop_torrents_table, []))
87 .and_then(|_| conn.execute(&drop_keys_table, []))?;
88
89 Ok(())
90 }
91
92 fn load_persistent_torrents(&self) -> Result<PersistentTorrents, Error> {
94 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
95
96 let mut stmt = conn.prepare("SELECT info_hash, completed FROM torrents")?;
97
98 let torrent_iter = stmt.query_map([], |row| {
99 let info_hash_string: String = row.get(0)?;
100 let info_hash = InfoHash::from_str(&info_hash_string).unwrap();
101 let completed: u32 = row.get(1)?;
102 Ok((info_hash, completed))
103 })?;
104
105 Ok(torrent_iter.filter_map(std::result::Result::ok).collect())
106 }
107
108 fn load_keys(&self) -> Result<Vec<auth::PeerKey>, Error> {
110 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
111
112 let mut stmt = conn.prepare("SELECT key, valid_until FROM keys")?;
113
114 let keys_iter = stmt.query_map([], |row| {
115 let key: String = row.get(0)?;
116 let opt_valid_until: Option<i64> = row.get(1)?;
117
118 match opt_valid_until {
119 Some(valid_until) => Ok(auth::PeerKey {
120 key: key.parse::<Key>().unwrap(),
121 valid_until: Some(DurationSinceUnixEpoch::from_secs(valid_until.unsigned_abs())),
122 }),
123 None => Ok(auth::PeerKey {
124 key: key.parse::<Key>().unwrap(),
125 valid_until: None,
126 }),
127 }
128 })?;
129
130 let keys: Vec<auth::PeerKey> = keys_iter.filter_map(std::result::Result::ok).collect();
131
132 Ok(keys)
133 }
134
135 fn load_whitelist(&self) -> Result<Vec<InfoHash>, Error> {
137 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
138
139 let mut stmt = conn.prepare("SELECT info_hash FROM whitelist")?;
140
141 let info_hash_iter = stmt.query_map([], |row| {
142 let info_hash: String = row.get(0)?;
143
144 Ok(InfoHash::from_str(&info_hash).unwrap())
145 })?;
146
147 let info_hashes: Vec<InfoHash> = info_hash_iter.filter_map(std::result::Result::ok).collect();
148
149 Ok(info_hashes)
150 }
151
152 fn save_persistent_torrent(&self, info_hash: &InfoHash, completed: u32) -> Result<(), Error> {
154 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
155
156 let insert = conn.execute(
157 "INSERT INTO torrents (info_hash, completed) VALUES (?1, ?2) ON CONFLICT(info_hash) DO UPDATE SET completed = ?2",
158 [info_hash.to_string(), completed.to_string()],
159 )?;
160
161 if insert == 0 {
162 Err(Error::InsertFailed {
163 location: Location::caller(),
164 driver: DRIVER,
165 })
166 } else {
167 Ok(())
168 }
169 }
170
171 fn get_info_hash_from_whitelist(&self, info_hash: InfoHash) -> Result<Option<InfoHash>, Error> {
173 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
174
175 let mut stmt = conn.prepare("SELECT info_hash FROM whitelist WHERE info_hash = ?")?;
176
177 let mut rows = stmt.query([info_hash.to_hex_string()])?;
178
179 let query = rows.next()?;
180
181 Ok(query.map(|f| InfoHash::from_str(&f.get_unwrap::<_, String>(0)).unwrap()))
182 }
183
184 fn add_info_hash_to_whitelist(&self, info_hash: InfoHash) -> Result<usize, Error> {
186 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
187
188 let insert = conn.execute("INSERT INTO whitelist (info_hash) VALUES (?)", [info_hash.to_string()])?;
189
190 if insert == 0 {
191 Err(Error::InsertFailed {
192 location: Location::caller(),
193 driver: DRIVER,
194 })
195 } else {
196 Ok(insert)
197 }
198 }
199
200 fn remove_info_hash_from_whitelist(&self, info_hash: InfoHash) -> Result<usize, Error> {
202 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
203
204 let deleted = conn.execute("DELETE FROM whitelist WHERE info_hash = ?", [info_hash.to_string()])?;
205
206 if deleted == 1 {
207 Ok(deleted)
209 } else {
210 Err(Error::DeleteFailed {
211 location: Location::caller(),
212 error_code: deleted,
213 driver: DRIVER,
214 })
215 }
216 }
217
218 fn get_key_from_keys(&self, key: &Key) -> Result<Option<auth::PeerKey>, Error> {
220 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
221
222 let mut stmt = conn.prepare("SELECT key, valid_until FROM keys WHERE key = ?")?;
223
224 let mut rows = stmt.query([key.to_string()])?;
225
226 let key = rows.next()?;
227
228 Ok(key.map(|f| {
229 let valid_until: Option<i64> = f.get(1).unwrap();
230 let key: String = f.get(0).unwrap();
231
232 match valid_until {
233 Some(valid_until) => auth::PeerKey {
234 key: key.parse::<Key>().unwrap(),
235 valid_until: Some(DurationSinceUnixEpoch::from_secs(valid_until.unsigned_abs())),
236 },
237 None => auth::PeerKey {
238 key: key.parse::<Key>().unwrap(),
239 valid_until: None,
240 },
241 }
242 }))
243 }
244
245 fn add_key_to_keys(&self, auth_key: &auth::PeerKey) -> Result<usize, Error> {
247 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
248
249 let insert = match auth_key.valid_until {
250 Some(valid_until) => conn.execute(
251 "INSERT INTO keys (key, valid_until) VALUES (?1, ?2)",
252 [auth_key.key.to_string(), valid_until.as_secs().to_string()],
253 )?,
254 None => conn.execute(
255 "INSERT INTO keys (key, valid_until) VALUES (?1, ?2)",
256 params![auth_key.key.to_string(), Null],
257 )?,
258 };
259
260 if insert == 0 {
261 Err(Error::InsertFailed {
262 location: Location::caller(),
263 driver: DRIVER,
264 })
265 } else {
266 Ok(insert)
267 }
268 }
269
270 fn remove_key_from_keys(&self, key: &Key) -> Result<usize, Error> {
272 let conn = self.pool.get().map_err(|e| (e, DRIVER))?;
273
274 let deleted = conn.execute("DELETE FROM keys WHERE key = ?", [key.to_string()])?;
275
276 if deleted == 1 {
277 Ok(deleted)
279 } else {
280 Err(Error::DeleteFailed {
281 location: Location::caller(),
282 error_code: deleted,
283 driver: DRIVER,
284 })
285 }
286 }
287}