Line data Source code
1 7 : int maildrop_read(LOGGER log, sqlite3_stmt* stmt, MAILDROP* maildrop, char* user_name, bool is_master){
2 7 : int status = 0;
3 : char* message_id;
4 7 : unsigned rows = maildrop->count;
5 7 : unsigned index = maildrop->count;
6 : unsigned i;
7 :
8 7 : POP_MAIL empty_mail = {
9 : .database_id = 0,
10 : .mail_size = 0,
11 : .flag_master = is_master,
12 : .flag_delete = false,
13 : .message_id = ""
14 : };
15 :
16 7 : if(sqlite3_bind_text(stmt, 1, user_name, -1, SQLITE_STATIC) == SQLITE_OK){
17 : do{
18 7 : status = sqlite3_step(stmt);
19 7 : switch(status){
20 : case SQLITE_ROW:
21 0 : if(index >= maildrop->count){
22 : //expand the maildrop
23 0 : rows += CMAIL_MAILDROP_CHUNK;
24 0 : maildrop->mails = realloc(maildrop->mails, rows * sizeof(POP_MAIL));
25 0 : for(i = index; i < rows; i++){
26 0 : maildrop->mails[i] = empty_mail;
27 : }
28 0 : maildrop->count = rows;
29 : }
30 :
31 0 : if(index >= maildrop->count){
32 0 : logprintf(log, LOG_WARNING, "Maildrop reading went out of bounds, this should not have happened\n");
33 0 : break;
34 : }
35 :
36 0 : maildrop->mails[index].database_id = sqlite3_column_int(stmt, 0);
37 0 : maildrop->mails[index].mail_size = sqlite3_column_int(stmt, 1);
38 0 : message_id = (char*)sqlite3_column_text(stmt, 2);
39 0 : if(message_id){
40 0 : strncpy(maildrop->mails[index].message_id, message_id, POP_MESSAGEID_MAX);
41 : }
42 :
43 0 : index++;
44 0 : break;
45 : case SQLITE_ERROR:
46 : //FIXME handle this
47 0 : break;
48 : }
49 : }
50 7 : while(status == SQLITE_ROW);
51 7 : maildrop->count = index;
52 : }
53 : else{
54 0 : logprintf(log, LOG_WARNING, "Failed to bind mail query parameter\n");
55 0 : status = -1;
56 : }
57 :
58 7 : sqlite3_reset(stmt);
59 7 : sqlite3_clear_bindings(stmt);
60 7 : return status;
61 : }
62 :
63 14 : int maildrop_lock(LOGGER log, DATABASE* database, char* user_name, bool lock){
64 : int status;
65 :
66 : //atomically modify maildrop lock, bail out if it fails
67 14 : if(sqlite3_bind_int(database->update_lock, 1, lock ? 1:0) != SQLITE_OK
68 14 : || sqlite3_bind_text(database->update_lock, 2, user_name, -1, SQLITE_STATIC) != SQLITE_OK
69 14 : || sqlite3_bind_int(database->update_lock, 3, lock?0:1) != SQLITE_OK){
70 0 : logprintf(log, LOG_ERROR, "Failed to bind lock update parameter\n");
71 0 : sqlite3_reset(database->update_lock);
72 0 : sqlite3_clear_bindings(database->update_lock);
73 0 : return -1;
74 : }
75 :
76 14 : status = sqlite3_step(database->update_lock);
77 14 : switch(status){
78 : case SQLITE_DONE:
79 14 : status = 0;
80 : //check if lock was updated
81 14 : if(sqlite3_changes(database->conn) != 1){
82 0 : status = -1;
83 : }
84 14 : break;
85 : default:
86 0 : logprintf(log, LOG_INFO, "Unhandled return value from lock update: %d\n", status);
87 0 : status = 1;
88 : }
89 :
90 14 : sqlite3_reset(database->update_lock);
91 14 : sqlite3_clear_bindings(database->update_lock);
92 :
93 14 : return status;
94 : }
95 :
96 7 : int maildrop_user_attach(LOGGER log, DATABASE* database, MAILDROP* maildrop, char* user_name){
97 7 : int status = 1;
98 7 : char* dbfile = NULL;
99 7 : char* err_str = NULL;
100 :
101 7 : char* LIST_MAILS_USER = "SELECT mail_id, length(mail_data) AS length, mail_ident FROM main.mailbox WHERE mail_user=? ORDER BY mail_submission ASC;";
102 7 : char* FETCH_MAIL_USER = "SELECT mail_data FROM main.mailbox WHERE mail_id=?;";
103 :
104 7 : char* MARK_DELETION = "INSERT INTO temp.deletions (user, mail) VALUES (?, ?);";
105 7 : char* UNMARK_DELETIONS = "DELETE FROM temp.deletions WHERE user = ?;";
106 7 : char* DELETE_MAIL_USER = "DELETE FROM main.mailbox WHERE mail_id IN (SELECT mail FROM temp.deletions WHERE user = ?);";
107 :
108 7 : char* CREATE_DELETION_TABLE = "CREATE TEMPORARY TABLE temp.deletions (user TEXT NOT NULL, mail INTEGER NOT NULL);";
109 :
110 : //test for user databases
111 7 : if(sqlite3_bind_text(database->query_userdatabase, 1, user_name, -1, SQLITE_STATIC) == SQLITE_OK){
112 7 : switch(sqlite3_step(database->query_userdatabase)){
113 : case SQLITE_ROW:
114 : //attach user database
115 0 : dbfile = (char*)sqlite3_column_text(database->query_userdatabase, 0);
116 0 : logprintf(log, LOG_INFO, "User %s has user database %s\n", user_name, dbfile);
117 :
118 0 : maildrop->user_conn = database_open(log, dbfile, SQLITE_OPEN_READWRITE);
119 :
120 0 : if(!(maildrop->user_conn)){
121 0 : status = -1;
122 0 : logprintf(log, LOG_ERROR, "Failed to attach user database %s\n", dbfile);
123 0 : break;
124 : }
125 :
126 0 : logprintf(log, LOG_INFO, "User database %s attached for user %s\n", dbfile, user_name);
127 :
128 : //create the temp table to store deletions
129 0 : switch(sqlite3_exec(maildrop->user_conn, CREATE_DELETION_TABLE, NULL, NULL, &err_str)){
130 : case SQLITE_OK:
131 : case SQLITE_DONE:
132 0 : break;
133 : default:
134 0 : logprintf(log, LOG_WARNING, "Non-completion response to temp table create statement\n");
135 : }
136 :
137 0 : if(err_str){
138 0 : logprintf(log, LOG_ERROR, "Failed to create temporary deletion table: %s\n", err_str);
139 0 : sqlite3_free(err_str);
140 0 : return -1;
141 : }
142 :
143 : //create user table statements
144 0 : maildrop->list_user = database_prepare(log, maildrop->user_conn, LIST_MAILS_USER);
145 0 : maildrop->fetch_user = database_prepare(log, maildrop->user_conn, FETCH_MAIL_USER);
146 0 : maildrop->mark_deletion = database_prepare(log, maildrop->user_conn, MARK_DELETION);
147 0 : maildrop->unmark_deletions = database_prepare(log, maildrop->user_conn, UNMARK_DELETIONS);
148 0 : maildrop->delete_user = database_prepare(log, maildrop->user_conn, DELETE_MAIL_USER);
149 :
150 0 : if(!maildrop->list_user || !maildrop->fetch_user || !maildrop->delete_user || !maildrop->mark_deletion || !maildrop->unmark_deletions){
151 0 : logprintf(log, LOG_WARNING, "Failed to prepare user mail access statements\n");
152 0 : status = -1;
153 : }
154 : else{
155 0 : status = 0;
156 : }
157 0 : break;
158 : case SQLITE_DONE:
159 : //no user database, done here
160 7 : break;
161 : default:
162 0 : logprintf(log, LOG_WARNING, "Failed to query user database for %s: %s\n", user_name, sqlite3_errmsg(database->conn));
163 0 : status = -1;
164 0 : break;
165 : }
166 : }
167 : else{
168 0 : logprintf(log, LOG_WARNING, "Failed to bind user parameter to user database query\n");
169 0 : status = -1;
170 : }
171 :
172 7 : sqlite3_reset(database->query_userdatabase);
173 7 : sqlite3_clear_bindings(database->query_userdatabase);
174 :
175 7 : return status;
176 : }
177 :
178 7 : int maildrop_acquire(LOGGER log, DATABASE* database, MAILDROP* maildrop, char* user_name){
179 7 : int status = 0;
180 :
181 : //lock maildrop
182 7 : if(maildrop_lock(log, database, user_name, true) < 0){
183 0 : logprintf(log, LOG_WARNING, "Maildrop for user %s could not be locked\n", user_name);
184 0 : return -1;
185 : }
186 :
187 : //read mail data from master
188 7 : if(maildrop_read(log, database->list_master, maildrop, user_name, true) < 0){
189 0 : logprintf(log, LOG_WARNING, "Failed to read master maildrop for user %s\n", user_name);
190 0 : status = -1;
191 : }
192 :
193 7 : switch(maildrop_user_attach(log, database, maildrop, user_name)){
194 : case 1:
195 7 : logprintf(log, LOG_INFO, "User %s does not have a user database\n", user_name);
196 7 : break;
197 : case 0:
198 : //read mail data from user database
199 0 : if(maildrop_read(log, maildrop->list_user, maildrop, user_name, false) < 0){
200 0 : logprintf(log, LOG_WARNING, "Failed to read user maildrop for %s\n", user_name);
201 0 : status = -1;
202 : }
203 0 : break;
204 : default:
205 0 : logprintf(log, LOG_WARNING, "Failed to attach database for user %s\n", user_name);
206 0 : status = -1;
207 : }
208 :
209 : //FIXME unlock the maildrop on error (if it was not locked beforehand)
210 :
211 7 : logprintf(log, LOG_INFO, "Maildrop for user %s is at %d mails\n", user_name, maildrop->count);
212 7 : return status;
213 : }
214 :
215 0 : int maildrop_mark(LOGGER log, DATABASE* database, char* user_name, MAILDROP* maildrop, int mail_id){
216 0 : int rv = 0;
217 0 : sqlite3_stmt* mark_deletion = maildrop->mails[mail_id].flag_master ? database->mark_deletion:maildrop->mark_deletion;
218 :
219 0 : if(sqlite3_bind_text(mark_deletion, 1, user_name, -1, SQLITE_STATIC) == SQLITE_OK
220 0 : && sqlite3_bind_int(mark_deletion, 2, maildrop->mails[mail_id].database_id) == SQLITE_OK){
221 0 : switch(sqlite3_step(mark_deletion)){
222 : case SQLITE_DONE:
223 : case SQLITE_OK:
224 0 : logprintf(log, LOG_DEBUG, "Marked mail %d %s as deleted (user %s)\n", mail_id, maildrop->mails[mail_id].flag_master? "in master":"in userdb", user_name);
225 0 : break;
226 : default:
227 0 : logprintf(log, LOG_WARNING, "Failed to mark mail as deleted: %s\n", sqlite3_errmsg(maildrop->mails[mail_id].flag_master ? database->conn:maildrop->user_conn));
228 0 : rv = -1;
229 0 : break;
230 : }
231 0 : }
232 : else{
233 0 : logprintf(log, LOG_ERROR, "Failed to bind parameter to deletion mark query\n");
234 0 : rv = -1;
235 : }
236 :
237 0 : sqlite3_reset(mark_deletion);
238 0 : sqlite3_clear_bindings(mark_deletion);
239 0 : return rv;
240 : }
241 :
242 7 : int maildrop_unmark(LOGGER log, sqlite3* conn, sqlite3_stmt* unmark_deletions, char* user_name){
243 7 : int rv = 0;
244 :
245 7 : if(sqlite3_bind_text(unmark_deletions, 1, user_name, -1, SQLITE_STATIC) == SQLITE_OK){
246 7 : switch(sqlite3_step(unmark_deletions)){
247 : case SQLITE_DONE:
248 : case SQLITE_OK:
249 7 : logprintf(log, LOG_DEBUG, "Deletions table for %s cleared (%d marks deleted)\n", user_name, sqlite3_changes(conn));
250 7 : break;
251 : default:
252 0 : logprintf(log, LOG_WARNING, "Failed to clear deletion table: %s\n", sqlite3_errmsg(conn));
253 0 : rv = -1;
254 0 : break;
255 : }
256 : }
257 : else{
258 0 : logprintf(log, LOG_ERROR, "Failed to bind deletion user name parameter\n");
259 0 : rv = -1;
260 : }
261 :
262 7 : sqlite3_reset(unmark_deletions);
263 7 : sqlite3_clear_bindings(unmark_deletions);
264 :
265 7 : return rv;
266 : }
267 :
268 4 : int maildrop_delete(LOGGER log, sqlite3_stmt* deletion_stmt, char* user_name){
269 4 : int rv = 0;
270 :
271 4 : if(sqlite3_bind_text(deletion_stmt, 1, user_name, -1, SQLITE_STATIC) == SQLITE_OK){
272 4 : switch(sqlite3_step(deletion_stmt)){
273 : case SQLITE_DONE:
274 : case SQLITE_OK:
275 4 : break;
276 : default:
277 0 : logprintf(log, LOG_WARNING, "Failed to perform deletion\n");
278 0 : rv = -1;
279 0 : break;
280 : }
281 : }
282 : else{
283 0 : logprintf(log, LOG_ERROR, "Failed to bind deletion user name parameter\n");
284 0 : rv = -1;
285 : }
286 :
287 4 : sqlite3_reset(deletion_stmt);
288 4 : sqlite3_clear_bindings(deletion_stmt);
289 4 : return rv;
290 : }
291 :
292 4 : int maildrop_update(LOGGER log, DATABASE* database, MAILDROP* maildrop, char* user_name){
293 4 : int status = 0;
294 4 : logprintf(log, LOG_INFO, "Performing deletions\n");
295 :
296 : //delete mails from master
297 4 : if(maildrop_delete(log, database->delete_master, user_name) < 0){
298 0 : logprintf(log, LOG_ERROR, "Failed to delete marked mails from master database: %s\n", sqlite3_errmsg(database->conn));
299 0 : status = -1;
300 : }
301 :
302 4 : logprintf(log, LOG_INFO, "Deleted %d mails from master database\n", sqlite3_changes(database->conn));
303 :
304 : //delete mails from user database if present
305 4 : if(maildrop->user_conn){
306 0 : if(maildrop_delete(log, maildrop->delete_user, user_name) < 0){
307 0 : logprintf(log, LOG_ERROR, "Failed to delete marked mails from user database: %s\n", sqlite3_errmsg(maildrop->user_conn));
308 0 : status = -1;
309 : }
310 :
311 0 : logprintf(log, LOG_INFO, "Deleted %d mails from user database\n", sqlite3_changes(maildrop->user_conn));
312 : }
313 : else{
314 4 : logprintf(log, LOG_DEBUG, "Not deleting from user database, none attached\n");
315 : }
316 :
317 4 : return status;
318 : }
319 :
320 7 : int maildrop_release(LOGGER log, DATABASE* database, MAILDROP* maildrop, char* user_name){
321 7 : MAILDROP empty_maildrop = {
322 : .count = 0,
323 : .mails = NULL,
324 : .user_conn = NULL,
325 : .list_user = NULL,
326 : .fetch_user = NULL,
327 : .mark_deletion = NULL,
328 : .unmark_deletions = NULL,
329 : .delete_user = NULL
330 : };
331 7 : int status = 0;
332 :
333 : //reset master deletion tables
334 7 : if(maildrop_unmark(log, database->conn, database->unmark_deletions, user_name) < 0){
335 0 : logprintf(log, LOG_ERROR, "Failed to reset master deletion table\n");
336 0 : status = -1;
337 : }
338 :
339 : //reset user deletion table
340 7 : if(maildrop->user_conn && maildrop_unmark(log, maildrop->user_conn, maildrop->unmark_deletions, user_name) < 0){
341 0 : logprintf(log, LOG_ERROR, "Failed to reset user database deletion table\n");
342 0 : status = -1;
343 : }
344 :
345 : //unlock maildrop
346 7 : if(maildrop_lock(log, database, user_name, false) < 0){
347 0 : logprintf(log, LOG_WARNING, "Failed to unlock maildrop for user %s\n", user_name);
348 : }
349 :
350 : //free user statements
351 7 : if(maildrop->user_conn){
352 0 : sqlite3_finalize(maildrop->list_user);
353 0 : sqlite3_finalize(maildrop->fetch_user);
354 0 : sqlite3_finalize(maildrop->delete_user);
355 0 : sqlite3_finalize(maildrop->mark_deletion);
356 0 : sqlite3_finalize(maildrop->unmark_deletions);
357 0 : sqlite3_close(maildrop->user_conn);
358 : }
359 :
360 : //free maildrop data
361 7 : if(maildrop->mails){
362 0 : free(maildrop->mails);
363 : }
364 :
365 7 : *maildrop = empty_maildrop;
366 :
367 7 : return status;
368 : }
|