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