LCOV - code coverage report
Current view: top level - cmail-smtpd - database.c (source / functions) Hit Total Coverage
Test: smtpd.info Lines: 97 127 76.4 %
Date: 2015-11-25 19:06:20 Functions: 6 6 100.0 %

          Line data    Source code
       1           3 : int database_attach(LOGGER log, DATABASE* database, char* dbfile){
       2           3 :         char* INSERT_USER_MAILBOX = "INSERT INTO mailbox (mail_user, mail_ident, mail_envelopeto, mail_envelopefrom, mail_submitter, mail_proto, mail_data) VALUES (?, ?, ?, ?, ?, ?, ?);";
       3             :         unsigned slot;
       4             :         USER_DATABASE* entry;
       5             : 
       6             :         //initialize user storage structure
       7           3 :         if(!database->mail_storage.users){
       8           1 :                 database->mail_storage.users = calloc(1, sizeof(USER_DATABASE*));
       9           1 :                 if(!database->mail_storage.users){
      10           0 :                         logprintf(log, LOG_ERROR, "Failed to allocate memory for user storage database structure\n");
      11           0 :                         return -1;
      12             :                 }
      13             :         }
      14             : 
      15             :         //create/reuse entry in user storage structure
      16           4 :         for(slot = 0; database->mail_storage.users[slot]; slot++){
      17           2 :                 if(!database->mail_storage.users[slot]->conn){
      18           1 :                         break;
      19             :                 }
      20             :         }
      21             : 
      22             :         //if no usable slot, reallocate
      23           3 :         if(!database->mail_storage.users[slot]){
      24           2 :                 database->mail_storage.users = realloc(database->mail_storage.users, (slot + 2) * sizeof(USER_DATABASE*));
      25           2 :                 if(!database->mail_storage.users){
      26           0 :                         logprintf(log, LOG_ERROR, "Failed to reallocate user storage database structure\n");
      27           0 :                         return -1;
      28             :                 }
      29           2 :                 database->mail_storage.users[slot + 1] = NULL;
      30           2 :                 database->mail_storage.users[slot] = calloc(1, sizeof(USER_DATABASE));
      31           2 :                 if(!database->mail_storage.users[slot]){
      32           0 :                         logprintf(log, LOG_ERROR, "Failed to allocate user storage database structure\n");
      33           0 :                         return -1;
      34             :                 }
      35             :         }
      36             : 
      37           3 :         entry = database->mail_storage.users[slot];
      38           3 :         entry->conn = database_open(log, dbfile, SQLITE_OPEN_READWRITE);
      39           3 :         if(!entry->conn){
      40             :                 //this automatically makes the slot reusable (conn = NULL)
      41           1 :                 logprintf(log, LOG_WARNING, "Failed to connect to user database file %s\n", dbfile);
      42           1 :                 return -1;
      43             :         }
      44             : 
      45           2 :         entry->mailbox = database_prepare(log, entry->conn, INSERT_USER_MAILBOX);
      46           2 :         if(!entry->mailbox){
      47           0 :                 logprintf(log, LOG_ERROR, "Failed to create user mailbox insert query\n");
      48             :                 //close the database connection and return the slot
      49           0 :                 sqlite3_close(entry->conn);
      50           0 :                 entry->conn = NULL;
      51           0 :                 return -1;
      52             :         }
      53             : 
      54           2 :         entry->file_name = common_strdup(dbfile);
      55           2 :         if(!entry->file_name){
      56           0 :                 logprintf(log, LOG_ERROR, "Failed to copy user storage file name\n");
      57             :                 //close the database connection and return the slot
      58           0 :                 sqlite3_finalize(entry->mailbox);
      59           0 :                 sqlite3_close(entry->conn);
      60           0 :                 entry->conn = NULL;
      61           0 :                 return -1;
      62             :         }
      63             : 
      64           2 :         return 0;
      65             : }
      66             : 
      67           5 : int database_detach(LOGGER log, USER_DATABASE* db){
      68           5 :         USER_DATABASE empty_db = {
      69             :                 .conn = NULL,
      70             :                 .active = false,
      71             :                 .mailbox = NULL,
      72             :                 .file_name = NULL
      73             :         };
      74             : 
      75           5 :         if(db->conn){
      76             :                 //TODO error check these
      77           2 :                 sqlite3_finalize(db->mailbox);
      78           2 :                 sqlite3_close(db->conn);
      79           2 :                 free(db->file_name);
      80             :         }
      81             : 
      82           5 :         *db = empty_db;
      83             : 
      84           5 :         return 0;
      85             : }
      86             : 
      87          12 : USER_DATABASE* database_userdb(LOGGER log, DATABASE* database, char* filename){
      88             :         unsigned i;
      89             : 
      90          12 :         if(!database->mail_storage.users){
      91           2 :                 return NULL;
      92             :         }
      93             : 
      94          18 :         for(i = 0; database->mail_storage.users[i]; i++){
      95          13 :                 if(database->mail_storage.users[i]->file_name && !strcmp(database->mail_storage.users[i]->file_name, filename)){
      96           5 :                         return database->mail_storage.users[i];
      97             :                 }
      98             :         }
      99             : 
     100           5 :         logprintf(log, LOG_INFO, "User storage queried for unknown database %s\n", filename);
     101           5 :         return NULL;
     102             : }
     103             : 
     104           4 : int database_refresh(LOGGER log, DATABASE* database){
     105           4 :         int status, rv = 0;
     106             :         unsigned i;
     107             : 
     108           4 :         char* QUERY_USER_DATABASES = "SELECT user_database FROM main.users WHERE user_database NOT NULL GROUP BY user_database;";
     109             : 
     110           4 :         sqlite3_stmt* select_dbs = database_prepare(log, database->conn, QUERY_USER_DATABASES);
     111           4 :         if(!select_dbs){
     112           0 :                 logprintf(log, LOG_ERROR, "Failed to prepare user storage management statement\n");
     113           0 :                 return -1;
     114             :         }
     115             : 
     116           4 :         if(database->mail_storage.users){
     117           5 :                 for(i = 0; database->mail_storage.users[i]; i++){
     118           3 :                         database->mail_storage.users[i]->active = false;
     119             :                 }
     120             :         }
     121             : 
     122             :         do{
     123             :                 //fetch user database
     124           6 :                 status = sqlite3_step(select_dbs);
     125           6 :                 switch(status){
     126             :                         case SQLITE_ROW:
     127             :                                 //if not attached, attach
     128           3 :                                 if(!database_userdb(log, database, (char*)sqlite3_column_text(select_dbs, 0))){
     129             :                                         //attach
     130           3 :                                         if(database_attach(log, database, (char*)sqlite3_column_text(select_dbs, 0)) < 0){
     131           1 :                                                 logprintf(log, LOG_ERROR, "Failed to attach database: %s\n", sqlite3_errmsg(database->conn));
     132           1 :                                                 status = SQLITE_ERROR;
     133           1 :                                                 rv = -1;
     134             :                                         }
     135             :                                         else{
     136             :                                                 //database seems to have been attached ok, mark it active
     137           2 :                                                 database_userdb(log, database, (char*)sqlite3_column_text(select_dbs, 0))->active = true;
     138             :                                         }
     139             :                                 }
     140           3 :                                 break;
     141             :                         case SQLITE_DONE:
     142             :                                 //traversed all databases
     143           3 :                                 break;
     144             :                         default:
     145           0 :                                 logprintf(log, LOG_ERROR, "User storage database initialization failed: %s\n", sqlite3_errmsg(database->conn));
     146           0 :                                 rv = -1;
     147           0 :                                 break;
     148             :                 }
     149             :         }
     150           6 :         while(status == SQLITE_ROW);
     151             : 
     152             :         //detach inactive databases
     153           4 :         if(database->mail_storage.users){
     154           8 :                 for(i = 0; database->mail_storage.users[i]; i++){
     155           5 :                         if(!database->mail_storage.users[i]->active){
     156             :                                 //FIXME check this for return value
     157           3 :                                 database_detach(log, database->mail_storage.users[i]);
     158             :                         }
     159             :                 }
     160             :         }
     161             : 
     162           4 :         sqlite3_finalize(select_dbs);
     163           4 :         return rv;
     164             : }
     165             : 
     166           1 : int database_initialize(LOGGER log, DATABASE* database){
     167           1 :         char* QUERY_ADDRESS_INFO = "SELECT address_router, address_route FROM main.addresses WHERE ? LIKE address_expression ORDER BY address_order DESC;";
     168           1 :         char* INSERT_MASTER_MAILBOX = "INSERT INTO main.mailbox (mail_user, mail_ident, mail_envelopeto, mail_envelopefrom, mail_submitter, mail_proto, mail_data) VALUES (?, ?, ?, ?, ?, ?, ?);";
     169           1 :         char* INSERT_MASTER_OUTBOX = "INSERT INTO main.outbox (mail_remote, mail_envelopefrom, mail_envelopeto, mail_submitter, mail_data) VALUES (?, ?, ?, ?, ?);";
     170           1 :         char* QUERY_ORIGINATING_ROUTER = "SELECT smtpd_router, smtpd_route FROM main.smtpd WHERE smtpd_user = ?;";
     171             : 
     172             :         //this query has to conform to the auth_validate contract (the first 2 columns are fixed)
     173           1 :         char* QUERY_AUTHENTICATION_DATA = "SELECT user_authdata, user_alias, user_database FROM main.users WHERE user_name = ?;";
     174             : 
     175             :         //check the database schema version
     176           1 :         if(database_schema_version(log, database->conn)!=CMAIL_CURRENT_SCHEMA_VERSION){
     177           0 :                 logprintf(log, LOG_ERROR, "The database schema is at another version than required for this build\n");
     178           0 :                 return -1;
     179             :         }
     180             : 
     181           1 :         database->query_authdata = database_prepare(log, database->conn, QUERY_AUTHENTICATION_DATA);
     182           1 :         database->query_address = database_prepare(log, database->conn, QUERY_ADDRESS_INFO);
     183           1 :         database->query_outbound_router = database_prepare(log, database->conn, QUERY_ORIGINATING_ROUTER);
     184           1 :         database->mail_storage.mailbox_master = database_prepare(log, database->conn, INSERT_MASTER_MAILBOX);
     185           1 :         database->mail_storage.outbox_master = database_prepare(log, database->conn, INSERT_MASTER_OUTBOX);
     186             : 
     187           1 :         if(!database->query_authdata){
     188           0 :                 logprintf(log, LOG_ERROR, "Failed to prepare authentication data query\n");
     189           0 :                 return -1;
     190             :         }
     191             : 
     192           1 :         if(!database->query_address){
     193           0 :                 logprintf(log, LOG_ERROR, "Failed to prepare address query statement\n");
     194           0 :                 return -1;
     195             :         }
     196             : 
     197           1 :         if(!database->query_outbound_router){
     198           0 :                 logprintf(log, LOG_ERROR, "Failed to prepare outbound router query statement\n");
     199           0 :                 return -1;
     200             :         }
     201             : 
     202           1 :         if(!database->mail_storage.mailbox_master || !database->mail_storage.outbox_master){
     203           0 :                 logprintf(log, LOG_ERROR, "Failed to prepare mail storage statement\n");
     204           0 :                 return -1;
     205             :         }
     206             : 
     207           1 :         return database_refresh(log, database);
     208             : }
     209             : 
     210           2 : void database_free(LOGGER log, DATABASE* database){
     211             :         unsigned i;
     212             : 
     213             :         //FIXME check for SQLITE_BUSY here
     214           2 :         if(database->conn){
     215           2 :                 sqlite3_finalize(database->query_authdata);
     216           2 :                 sqlite3_finalize(database->query_address);
     217           2 :                 sqlite3_finalize(database->query_outbound_router);
     218           2 :                 sqlite3_finalize(database->mail_storage.mailbox_master);
     219           2 :                 sqlite3_finalize(database->mail_storage.outbox_master);
     220             : 
     221             :                 //finalize user storage
     222           2 :                 if(database->mail_storage.users){
     223           3 :                         for(i = 0; database->mail_storage.users[i]; i++){
     224             :                                 //FIXME user return value
     225           2 :                                 database_detach(log, database->mail_storage.users[i]);
     226             : 
     227           2 :                                 free(database->mail_storage.users[i]);
     228             :                         }
     229           1 :                         free(database->mail_storage.users);
     230             :                 }
     231             : 
     232           2 :                 sqlite3_close(database->conn);
     233           2 :                 database->conn = NULL;
     234             :         }
     235           2 : }

Generated by: LCOV version 1.11