LCOV - code coverage report
Current view: top level - cmail-popd - maildrop.c (source / functions) Hit Total Coverage
Test: popd.info Lines: 85 196 43.4 %
Date: 2015-11-25 19:05:59 Functions: 8 9 88.9 %

          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             : }

Generated by: LCOV version 1.11