off by one bug in string length; CR 1159
[tprouty/samba.git] / source / printing / notify.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.2
4    printing backend routines
5    Copyright (C) Tim Potter, 2002
6    Copyright (C) Gerald Carter,         2002
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "printing.h"
24
25 static TALLOC_CTX *send_ctx;
26
27 static struct notify_queue {
28         struct notify_queue *next, *prev;
29         struct spoolss_notify_msg *msg;
30         char *buf;
31         size_t buflen;
32 } *notify_queue_head = NULL;
33
34
35 static BOOL create_send_ctx(void)
36 {
37         if (!send_ctx)
38                 send_ctx = talloc_init("print notify queue");
39
40         if (!send_ctx)
41                 return False;
42
43         return True;
44 }
45
46 /****************************************************************************
47  Turn a queue name into a snum.
48 ****************************************************************************/
49
50 int print_queue_snum(const char *qname)
51 {
52         int snum = lp_servicenumber(qname);
53         if (snum == -1 || !lp_print_ok(snum))
54                 return -1;
55         return snum;
56 }
57
58 /*******************************************************************
59  Used to decide if we need a short select timeout.
60 *******************************************************************/
61
62 BOOL print_notify_messages_pending(void)
63 {
64         return (notify_queue_head != NULL);
65 }
66
67 /*******************************************************************
68  Flatten data into a message.
69 *******************************************************************/
70
71 static BOOL flatten_message(struct notify_queue *q)
72 {
73         struct spoolss_notify_msg *msg = q->msg;
74         char *buf = NULL;
75         size_t buflen = 0, len;
76
77 again:
78         len = 0;
79
80         /* Pack header */
81
82         len += tdb_pack(buf + len, buflen - len, "f", msg->printer);
83
84         len += tdb_pack(buf + len, buflen - len, "ddddd",
85                         msg->type, msg->field, msg->id, msg->len, msg->flags);
86
87         /* Pack data */
88
89         if (msg->len == 0)
90                 len += tdb_pack(buf + len, buflen - len, "dd",
91                                 msg->notify.value[0], msg->notify.value[1]);
92         else
93                 len += tdb_pack(buf + len, buflen - len, "B",
94                                 msg->len, msg->notify.data);
95
96         if (buflen != len) {
97                 buf = talloc_realloc(send_ctx, buf, len);
98                 if (!buf)
99                         return False;
100                 buflen = len;
101                 goto again;
102         }
103
104         q->buf = buf;
105         q->buflen = buflen;
106
107         return True;
108 }
109
110 /*******************************************************************
111  Send the batched messages - on a per-printer basis.
112 *******************************************************************/
113
114 static void print_notify_send_messages_to_printer(const char *printer, unsigned int timeout)
115 {
116         char *buf;
117         struct notify_queue *pq, *pq_next;
118         size_t msg_count = 0, offset = 0;
119         size_t num_pids = 0;
120         size_t i;
121         pid_t *pid_list = NULL;
122
123         /* Count the space needed to send the messages. */
124         for (pq = notify_queue_head; pq; pq = pq->next) {
125                 if (strequal(printer, pq->msg->printer)) {
126                         if (!flatten_message(pq)) {
127                                 DEBUG(0,("print_notify_send_messages: Out of memory\n"));
128                                 talloc_destroy_pool(send_ctx);
129                                 return;
130                         }
131                         offset += (pq->buflen + 4);
132                         msg_count++;
133                 }       
134         }
135         offset += 4; /* For count. */
136
137         buf = talloc(send_ctx, offset);
138         if (!buf) {
139                 DEBUG(0,("print_notify_send_messages: Out of memory\n"));
140                 talloc_destroy_pool(send_ctx);
141                 return;
142         }
143
144         offset = 0;
145         SIVAL(buf,offset,msg_count);
146         offset += 4;
147         for (pq = notify_queue_head; pq; pq = pq_next) {
148                 pq_next = pq->next;
149
150                 if (strequal(printer, pq->msg->printer)) {
151                         SIVAL(buf,offset,pq->buflen);
152                         offset += 4;
153                         memcpy(buf + offset, pq->buf, pq->buflen);
154                         offset += pq->buflen;
155
156                         /* Remove from list. */
157                         DLIST_REMOVE(notify_queue_head, pq);
158                 }
159         }
160
161         DEBUG(5, ("print_notify_send_messages_to_printer: sending %d print notify message%s to printer %s\n", 
162                   msg_count, msg_count != 1 ? "s" : "", printer));
163
164         /*
165          * Get the list of PID's to send to.
166          */
167
168         if (!print_notify_pid_list(printer, send_ctx, &num_pids, &pid_list))
169                 return;
170
171         for (i = 0; i < num_pids; i++)
172                 message_send_pid_with_timeout(pid_list[i], MSG_PRINTER_NOTIFY2, buf, offset, True, timeout);
173 }
174
175 /*******************************************************************
176  Actually send the batched messages.
177 *******************************************************************/
178
179 void print_notify_send_messages(unsigned int timeout)
180 {
181         if (!print_notify_messages_pending())
182                 return;
183
184         if (!create_send_ctx())
185                 return;
186
187         while (print_notify_messages_pending())
188                 print_notify_send_messages_to_printer(notify_queue_head->msg->printer, timeout);
189
190         talloc_destroy_pool(send_ctx);
191 }
192
193 /*******************************************************************
194  Batch up print notify messages.
195 *******************************************************************/
196
197 static void send_spoolss_notify2_msg(struct spoolss_notify_msg *msg)
198 {
199         struct notify_queue *pnqueue, *tmp_ptr;
200
201         /*
202          * Ensure we only have one message unique to each name/type/field/id/flags
203          * tuple. There is no point in sending multiple messages that match
204          * as they will just cause flickering updates in the client.
205          */
206
207         for (tmp_ptr = notify_queue_head; tmp_ptr; tmp_ptr = tmp_ptr->next) {
208                 if (tmp_ptr->msg->type == msg->type &&
209                                 tmp_ptr->msg->field == msg->field &&
210                                 tmp_ptr->msg->id == msg->id &&
211                                 tmp_ptr->msg->flags == msg->flags &&
212                                 strequal(tmp_ptr->msg->printer, msg->printer)) {
213
214                         DEBUG(5, ("send_spoolss_notify2_msg: replacing message 0x%02x/0x%02x for printer %s \
215 in notify_queue\n", msg->type, msg->field, msg->printer));
216
217                         tmp_ptr->msg = msg;
218                         return;
219                 }
220         }
221
222         /* Store the message on the pending queue. */
223
224         pnqueue = talloc(send_ctx, sizeof(*pnqueue));
225         if (!pnqueue) {
226                 DEBUG(0,("send_spoolss_notify2_msg: Out of memory.\n"));
227                 return;
228         }
229
230         pnqueue->msg = msg;
231         pnqueue->buf = NULL;
232         pnqueue->buflen = 0;
233
234         DEBUG(5, ("send_spoolss_notify2_msg: appending message 0x%02x/0x%02x for printer %s \
235 to notify_queue_head\n", msg->type, msg->field, msg->printer));
236
237         /*
238          * Note we add to the end of the list to ensure
239          * the messages are sent in the order they were received. JRA.
240          */
241
242         DLIST_ADD_END(notify_queue_head, pnqueue, tmp_ptr);
243 }
244
245 static void send_notify_field_values(const char *printer_name, uint32 type,
246                                      uint32 field, uint32 id, uint32 value1, 
247                                      uint32 value2, uint32 flags)
248 {
249         struct spoolss_notify_msg *msg;
250
251         if (lp_disable_spoolss())
252                 return;
253
254         if (!create_send_ctx())
255                 return;
256
257         msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg));
258         if (!msg)
259                 return;
260
261         ZERO_STRUCTP(msg);
262
263         fstrcpy(msg->printer, printer_name);
264         msg->type = type;
265         msg->field = field;
266         msg->id = id;
267         msg->notify.value[0] = value1;
268         msg->notify.value[1] = value2;
269         msg->flags = flags;
270
271         send_spoolss_notify2_msg(msg);
272 }
273
274 static void send_notify_field_buffer(const char *printer_name, uint32 type,
275                                      uint32 field, uint32 id, uint32 len,
276                                      char *buffer)
277 {
278         struct spoolss_notify_msg *msg;
279
280         if (lp_disable_spoolss())
281                 return;
282
283         if (!create_send_ctx())
284                 return;
285
286         msg = (struct spoolss_notify_msg *)talloc(send_ctx, sizeof(struct spoolss_notify_msg));
287         if (!msg)
288                 return;
289
290         ZERO_STRUCTP(msg);
291
292         fstrcpy(msg->printer, printer_name);
293         msg->type = type;
294         msg->field = field;
295         msg->id = id;
296         msg->len = len;
297         msg->notify.data = buffer;
298
299         send_spoolss_notify2_msg(msg);
300 }
301
302 /* Send a message that the printer status has changed */
303
304 void notify_printer_status_byname(const char *printer_name, uint32 status)
305 {
306         /* Printer status stored in value1 */
307
308         send_notify_field_values(printer_name, PRINTER_NOTIFY_TYPE, 
309                                  PRINTER_NOTIFY_STATUS, 0, 
310                                  status, 0, 0);
311 }
312
313 void notify_printer_status(int snum, uint32 status)
314 {
315         const char *printer_name = SERVICE(snum); 
316
317         if (printer_name)
318                 notify_printer_status_byname(printer_name, status);
319 }
320
321 void notify_job_status_byname(const char *printer_name, uint32 jobid, uint32 status,
322                               uint32 flags)
323 {
324         /* Job id stored in id field, status in value1 */
325
326         send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
327                                  JOB_NOTIFY_STATUS, jobid,
328                                  status, 0, flags);
329 }
330
331 void notify_job_status(int snum, uint32 jobid, uint32 status)
332 {
333         const char *printer_name = SERVICE(snum);
334
335         notify_job_status_byname(printer_name, jobid, status, 0);
336 }
337
338 void notify_job_total_bytes(int snum, uint32 jobid, uint32 size)
339 {
340         const char *printer_name = SERVICE(snum);
341
342         /* Job id stored in id field, status in value1 */
343
344         send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
345                                  JOB_NOTIFY_TOTAL_BYTES, jobid,
346                                  size, 0, 0);
347 }
348
349 void notify_job_total_pages(int snum, uint32 jobid, uint32 pages)
350 {
351         const char *printer_name = SERVICE(snum);
352
353         /* Job id stored in id field, status in value1 */
354
355         send_notify_field_values(printer_name, JOB_NOTIFY_TYPE,
356                                  JOB_NOTIFY_TOTAL_PAGES, jobid,
357                                  pages, 0, 0);
358 }
359
360 void notify_job_username(int snum, uint32 jobid, char *name)
361 {
362         const char *printer_name = SERVICE(snum);
363
364         send_notify_field_buffer(
365                 printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_USER_NAME,
366                 jobid, strlen(name) + 1, name);
367 }
368
369 void notify_job_name(int snum, uint32 jobid, char *name)
370 {
371         const char *printer_name = SERVICE(snum);
372
373         send_notify_field_buffer(
374                 printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_DOCUMENT,
375                 jobid, strlen(name) + 1, name);
376 }
377
378 void notify_job_submitted(int snum, uint32 jobid, time_t submitted)
379 {
380         const char *printer_name = SERVICE(snum);
381
382         send_notify_field_buffer(
383                 printer_name, JOB_NOTIFY_TYPE, JOB_NOTIFY_SUBMITTED,
384                 jobid, sizeof(submitted), (char *)&submitted);
385 }
386
387 void notify_printer_driver(int snum, char *driver_name)
388 {
389         const char *printer_name = SERVICE(snum);
390
391         send_notify_field_buffer(
392                 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME,
393                 snum, strlen(driver_name) + 1, driver_name);
394 }
395
396 void notify_printer_comment(int snum, char *comment)
397 {
398         const char *printer_name = SERVICE(snum);
399
400         send_notify_field_buffer(
401                 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT,
402                 snum, strlen(comment) + 1, comment);
403 }
404
405 void notify_printer_sharename(int snum, char *share_name)
406 {
407         const char *printer_name = SERVICE(snum);
408
409         send_notify_field_buffer(
410                 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME,
411                 snum, strlen(share_name) + 1, share_name);
412 }
413
414 void notify_printer_port(int snum, char *port_name)
415 {
416         const char *printer_name = SERVICE(snum);
417
418         send_notify_field_buffer(
419                 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME,
420                 snum, strlen(port_name) + 1, port_name);
421 }
422
423 void notify_printer_location(int snum, char *location)
424 {
425         const char *printer_name = SERVICE(snum);
426
427         send_notify_field_buffer(
428                 printer_name, PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION,
429                 snum, strlen(location) + 1, location);
430 }
431
432 void notify_printer_byname( char *printername, uint32 change, char *value )
433 {
434         int snum = print_queue_snum(printername);
435         int type = PRINTER_NOTIFY_TYPE;
436         
437         if ( snum == -1 )
438                 return;
439                 
440         send_notify_field_buffer( printername, type, change, snum, strlen(value)+1, value );
441
442
443
444 /****************************************************************************
445  Return a malloced list of pid_t's that are interested in getting update
446  messages on this print queue. Used in printing/notify to send the messages.
447 ****************************************************************************/
448
449 BOOL print_notify_pid_list(const char *printername, TALLOC_CTX *mem_ctx, size_t *p_num_pids, pid_t **pp_pid_list)
450 {
451         struct tdb_print_db *pdb = NULL;
452         TDB_CONTEXT *tdb = NULL;
453         TDB_DATA data;
454         BOOL ret = True;
455         size_t i, num_pids, offset;
456         pid_t *pid_list;
457
458         *p_num_pids = 0;
459         *pp_pid_list = NULL;
460
461         pdb = get_print_db_byname(printername);
462         if (!pdb)
463                 return False;
464         tdb = pdb->tdb;
465
466         if (tdb_read_lock_bystring(tdb, NOTIFY_PID_LIST_KEY, 10) == -1) {
467                 DEBUG(0,("print_notify_pid_list: Failed to lock printer %s database\n",
468                                         printername));
469                 if (pdb)
470                         release_print_db(pdb);
471                 return False;
472         }
473
474         data = get_printer_notify_pid_list( tdb, printername, True );
475
476         if (!data.dptr) {
477                 ret = True;
478                 goto done;
479         }
480
481         num_pids = data.dsize / 8;
482
483         if ((pid_list = (pid_t *)talloc(mem_ctx, sizeof(pid_t) * num_pids)) == NULL) {
484                 ret = False;
485                 goto done;
486         }
487
488         for( i = 0, offset = 0; offset < data.dsize; offset += 8, i++)
489                 pid_list[i] = (pid_t)IVAL(data.dptr, offset);
490
491         *pp_pid_list = pid_list;
492         *p_num_pids = num_pids;
493
494         ret = True;
495
496   done:
497
498         tdb_read_unlock_bystring(tdb, NOTIFY_PID_LIST_KEY);
499         if (pdb)
500                 release_print_db(pdb);
501         SAFE_FREE(data.dptr);
502         return ret;
503 }