s3:printing: convert printer_list to use dbwrap wrapper functions
[obnox/samba/samba-obnox.git] / source3 / printing / printer_list.c
1 /*
2    Unix SMB/CIFS implementation.
3    Share Database of available printers.
4    Copyright (C) Simo Sorce 2010
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "dbwrap/dbwrap.h"
23 #include "dbwrap/dbwrap_open.h"
24 #include "util_tdb.h"
25 #include "printer_list.h"
26
27 #define PL_DB_NAME() lock_path("printer_list.tdb")
28 #define PL_KEY_PREFIX "PRINTERLIST/PRN/"
29 #define PL_KEY_FORMAT PL_KEY_PREFIX"%s"
30 #define PL_TIMESTAMP_KEY "PRINTERLIST/GLOBAL/LAST_REFRESH"
31 #define PL_DATA_FORMAT "ddPPP"
32 #define PL_TSTAMP_FORMAT "dd"
33
34 static struct db_context *get_printer_list_db(void)
35 {
36         static struct db_context *db;
37
38         if (db != NULL) {
39                 return db;
40         }
41         db = db_open(NULL, PL_DB_NAME(), 0,
42                      TDB_DEFAULT|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
43                      O_RDWR|O_CREAT, 0644);
44         return db;
45 }
46
47 bool printer_list_parent_init(void)
48 {
49         struct db_context *db;
50
51         /*
52          * Open the tdb in the parent process (smbd) so that our
53          * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
54          * work.
55          */
56
57         db = get_printer_list_db();
58         if (db == NULL) {
59                 DEBUG(1, ("could not open Printer List Database: %s\n",
60                           strerror(errno)));
61                 return false;
62         }
63         return true;
64 }
65
66 NTSTATUS printer_list_get_printer(TALLOC_CTX *mem_ctx,
67                                   const char *name,
68                                   const char **comment,
69                                   const char **location,
70                                   time_t *last_refresh)
71 {
72         struct db_context *db;
73         char *key;
74         TDB_DATA data;
75         uint32_t time_h, time_l;
76         char *nstr = NULL;
77         char *cstr = NULL;
78         char *lstr = NULL;
79         NTSTATUS status;
80         int ret;
81
82         db = get_printer_list_db();
83         if (db == NULL) {
84                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
85         }
86
87         key = talloc_asprintf(mem_ctx, PL_KEY_FORMAT, name);
88         if (!key) {
89                 DEBUG(0, ("Failed to allocate key name!\n"));
90                 return NT_STATUS_NO_MEMORY;
91         }
92
93         status = dbwrap_fetch_bystring_upper(db, key, key, &data);
94         if (!NT_STATUS_IS_OK(status)) {
95                 DEBUG(1, ("Failed to fetch record!\n"));
96                 goto done;
97         }
98
99         ret = tdb_unpack(data.dptr, data.dsize,
100                          PL_DATA_FORMAT,
101                          &time_h, &time_l, &nstr, &cstr, &lstr);
102         if (ret == -1) {
103                 DEBUG(1, ("Failed to un pack printer data"));
104                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
105                 goto done;
106         }
107
108         if (last_refresh) {
109                 *last_refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
110         }
111
112         if (comment) {
113                 *comment = talloc_strdup(mem_ctx, cstr);
114                 if (!*comment) {
115                         DEBUG(1, ("Failed to strdup comment!\n"));
116                         status = NT_STATUS_NO_MEMORY;
117                         goto done;
118                 }
119         }
120
121         if (location) {
122                 *location = talloc_strdup(mem_ctx, lstr);
123                 if (*location == NULL) {
124                         DEBUG(1, ("Failed to strdup location!\n"));
125                         status = NT_STATUS_NO_MEMORY;
126                         goto done;
127                 }
128         }
129
130         status = NT_STATUS_OK;
131
132 done:
133         SAFE_FREE(nstr);
134         SAFE_FREE(cstr);
135         TALLOC_FREE(key);
136         return status;
137 }
138
139 NTSTATUS printer_list_set_printer(TALLOC_CTX *mem_ctx,
140                                   const char *name,
141                                   const char *comment,
142                                   const char *location,
143                                   time_t last_refresh)
144 {
145         struct db_context *db;
146         char *key;
147         TDB_DATA data;
148         uint64_t time_64;
149         uint32_t time_h, time_l;
150         const char *str = NULL;
151         const char *str2 = NULL;
152         NTSTATUS status;
153         int len;
154
155         db = get_printer_list_db();
156         if (db == NULL) {
157                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
158         }
159
160         key = talloc_asprintf(mem_ctx, PL_KEY_FORMAT, name);
161         if (!key) {
162                 DEBUG(0, ("Failed to allocate key name!\n"));
163                 return NT_STATUS_NO_MEMORY;
164         }
165
166         if (comment) {
167                 str = comment;
168         } else {
169                 str = "";
170         }
171
172         if (location) {
173                 str2 = location;
174         } else {
175                 str2 = "";
176         }
177
178
179         time_64 = last_refresh;
180         time_l = time_64 & 0xFFFFFFFFL;
181         time_h = time_64 >> 32;
182
183         len = tdb_pack(NULL, 0, PL_DATA_FORMAT, time_h, time_l, name, str, str2);
184
185         data.dptr = talloc_array(key, uint8_t, len);
186         if (!data.dptr) {
187                 DEBUG(0, ("Failed to allocate tdb data buffer!\n"));
188                 status = NT_STATUS_NO_MEMORY;
189                 goto done;
190         }
191         data.dsize = len;
192
193         len = tdb_pack(data.dptr, data.dsize,
194                        PL_DATA_FORMAT, time_h, time_l, name, str);
195
196         status = dbwrap_store_bystring_upper(db, key, data, TDB_REPLACE);
197
198 done:
199         TALLOC_FREE(key);
200         return status;
201 }
202
203 NTSTATUS printer_list_get_last_refresh(time_t *last_refresh)
204 {
205         struct db_context *db;
206         TDB_DATA data;
207         uint32_t time_h, time_l;
208         NTSTATUS status;
209         int ret;
210
211         db = get_printer_list_db();
212         if (db == NULL) {
213                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
214         }
215
216         ZERO_STRUCT(data);
217
218         status = dbwrap_fetch_bystring(db, talloc_tos(), PL_TIMESTAMP_KEY, &data);
219         if (!NT_STATUS_IS_OK(status)) {
220                 DEBUG(1, ("Failed to fetch record!\n"));
221                 goto done;
222         }
223
224         ret = tdb_unpack(data.dptr, data.dsize,
225                          PL_TSTAMP_FORMAT, &time_h, &time_l);
226         if (ret == -1) {
227                 DEBUG(1, ("Failed to un pack printer data"));
228                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
229                 goto done;
230         }
231
232         *last_refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
233         status = NT_STATUS_OK;
234
235 done:
236         return status;
237 }
238
239 NTSTATUS printer_list_mark_reload(void)
240 {
241         struct db_context *db;
242         TDB_DATA data;
243         uint32_t time_h, time_l;
244         time_t now = time_mono(NULL);
245         NTSTATUS status;
246         int len;
247
248         db = get_printer_list_db();
249         if (db == NULL) {
250                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
251         }
252
253         time_l = ((uint64_t)now) & 0xFFFFFFFFL;
254         time_h = ((uint64_t)now) >> 32;
255
256         len = tdb_pack(NULL, 0, PL_TSTAMP_FORMAT, time_h, time_l);
257
258         data.dptr = talloc_array(talloc_tos(), uint8_t, len);
259         if (!data.dptr) {
260                 DEBUG(0, ("Failed to allocate tdb data buffer!\n"));
261                 status = NT_STATUS_NO_MEMORY;
262                 goto done;
263         }
264         data.dsize = len;
265
266         len = tdb_pack(data.dptr, data.dsize,
267                        PL_TSTAMP_FORMAT, time_h, time_l);
268
269         status = dbwrap_store_bystring(db, PL_TIMESTAMP_KEY,
270                                                 data, TDB_REPLACE);
271
272 done:
273         TALLOC_FREE(data.dptr);
274         return status;
275 }
276
277 typedef int (printer_list_trv_fn_t)(struct db_record *, void *);
278
279 static NTSTATUS printer_list_traverse(printer_list_trv_fn_t *fn,
280                                                 void *private_data)
281 {
282         struct db_context *db;
283         NTSTATUS status;
284
285         db = get_printer_list_db();
286         if (db == NULL) {
287                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
288         }
289
290         status = dbwrap_traverse(db, fn, private_data, NULL);
291
292         return status;
293 }
294
295 struct printer_list_clean_state {
296         time_t last_refresh;
297         NTSTATUS status;
298 };
299
300 static int printer_list_clean_fn(struct db_record *rec, void *private_data)
301 {
302         struct printer_list_clean_state *state =
303                         (struct printer_list_clean_state *)private_data;
304         uint32_t time_h, time_l;
305         time_t refresh;
306         char *name;
307         char *comment;
308         char *location;
309         int ret;
310         TDB_DATA key;
311         TDB_DATA value;
312
313         key = dbwrap_record_get_key(rec);
314
315         /* skip anything that does not contain PL_DATA_FORMAT data */
316         if (strncmp((char *)key.dptr,
317                     PL_KEY_PREFIX, sizeof(PL_KEY_PREFIX)-1)) {
318                 return 0;
319         }
320
321         value = dbwrap_record_get_value(rec);
322
323         ret = tdb_unpack(value.dptr, value.dsize,
324                          PL_DATA_FORMAT, &time_h, &time_l, &name, &comment,
325                          &location);
326         if (ret == -1) {
327                 DEBUG(1, ("Failed to un pack printer data"));
328                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
329                 return -1;
330         }
331
332         SAFE_FREE(name);
333         SAFE_FREE(comment);
334         SAFE_FREE(location);
335
336         refresh = (time_t)(((uint64_t)time_h << 32) + time_l);
337
338         if (refresh < state->last_refresh) {
339                 state->status = dbwrap_record_delete(rec);
340                 if (!NT_STATUS_IS_OK(state->status)) {
341                         return -1;
342                 }
343         }
344
345         return 0;
346 }
347
348 NTSTATUS printer_list_clean_old(void)
349 {
350         struct printer_list_clean_state state;
351         NTSTATUS status;
352
353         status = printer_list_get_last_refresh(&state.last_refresh);
354         if (!NT_STATUS_IS_OK(status)) {
355                 return status;
356         }
357
358         state.status = NT_STATUS_OK;
359
360         status = printer_list_traverse(printer_list_clean_fn, &state);
361         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
362             !NT_STATUS_IS_OK(state.status)) {
363                 status = state.status;
364         }
365
366         return status;
367 }
368
369 struct printer_list_exec_state {
370         void (*fn)(const char *, const char *, const char *, void *);
371         void *private_data;
372         NTSTATUS status;
373 };
374
375 static int printer_list_exec_fn(struct db_record *rec, void *private_data)
376 {
377         struct printer_list_exec_state *state =
378                         (struct printer_list_exec_state *)private_data;
379         uint32_t time_h, time_l;
380         char *name;
381         char *comment;
382         char *location;
383         int ret;
384         TDB_DATA key;
385         TDB_DATA value;
386
387         key = dbwrap_record_get_key(rec);
388
389         /* always skip PL_TIMESTAMP_KEY key */
390         if (strequal((const char *)key.dptr, PL_TIMESTAMP_KEY)) {
391                 return 0;
392         }
393
394         value = dbwrap_record_get_value(rec);
395
396         ret = tdb_unpack(value.dptr, value.dsize,
397                          PL_DATA_FORMAT, &time_h, &time_l, &name, &comment,
398                          &location);
399         if (ret == -1) {
400                 DEBUG(1, ("Failed to un pack printer data"));
401                 state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
402                 return -1;
403         }
404
405         state->fn(name, comment, location, state->private_data);
406
407         SAFE_FREE(name);
408         SAFE_FREE(comment);
409         SAFE_FREE(location);
410         return 0;
411 }
412
413 NTSTATUS printer_list_run_fn(void (*fn)(const char *, const char *, const char *, void *),
414                              void *private_data)
415 {
416         struct printer_list_exec_state state;
417         NTSTATUS status;
418
419         state.fn = fn;
420         state.private_data = private_data;
421         state.status = NT_STATUS_OK;
422
423         status = printer_list_traverse(printer_list_exec_fn, &state);
424         if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL) &&
425             !NT_STATUS_IS_OK(state.status)) {
426                 status = state.status;
427         }
428
429         return status;
430 }