updated the 3.0 branch from the head branch - ready for alpha18
[kai/samba.git] / source3 / rpc_server / srv_spoolss_nt.c
index 322efa22b5a07bc9381edcba10eabb3fe18cb4d6..68c792f8b0713f77c83523ef54ca085106e2306e 100644 (file)
@@ -5,7 +5,7 @@
  *  Copyright (C) Luke Kenneth Casson Leighton 1996-2000,
  *  Copyright (C) Jean François Micouleau      1998-2000,
  *  Copyright (C) Jeremy Allison                   2001,
- *  Copyright (C) Gerald Carter                       2000-2001,
+ *  Copyright (C) Gerald Carter                       2000-2002,
  *  Copyright (C) Tim Potter                   2001-2002.
  *
  *  This program is free software; you can redistribute it and/or modify
 
 #include "includes.h"
 
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+/* #define EMULATE_WIN2K_HACK  1 */
+
 #ifndef MAX_OPEN_PRINTER_EXS
 #define MAX_OPEN_PRINTER_EXS 50
 #endif
 #define PRINTER_HANDLE_IS_PRINTER      0
 #define PRINTER_HANDLE_IS_PRINTSERVER  1
 
+/* Table to map the driver version */
+/* to OS */
+char * drv_ver_to_os[] = {
+       "WIN9X",   /* driver version/cversion 0 */
+       "",        /* unused ? */
+       "WINNT",   /* driver version/cversion 2 */
+       "WIN2K",   /* driver version/cversion 3 */
+};
+
 struct table_node {
        char    *long_archi;
        char    *short_archi;
@@ -68,6 +81,7 @@ typedef struct _Printer{
                SPOOL_NOTIFY_OPTION *option;
                POLICY_HND client_hnd;
                uint32 client_connected;
+               uint32 change;
        } notify;
        struct {
                fstring machine;
@@ -178,10 +192,10 @@ static void srv_spoolss_replycloseprinter(POLICY_HND *handle)
 
        /* if it's the last connection, deconnect the IPC$ share */
        if (smb_connections==1) {
-               if(!spoolss_disconnect_from_client(&cli))
-                       return;
-
-               message_deregister(MSG_PRINTER_NOTIFY);
+               cli_nt_session_close(&cli);
+               cli_ulogoff(&cli);
+               cli_shutdown(&cli);
+               message_deregister(MSG_PRINTER_NOTIFY2);
        }
 
        smb_connections--;
@@ -426,7 +440,7 @@ static BOOL set_printer_hnd_name(Printer_entry *Printer, char *handlename)
         * anymore, so I've simplified this loop greatly.  Here
         * we are just verifying that the printer name is a valid
         * printer service defined in smb.conf
-        *                          --jerry [Fri Feb 15 11:17:46 CST 2002] 
+        *                          --jerry [Fri Feb 15 11:17:46 CST 2002]
         */
 
        for (snum=0; snum<n_services; snum++) {
@@ -535,229 +549,417 @@ static BOOL alloc_buffer_size(NEW_BUFFER *buffer, uint32 buffer_size)
 
        return True;
 }
+
 /***************************************************************************
- Always give preference Printer_entry.notify.option over 
- Printer_entry.notify.flags.  Return True if we should send notification 
- events using SPOOLSS_RRPCN.  False means that we should use 
- SPOOLSS_ROUTERREPLYPRINTER.
+ check to see if the client motify handle is monitoring the notification
+ given by (notify_type, notify_field).
  **************************************************************************/
-static BOOL valid_notify_options(Printer_entry *printer)
+
+static BOOL is_monitoring_event_flags(uint32 flags, uint16 notify_type,
+                                     uint16 notify_field)
 {
-       if (printer->notify.option == NULL)
-               return False;
-               
        return True;
 }
 
-/***************************************************************************
- Simple check to see if the client motify handle is set to watch for events
- represented by 'flags'
- FIXME!!!! only a stub right now     --jerry
- **************************************************************************/
-static BOOL is_client_monitoring_event(Printer_entry *p, uint32 flags)
+static BOOL is_monitoring_event(Printer_entry *p, uint16 notify_type,
+                               uint16 notify_field)
 {
+       SPOOL_NOTIFY_OPTION *option = p->notify.option;
+       uint32 i, j;
+       
+       if (p->notify.flags)
+               return is_monitoring_event_flags(
+                       p->notify.flags, notify_type, notify_field);
 
-       return True;
-}
+       for (i = 0; i < option->count; i++) {
+               
+               /* Check match for notify_type */
+               
+               if (option->ctr.type[i].type != notify_type)
+                       continue;
 
-/***************************************************************************
- Server wrapper for cli_spoolss_routerreplyprinter() since the client 
- function can only send a single change notification at a time.
- FIXME!!!  only handles one change currently (PRINTER_CHANGE_SET_PRINTER_DRIVER)
- --jerry
- **************************************************************************/
-static WERROR srv_spoolss_routerreplyprinter (struct cli_state *reply_cli, TALLOC_CTX *mem_ctx,
-                                       POLICY_HND *pol, PRINTER_MESSAGE_INFO *info,
-                                       NT_PRINTER_INFO_LEVEL *printer)                         
-{
-       WERROR result;
-       uint32 condition = 0x0;
+               /* Check match for field */
+               
+               for (j = 0; j < option->ctr.type[i].count; j++) {
+                       if (option->ctr.type[i].fields[j] == notify_field) {
+                               return True;
+                       }
+               }
+       }
        
-       if (info->flags & PRINTER_MESSAGE_DRIVER)
-               condition = PRINTER_CHANGE_SET_PRINTER_DRIVER;
+       DEBUG(10, ("%s is not monitoring 0x%02x/0x%02x\n",
+                  (p->printer_type == PRINTER_HANDLE_IS_PRINTER) ?
+                  p->dev.handlename : p->dev.printerservername,
+                  notify_type, notify_field));
        
-       result = cli_spoolss_routerreplyprinter(reply_cli, mem_ctx, pol, condition, 
-                       printer->info_2->changeid);
+       return False;
+}
 
-       return result;
+/* Convert a notification message to a SPOOL_NOTIFY_INFO_DATA struct */
+
+static void notify_one_value(struct spoolss_notify_msg *msg,
+                            SPOOL_NOTIFY_INFO_DATA *data,
+                            TALLOC_CTX *mem_ctx)
+{
+       data->notify_data.value[0] = msg->notify.value[0];
+       data->notify_data.value[1] = 0;
 }
 
-/***********************************************************************
- Wrapper around the decision of which RPC use to in the change 
- notification
- **********************************************************************/
-static WERROR srv_spoolss_send_event_to_client(Printer_entry* Printer, 
-       struct cli_state *send_cli,     PRINTER_MESSAGE_INFO *msg, 
-       NT_PRINTER_INFO_LEVEL *info)
+static void notify_string(struct spoolss_notify_msg *msg,
+                         SPOOL_NOTIFY_INFO_DATA *data,
+                         TALLOC_CTX *mem_ctx)
 {
-       WERROR result;
+       UNISTR2 unistr;
        
-       if (valid_notify_options(Printer)) {
-               /* This is a single call that can send information about multiple changes */
-               if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
-                       msg->flags |= PRINTER_MESSAGE_ATTRIBUTES;
+       /* The length of the message includes the trailing \0 */
 
-               result = cli_spoolss_reply_rrpcn(send_cli, send_cli->mem_ctx, &Printer->notify.client_hnd, 
-                               msg, info);
-       }
-       else {
-               /* This requires that the server send an individual event notification for each change */
-               result = srv_spoolss_routerreplyprinter(send_cli, send_cli->mem_ctx, &Printer->notify.client_hnd, 
-                               msg, info);
+       init_unistr2(&unistr, msg->notify.data, msg->len);
+
+       data->notify_data.data.length = msg->len * 2;
+       data->notify_data.data.string = (uint16 *)talloc(mem_ctx, msg->len * 2);
+
+       if (!data->notify_data.data.string) {
+               data->notify_data.data.length = 0;
+               return;
        }
        
-       return result;
+       memcpy(data->notify_data.data.string, unistr.buffer, msg->len * 2);
 }
 
+static void notify_system_time(struct spoolss_notify_msg *msg,
+                              SPOOL_NOTIFY_INFO_DATA *data,
+                              TALLOC_CTX *mem_ctx)
+{
+       SYSTEMTIME systime;
+       prs_struct ps;
+
+       if (msg->len != sizeof(time_t)) {
+               DEBUG(5, ("notify_system_time: received wrong sized message (%d)\n",
+                         msg->len));
+               return;
+       }
+
+       if (!prs_init(&ps, MAX_PDU_FRAG_LEN, mem_ctx, MARSHALL)) {
+               DEBUG(5, ("notify_system_time: prs_init() failed\n"));
+               return;
+       }
+
+       if (!make_systemtime(&systime, localtime((time_t *)msg->notify.data))) {
+               DEBUG(5, ("notify_system_time: unable to make systemtime\n"));
+               return;
+       }
+
+       if (!spoolss_io_system_time("", &ps, 0, &systime))
+               return;
+
+       data->notify_data.data.length = prs_offset(&ps);
+       data->notify_data.data.string =
+               talloc(mem_ctx, prs_offset(&ps));
+
+       memcpy(data->notify_data.data.string, prs_data_p(&ps), prs_offset(&ps));
+
+       prs_mem_free(&ps);
+}
+
+struct notify2_message_table {
+       char *name;
+       void (*fn)(struct spoolss_notify_msg *msg,
+                  SPOOL_NOTIFY_INFO_DATA *data, TALLOC_CTX *mem_ctx);
+};
+
+static struct notify2_message_table printer_notify_table[] = {
+       /* 0x00 */ { "PRINTER_NOTIFY_SERVER_NAME", NULL },
+       /* 0x01 */ { "PRINTER_NOTIFY_PRINTER_NAME", NULL },
+       /* 0x02 */ { "PRINTER_NOTIFY_SHARE_NAME", NULL },
+       /* 0x03 */ { "PRINTER_NOTIFY_PORT_NAME", NULL },
+       /* 0x04 */ { "PRINTER_NOTIFY_DRIVER_NAME", NULL },
+       /* 0x05 */ { "PRINTER_NOTIFY_COMMENT", NULL },
+       /* 0x06 */ { "PRINTER_NOTIFY_LOCATION", NULL },
+       /* 0x07 */ { "PRINTER_NOTIFY_DEVMODE", NULL },
+       /* 0x08 */ { "PRINTER_NOTIFY_SEPFILE", NULL },
+       /* 0x09 */ { "PRINTER_NOTIFY_PRINT_PROCESSOR", NULL },
+       /* 0x0a */ { "PRINTER_NOTIFY_PARAMETERS", NULL },
+       /* 0x0b */ { "PRINTER_NOTIFY_DATATYPE", NULL },
+       /* 0x0c */ { "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+       /* 0x0d */ { "PRINTER_NOTIFY_ATTRIBUTES", NULL },
+       /* 0x0e */ { "PRINTER_NOTIFY_PRIORITY", NULL },
+       /* 0x0f */ { "PRINTER_NOTIFY_DEFAULT_PRIORITY", NULL },
+       /* 0x10 */ { "PRINTER_NOTIFY_START_TIME", NULL },
+       /* 0x11 */ { "PRINTER_NOTIFY_UNTIL_TIME", NULL },
+       /* 0x12 */ { "PRINTER_NOTIFY_STATUS", notify_one_value },
+};
+
+static struct notify2_message_table job_notify_table[] = {
+       /* 0x00 */ { "JOB_NOTIFY_PRINTER_NAME", NULL },
+       /* 0x01 */ { "JOB_NOTIFY_MACHINE_NAME", NULL },
+       /* 0x02 */ { "JOB_NOTIFY_PORT_NAME", NULL },
+       /* 0x03 */ { "JOB_NOTIFY_USER_NAME", notify_string },
+       /* 0x04 */ { "JOB_NOTIFY_NOTIFY_NAME", NULL },
+       /* 0x05 */ { "JOB_NOTIFY_DATATYPE", NULL },
+       /* 0x06 */ { "JOB_NOTIFY_PRINT_PROCESSOR", NULL },
+       /* 0x07 */ { "JOB_NOTIFY_PARAMETERS", NULL },
+       /* 0x08 */ { "JOB_NOTIFY_DRIVER_NAME", NULL },
+       /* 0x09 */ { "JOB_NOTIFY_DEVMODE", NULL },
+       /* 0x0a */ { "JOB_NOTIFY_STATUS", notify_one_value },
+       /* 0x0b */ { "JOB_NOTIFY_STATUS_STRING", NULL },
+       /* 0x0c */ { "JOB_NOTIFY_SECURITY_DESCRIPTOR", NULL },
+       /* 0x0d */ { "JOB_NOTIFY_DOCUMENT", notify_string },
+       /* 0x0e */ { "JOB_NOTIFY_PRIORITY", NULL },
+       /* 0x0f */ { "JOB_NOTIFY_POSITION", NULL },
+       /* 0x10 */ { "JOB_NOTIFY_SUBMITTED", notify_system_time },
+       /* 0x11 */ { "JOB_NOTIFY_START_TIME", NULL },
+       /* 0x12 */ { "JOB_NOTIFY_UNTIL_TIME", NULL },
+       /* 0x13 */ { "JOB_NOTIFY_TIME", NULL },
+       /* 0x14 */ { "JOB_NOTIFY_TOTAL_PAGES", notify_one_value },
+       /* 0x15 */ { "JOB_NOTIFY_PAGES_PRINTED", NULL },
+       /* 0x16 */ { "JOB_NOTIFY_TOTAL_BYTES", notify_one_value },
+       /* 0x17 */ { "JOB_NOTIFY_BYTES_PRINTED", NULL },
+};
 
 /***********************************************************************
  Send a change notication message on all handles which have a call 
  back registered
  **********************************************************************/
 
-static void send_spoolss_event_notification(PRINTER_MESSAGE_INFO *msg)
+static void process_notify2_message(struct spoolss_notify_msg *msg, 
+                                   TALLOC_CTX *mem_ctx)
 {
-       Printer_entry *find_printer;
-       WERROR result;
-       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       Printer_entry *p;
 
-       if (!msg) {
-               DEBUG(0,("send_spoolss_event_notification: NULL msg pointer!\n"));
-               return;
-       }
+       for (p = printers_list; p; p = p->next) {
+               SPOOL_NOTIFY_INFO_DATA *data;
+               uint32 data_len = 1;
+               uint32 id;
 
-       for(find_printer = printers_list; find_printer; find_printer = find_printer->next) {
+               /* Is there notification on this handle? */
 
-               /*
-                * If the entry has a connected client we send the message. There should 
-                * only be one of these normally when dealing with the NT/2k spooler.
-                * However, iterate over all to make sure we deal with user applications
-                * in addition to spooler service.
-                *
-                * While we are only maintaining a single connection to the client, 
-                * the FindFirstPrinterChangeNotification() call is made on a printer 
-                * handle, so "client_connected" represents the whether or not the 
-                * client asked for change notication on this handle.
-                * 
-                * --jerry
-                */
+               if (!p->notify.client_connected)
+                       continue;
 
-               if (find_printer->notify.client_connected==True) {
-               
-                       /* does the client care about what changed? */
+               /* For this printer?  Print servers always receive 
+                   notifications. */
 
-                       if (msg->flags && !is_client_monitoring_event(find_printer, msg->flags)) {
-                               DEBUG(10,("send_spoolss_event_notification: Client [%s] not monitoring these events\n",
-                                       find_printer->client.machine)); 
-                               continue;
-                       }
+               if (p->printer_type == PRINTER_HANDLE_IS_PRINTER &&
+                   !strequal(msg->printer, p->dev.handlename))
+                       continue;
 
-                       if (find_printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
-                               DEBUG(10,("send_spoolss_event_notification: printserver [%s]\n", find_printer->dev.printerservername ));
-                       else
-                               DEBUG(10,("send_spoolss_event_notification: printer [%s]\n", find_printer->dev.handlename));
+               /* Are we monitoring this event? */
+
+               if (!is_monitoring_event(p, msg->type, msg->field))
+                       continue;
 
-                       /*
-                        * if handle is a printer, only send if the printer_name matches.
-                        * ...else if handle is a printerserver, send to all
-                        */
+               /* OK - send the event to the client */
 
-                       if (*msg->printer_name && (find_printer->printer_type==PRINTER_HANDLE_IS_PRINTER) 
-                               && !strequal(msg->printer_name, find_printer->dev.handlename)) 
-                       {
-                               DEBUG(10,("send_spoolss_event_notification: ignoring message sent to %s [%s]\n",
-                                       msg->printer_name, find_printer->dev.handlename ));
-                               continue;
+               data = talloc(mem_ctx, sizeof(SPOOL_NOTIFY_INFO_DATA));
+
+               ZERO_STRUCTP(data);
+
+               /* Convert unix jobid to smb jobid */
+
+               id = msg->id;
+
+               if (msg->flags & SPOOLSS_NOTIFY_MSG_UNIX_JOBID) {
+
+                       id = sysjob_to_jobid(msg->id);
+
+                       if (id == -1) {
+                               DEBUG(3, ("no such unix jobid %d\n", msg->id));
+                               goto done;
                        }
+               }
 
+               construct_info_data(data, msg->type, msg->field, id);
 
-                       /* lookup the printer if we have a name if we don't already have a 
-                          valid NT_PRINTER_INFO_LEVEL structure. And yes I'm assuming we 
-                          will always have a non-empty msg.printer_name */
-                                  
-                       if (!printer || !printer->info_2 || strcmp(msg->printer_name, printer->info_2->printername)) 
-                       {
-                       
-                               if (printer) {
-                                       free_a_printer(&printer, 2);
-                                       printer = NULL;
-                               }
-                                       
-                               result = get_a_printer(&printer, 2, msg->printer_name);
-                               if (!W_ERROR_IS_OK(result))
-                                       continue;
+               switch(msg->type) {
+               case PRINTER_NOTIFY_TYPE:
+                       if (printer_notify_table[msg->field].fn)
+                               printer_notify_table[msg->field].fn(
+                                       msg, data, mem_ctx);
+                       else
+                               goto done;
+                       break;
+               case JOB_NOTIFY_TYPE:
+                       if (job_notify_table[msg->field].fn)
+                               job_notify_table[msg->field].fn(
+                                       msg, data, mem_ctx);
+                       else
+                               goto done;
+                       break;
+               default:
+                       DEBUG(5, ("Unknown notification type %d\n", 
+                                 msg->type));
+                       goto done;
+               }
+
+               if (!p->notify.flags)
+                       cli_spoolss_rrpcn(
+                               &cli, mem_ctx, &p->notify.client_hnd, 
+                               data_len, data, p->notify.change, 0);
+               else {
+                       NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+                       get_a_printer(&printer, 2, msg->printer);
+
+                       if (!printer) {
+                               DEBUG(5, ("unable to load info2 for %s\n",
+                                         msg->printer));
+                               goto done;
                        }
 
-                       /* issue the client call */
+                       /* XXX: This needs to be updated for 
+                           PRINTER_CHANGE_SET_PRINTER_DRIVER. */ 
 
-                       result = srv_spoolss_send_event_to_client(find_printer, &cli, msg, printer);
-                       
-                       if (!W_ERROR_IS_OK(result)) {
-                               DEBUG(5,("send_spoolss_event_notification: Event notification failed [%s]\n",
-                                       dos_errstr(result)));
+                       cli_spoolss_routerreplyprinter(
+                               &cli, mem_ctx, &p->notify.client_hnd,
+                               0, printer->info_2->changeid);
+
+                       free_a_printer(&printer, 2);
                }
        }
-}
-
+done:
        return;
 }
-/***************************************************************************
- Receive the notify message and decode the message.  Do not send 
- notification if we sent this originally as that would result in 
- duplicates.
-****************************************************************************/
 
-static void srv_spoolss_receive_message(int msg_type, pid_t src, void *buf, size_t len)
+/* Receive a notify2 message */
+
+static void receive_notify2_message(int msg_type, pid_t src, void *buf, 
+                                   size_t len)
 {
-       PRINTER_MESSAGE_INFO msg;
-       
-       if (len < sizeof(msg)) {
-               DEBUG(2,("srv_spoolss_receive_message: got incorrect message size (%u)!\n", (unsigned int)len));
-               return;
-       }
+       struct spoolss_notify_msg msg;
+       int offset = 0;
+       TALLOC_CTX *mem_ctx = talloc_init();
 
-       memcpy(&msg, buf, sizeof(PRINTER_MESSAGE_INFO));
-       
-       DEBUG(10,("srv_spoolss_receive_message: Got message printer change [queue = %s] low=0x%x  high=0x%x flags=0x%x\n",
-               msg.printer_name, (unsigned int)msg.low, (unsigned int)msg.high, msg.flags ));
+       /* Unpack message */
 
-       /* Iterate the printer list */
-       
-       send_spoolss_event_notification(&msg);
+       ZERO_STRUCT(msg);
+
+       offset += tdb_unpack((char *)buf + offset, len - offset, "f",
+                            msg.printer);
        
+       offset += tdb_unpack((char *)buf + offset, len - offset, "ddddd",
+                            &msg.type, &msg.field, &msg.id, &msg.len, &msg.flags);
+
+       if (msg.len == 0)
+               tdb_unpack((char *)buf + offset, len - offset, "dd",
+                          &msg.notify.value[0], &msg.notify.value[1]);
+       else
+               tdb_unpack((char *)buf + offset, len - offset, "B", 
+                          &msg.len, &msg.notify.data);
+
+       DEBUG(3, ("got NOTIFY2 message, type %d, field 0x%02x, flags 0x%04x\n",
+                 msg.type, msg.field, msg.flags));
+
+       if (msg.len == 0)
+               DEBUG(3, ("value1 = %d, value2 = %d\n", msg.notify.value[0],
+                         msg.notify.value[1]));
+       else
+               dump_data(3, msg.notify.data, msg.len);
+
+       /* Process message */
+
+       process_notify2_message(&msg, mem_ctx);
+
+       /* Free message */
+
+       if (msg.len > 0)
+               free(msg.notify.data);
+
+       talloc_destroy(mem_ctx);
 }
 
 /***************************************************************************
- Send a notify event.
-****************************************************************************/
-
-static BOOL srv_spoolss_sendnotify(char* printer_name, uint32 high, uint32 low, uint32 flags)
+ Server wrapper for cli_spoolss_routerreplyprinter() since the client 
+ function can only send a single change notification at a time.
+ FIXME!!!  only handles one change currently (PRINTER_CHANGE_SET_PRINTER_DRIVER)
+ --jerry
+ **************************************************************************/
+static WERROR srv_spoolss_routerreplyprinter (struct cli_state *reply_cli, TALLOC_CTX *mem_ctx,
+                                       POLICY_HND *pol, PRINTER_MESSAGE_INFO *info,
+                                       NT_PRINTER_INFO_LEVEL *printer)                         
 {
-       char msg[sizeof(PRINTER_MESSAGE_INFO)];
-       PRINTER_MESSAGE_INFO info;
+       WERROR result;
+       uint32 condition = 0x0;
        
-       ZERO_STRUCT(info);
+       if (info->flags & PRINTER_MESSAGE_DRIVER)
+               condition = PRINTER_CHANGE_SET_PRINTER_DRIVER;
+       
+       result = cli_spoolss_routerreplyprinter(reply_cli, mem_ctx, pol, condition, 
+                       printer->info_2->changeid);
 
-       info.low        = low;
-       info.high       = high;
-       info.flags      = flags;
-       fstrcpy(info.printer_name, printer_name);
+       return result;
+}
+
+/********************************************************************
+ Send a message to ourself about new driver being installed
+ so we can upgrade the information for each printer bound to this
+ driver
+ ********************************************************************/
+static BOOL srv_spoolss_drv_upgrade_printer(char* drivername)
+{
+       int len = strlen(drivername);
        
-       memcpy(msg, &info, sizeof(PRINTER_MESSAGE_INFO));       
+       if (!len)
+               return False;
 
-       DEBUG(10,("srv_spoolss_sendnotify: printer change low=0x%x  high=0x%x [%s], flags=0x%x\n", 
-               low, high, printer_name, flags));
+       DEBUG(10,("srv_spoolss_drv_upgrade_printer: Sending message about driver upgrade [%s]\n",
+               drivername));
                
-       message_send_all(conn_tdb_ctx(), MSG_PRINTER_NOTIFY, msg, sizeof(PRINTER_MESSAGE_INFO), 
-               False, NULL);
+       message_send_pid(sys_getpid(), MSG_PRINTER_DRVUPGRADE, drivername, len+1, False);
 
        return True;
-}      
+}
+
+/**********************************************************************
+ callback to receive a MSG_PRINTER_DRVUPGRADE message and interate
+ over all printers, upgrading ones as neessary 
+ **********************************************************************/
+void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len)
+{
+       fstring drivername;
+       int snum;
+       int n_services = lp_numservices();
+       
+       len = MIN(len,sizeof(drivername)-1);
+       strncpy(drivername, buf, len);
+       
+       DEBUG(10,("do_drv_upgrade_printer: Got message for new driver [%s]\n", drivername ));
+
+       /* Iterate the printer list */
+       
+       for (snum=0; snum<n_services; snum++)
+       {
+               if (lp_snum_ok(snum) && lp_print_ok(snum) ) 
+               {
+                       WERROR result;
+                       NT_PRINTER_INFO_LEVEL *printer = NULL;
+                       
+                       result = get_a_printer(&printer, 2, lp_servicename(snum));
+                       if (!W_ERROR_IS_OK(result))
+                               continue;
+                               
+                       if (printer && printer->info_2 && !strcmp(drivername, printer->info_2->drivername)) 
+                       {
+                               DEBUG(6,("Updating printer [%s]\n", printer->info_2->printername));
+                               
+                               /* all we care about currently is the change_id */
+                               
+                               result = mod_a_printer(*printer, 2);
+                               if (!W_ERROR_IS_OK(result)) {
+                                       DEBUG(3,("do_drv_upgrade_printer: mod_a_printer() failed with status [%s]\n", 
+                                               dos_errstr(result)));
+                               }
+                       }
+                       
+                       free_a_printer(&printer, 2);                    
+               }
+       }
+       
+       /* all done */  
+}
 
 /********************************************************************
  Copy routines used by convert_to_openprinterex()
@@ -924,16 +1126,6 @@ Can't find printer handle we created for printer %s\n", name ));
                return WERR_INVALID_PRINTER_NAME;
        }
 
-/*
-       if (printer_default->datatype_ptr != NULL)
-       {
-               unistr2_to_ascii(datatype, printer_default->datatype, sizeof(datatype)-1);
-               set_printer_hnd_datatype(handle, datatype);
-       }
-       else
-               set_printer_hnd_datatype(handle, "");
-*/
-       
        /*
           First case: the user is opening the print server:
 
@@ -997,7 +1189,7 @@ Can't find printer handle we created for printer %s\n", name ));
                            user_in_list(uidtoname(user.uid),
                                         lp_printer_admin(snum)))
                                return WERR_OK;
-
+                       
                        close_printer_handle(p, handle);
                        return WERR_ACCESS_DENIED;
                }
@@ -1031,7 +1223,9 @@ Can't find printer handle we created for printer %s\n", name ));
                        printer_default->access_required = PRINTER_ACCESS_USE;
                }
 
-               if (!print_access_check(&user, snum, printer_default->access_required)) {
+               /* check smb.conf parameters and the the sec_desc */
+               
+               if (!user_ok(uidtoname(user.uid), snum) || !print_access_check(&user, snum, printer_default->access_required)) {
                        DEBUG(3, ("access DENIED for printer open\n"));
                        close_printer_handle(p, handle);
                        return WERR_ACCESS_DENIED;
@@ -1308,10 +1502,6 @@ WERROR _spoolss_deleteprinter(pipes_struct *p, SPOOL_Q_DELETEPRINTER *q_u, SPOOL
 
        update_c_setprinter(False);
 
-       if (W_ERROR_IS_OK(result)) {
-               srv_spoolss_sendnotify(Printer->dev.handlename, 0, PRINTER_CHANGE_DELETE_PRINTER, 0x0);
-       }
-               
        return result;
 }
 
@@ -1344,26 +1534,17 @@ static int get_version_id (char * arch)
 
 /********************************************************************
  * _spoolss_deleteprinterdriver
- *
- * We currently delete the driver for the architecture only.
- * This can leave the driver for other archtectures.  However,
- * since every printer associates a "Windows NT x86" driver name
- * and we cannot delete that one while it is in use, **and** since
- * it is impossible to assign a driver to a Samba printer without
- * having the "Windows NT x86" driver installed,...
- * 
- * ....we should not get into trouble here.  
- *
- *                                                      --jerry
  ********************************************************************/
 
-WERROR _spoolss_deleteprinterdriver(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVER *q_u, 
-                                   SPOOL_R_DELETEPRINTERDRIVER *r_u)
+WERROR _spoolss_deleteprinterdriver(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVER *q_u, SPOOL_R_DELETEPRINTERDRIVER *r_u)
 {
        fstring                         driver;
        fstring                         arch;
        NT_PRINTER_DRIVER_INFO_LEVEL    info;
        int                             version;
+       struct current_user             user;
+       
+       get_current_user(&user, p);
         
        unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 );
        unistr2_to_ascii(arch,   &q_u->arch,   sizeof(arch)-1   );
@@ -1373,21 +1554,89 @@ WERROR _spoolss_deleteprinterdriver(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVER
                /* this is what NT returns */
                return WERR_INVALID_ENVIRONMENT;
        }
+       
+       /* if they said "Windows NT x86", then try for version 2 & 3 */
+       
+       if ( version == 2 )
+               version = DRIVER_ANY_VERSION;
                
        ZERO_STRUCT(info);
-       if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) {
+       
+       if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version)))
                return WERR_UNKNOWN_PRINTER_DRIVER;
-       }
        
+       if (printer_driver_in_use(info.info_3))
+               return WERR_PRINTER_DRIVER_IN_USE;
 
-       if (printer_driver_in_use(arch, driver))
-       {
+       return delete_printer_driver(info.info_3, &user, DRIVER_ANY_VERSION, False);
+}
+
+/********************************************************************
+ * spoolss_deleteprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdriverex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDRIVEREX *q_u, SPOOL_R_DELETEPRINTERDRIVEREX *r_u)
+{
+       fstring                         driver;
+       fstring                         arch;
+       NT_PRINTER_DRIVER_INFO_LEVEL    info;
+       int                             version;
+       uint32                          flags = q_u->delete_flags;
+       BOOL                            delete_files;
+       struct current_user             user;
+       
+       get_current_user(&user, p);
+       
+       unistr2_to_ascii(driver, &q_u->driver, sizeof(driver)-1 );
+       unistr2_to_ascii(arch,   &q_u->arch,   sizeof(arch)-1   );
+
+       /* check that we have a valid driver name first */
+       if ((version=get_version_id(arch)) == -1) {
+               /* this is what NT returns */
+               return WERR_INVALID_ENVIRONMENT;
+       }
+       
+       if ( flags & DPD_DELETE_SPECIFIC_VERSION )
+               version = q_u->version;
+       else if ( version == 2 )
+               /* if they said "Windows NT x86", then try for version 2 & 3 */
+               version = DRIVER_ANY_VERSION;
+               
+       ZERO_STRUCT(info);
+       
+       if (!W_ERROR_IS_OK(get_a_printer_driver(&info, 3, driver, arch, version))) 
+               return WERR_UNKNOWN_PRINTER_DRIVER;
+               
+       if ( printer_driver_in_use(info.info_3) )
                return WERR_PRINTER_DRIVER_IN_USE;
+       
+       /* 
+        * we have a couple of cases to consider. 
+        * (1) Are any files in use?  If so and DPD_DELTE_ALL_FILE is set,
+        *     then the delete should fail if **any** files overlap with 
+        *     other drivers 
+        * (2) If DPD_DELTE_UNUSED_FILES is sert, then delete all
+        *     non-overlapping files 
+        * (3) If neither DPD_DELTE_ALL_FILE nor DPD_DELTE_ALL_FILES
+        *     is set, the do not delete any files
+        * Refer to MSDN docs on DeletePrinterDriverEx() for details.
+        */
+       
+       delete_files = flags & (DPD_DELETE_ALL_FILES|DPD_DELETE_UNUSED_FILES);
+       
+       if ( delete_files ) 
+       {
+               /* fail if any files are in use and DPD_DELETE_ALL_FILES is set */
+               
+               if ( printer_driver_files_in_use(info.info_3) & (flags&DPD_DELETE_ALL_FILES) )
+                       /* no idea of the correct error here */
+                       return WERR_ACCESS_DENIED;      
        }
 
-       return delete_printer_driver(info.info_3);       
+       return delete_printer_driver(info.info_3, &user, version, delete_files);
 }
 
+
 /********************************************************************
  GetPrinterData on a printer server Handle.
 ********************************************************************/
@@ -1438,7 +1687,11 @@ static BOOL getprinterdata_printer_server(TALLOC_CTX *ctx, fstring value, uint32
                *type = 0x4;
                if((*data = (uint8 *)talloc(ctx, 4*sizeof(uint8) )) == NULL)
                        return False;
+#ifndef EMULATE_WIN2K_HACK /* JERRY */
                SIVAL(*data, 0, 2);
+#else
+               SIVAL(*data, 0, 3);
+#endif
                *needed = 0x4;
                return True;
        }
@@ -1574,36 +1827,126 @@ WERROR _spoolss_getprinterdata(pipes_struct *p, SPOOL_Q_GETPRINTERDATA *q_u, SPO
                DEBUG(2,("_spoolss_getprinterdata: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(handle)));
                return WERR_BADFID;
        }
-       
-       unistr2_to_ascii(value, valuename, sizeof(value)-1);
-       
-       if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
-               found=getprinterdata_printer_server(p->mem_ctx, value, type, data, needed, *out_size);
-       else
-               found= getprinterdata_printer(p, p->mem_ctx, handle, value, type, data, needed, *out_size);
+       
+       unistr2_to_ascii(value, valuename, sizeof(value)-1);
+       
+       if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+               found=getprinterdata_printer_server(p->mem_ctx, value, type, data, needed, *out_size);
+       else
+               found= getprinterdata_printer(p, p->mem_ctx, handle, value, type, data, needed, *out_size);
+
+       if (found==False) {
+               DEBUG(5, ("value not found, allocating %d\n", *out_size));
+               /* reply this param doesn't exist */
+               if (*out_size) {
+                       if((*data=(uint8 *)talloc_zero(p->mem_ctx, *out_size*sizeof(uint8))) == NULL)
+                               return WERR_NOMEM;
+               } else {
+                       *data = NULL;
+               }
+
+               /* error depends on handle type */
+
+               if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
+                       return WERR_INVALID_PARAM;
+               else 
+                       return WERR_BADFILE;
+       }
+       
+       if (*needed > *out_size)
+               return WERR_MORE_DATA;
+       else 
+               return WERR_OK;
+}
+
+/*********************************************************
+ Connect to the client machine.
+**********************************************************/
+
+static BOOL spoolss_connect_to_client(struct cli_state *the_cli, char *remote_machine)
+{
+       extern pstring global_myname;
+
+       ZERO_STRUCTP(the_cli);
+       if(cli_initialise(the_cli) == NULL) {
+               DEBUG(0,("connect_to_client: unable to initialize client connection.\n"));
+               return False;
+       }
+
+       if(!resolve_name( remote_machine, &the_cli->dest_ip, 0x20)) {
+               DEBUG(0,("connect_to_client: Can't resolve address for %s\n", remote_machine));
+               cli_shutdown(the_cli);
+       return False;
+       }
+
+       if (ismyip(the_cli->dest_ip)) {
+               DEBUG(0,("connect_to_client: Machine %s is one of our addresses. Cannot add to ourselves.\n", remote_machine));
+               cli_shutdown(the_cli);
+               return False;
+       }
+
+       if (!cli_connect(the_cli, remote_machine, &the_cli->dest_ip)) {
+               DEBUG(0,("connect_to_client: unable to connect to SMB server on machine %s. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+               cli_shutdown(the_cli);
+               return False;
+       }
+  
+       if (!attempt_netbios_session_request(the_cli, global_myname, remote_machine, &the_cli->dest_ip)) {
+               DEBUG(0,("connect_to_client: machine %s rejected the NetBIOS session request.\n", 
+                       remote_machine));
+               return False;
+       }
+
+       the_cli->protocol = PROTOCOL_NT1;
+    
+       if (!cli_negprot(the_cli)) {
+               DEBUG(0,("connect_to_client: machine %s rejected the negotiate protocol. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+               cli_shutdown(the_cli);
+               return False;
+       }
+
+       if (the_cli->protocol != PROTOCOL_NT1) {
+               DEBUG(0,("connect_to_client: machine %s didn't negotiate NT protocol.\n", remote_machine));
+               cli_shutdown(the_cli);
+               return False;
+       }
+    
+       /*
+        * Do an anonymous session setup.
+        */
+    
+       if (!cli_session_setup(the_cli, "", "", 0, "", 0, "")) {
+               DEBUG(0,("connect_to_client: machine %s rejected the session setup. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+               cli_shutdown(the_cli);
+               return False;
+       }
+    
+       if (!(the_cli->sec_mode & 1)) {
+               DEBUG(0,("connect_to_client: machine %s isn't in user level security mode\n", remote_machine));
+               cli_shutdown(the_cli);
+               return False;
+       }
+    
+       if (!cli_send_tconX(the_cli, "IPC$", "IPC", "", 1)) {
+               DEBUG(0,("connect_to_client: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n", remote_machine, cli_errstr(the_cli) ));
+               cli_shutdown(the_cli);
+               return False;
+       }
 
-       if (found==False) {
-               DEBUG(5, ("value not found, allocating %d\n", *out_size));
-               /* reply this param doesn't exist */
-               if (*out_size) {
-                       if((*data=(uint8 *)talloc_zero(p->mem_ctx, *out_size*sizeof(uint8))) == NULL)
-                               return WERR_NOMEM;
-               } else {
-                       *data = NULL;
-               }
+       /*
+        * Ok - we have an anonymous connection to the IPC$ share.
+        * Now start the NT Domain stuff :-).
+        */
 
-               /* error depends on handle type */
+       if(cli_nt_session_open(the_cli, PIPE_SPOOLSS) == False) {
+               DEBUG(0,("connect_to_client: unable to open the domain client session to machine %s. Error was : %s.\n", remote_machine, cli_errstr(the_cli)));
+               cli_nt_session_close(the_cli);
+               cli_ulogoff(the_cli);
+               cli_shutdown(the_cli);
+               return False;
+       } 
 
-               if (Printer->printer_type == PRINTER_HANDLE_IS_PRINTSERVER)
-                       return WERR_INVALID_PARAM;
-               else 
-                       return WERR_BADFILE;
-       }
-       
-       if (*needed > *out_size)
-               return WERR_MORE_DATA;
-       else 
-               return WERR_OK;
+       return True;
 }
 
 /***************************************************************************
@@ -1626,15 +1969,14 @@ static BOOL srv_spoolss_replyopenprinter(char *printer, uint32 localprinter, uin
                if(!spoolss_connect_to_client(&cli, unix_printer))
                        return False;
                        
-               message_register(MSG_PRINTER_NOTIFY, srv_spoolss_receive_message);
-
+               message_register(MSG_PRINTER_NOTIFY2, receive_notify2_message);
        }
 
        smb_connections++;
 
        result = cli_spoolss_reply_open_printer(&cli, cli.mem_ctx, printer, localprinter, 
                        type, handle);
-
+                       
        if (!W_ERROR_IS_OK(result))
                DEBUG(5,("srv_spoolss_reply_open_printer: Client RPC returned [%s]\n",
                        dos_errstr(result)));
@@ -1646,9 +1988,8 @@ static BOOL srv_spoolss_replyopenprinter(char *printer, uint32 localprinter, uin
  * _spoolss_rffpcnex
  * ReplyFindFirstPrinterChangeNotifyEx
  *
- * jfmxxxx: before replying OK: status=0
- * should do a rpc call to the workstation asking ReplyOpenPrinter
- * have to code it, later.
+ * before replying OK: status=0 a rpc call is made to the workstation
+ * asking ReplyOpenPrinter 
  *
  * in fact ReplyOpenPrinter is the changenotify equivalent on the spoolss pipe
  * called from api_spoolss_rffpcnex
@@ -1681,15 +2022,17 @@ WERROR _spoolss_rffpcnex(pipes_struct *p, SPOOL_Q_RFFPCNEX *q_u, SPOOL_R_RFFPCNE
 
        Printer->notify.option=dup_spool_notify_option(option);
 
-       unistr2_to_ascii(Printer->notify.localmachine, localmachine, sizeof(Printer->notify.localmachine)-1);
+       unistr2_to_ascii(Printer->notify.localmachine, localmachine, 
+                      sizeof(Printer->notify.localmachine)-1);
+
+       /* Connect to the client machine and send a ReplyOpenPrinter */
 
-       /* connect to the client machine and send a ReplyOpenPrinter */
-       if(srv_spoolss_replyopenprinter(Printer->notify.localmachine,
+       if(!srv_spoolss_replyopenprinter(Printer->notify.localmachine,
                                        Printer->notify.printerlocal, 1,
                                        &Printer->notify.client_hnd))
-       {
-               Printer->notify.client_connected=True;
-       }
+               return WERR_SERVER_UNAVAILABLE;
+
+       Printer->notify.client_connected=True;
 
        return WERR_OK;
 }
@@ -1711,7 +2054,7 @@ void spoolss_notify_server_name(int snum,
 
        len = rpcstr_push(temp, temp_name, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
 
        if (!data->notify_data.data.string) {
@@ -1746,7 +2089,7 @@ void spoolss_notify_printer_name(int snum,
 
        len = rpcstr_push(temp, p, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -1772,7 +2115,7 @@ void spoolss_notify_share_name(int snum,
 
        len = rpcstr_push(temp, lp_servicename(snum), sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -1800,7 +2143,7 @@ void spoolss_notify_port_name(int snum,
 
        len = rpcstr_push(temp, printer->info_2->portname, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -1826,7 +2169,8 @@ void spoolss_notify_driver_name(int snum,
        uint32 len;
 
        len = rpcstr_push(temp, printer->info_2->drivername, sizeof(temp)-2, STR_TERMINATE);
-       data->notify_data.data.length = len / 2 - 1;
+
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -1855,7 +2199,7 @@ void spoolss_notify_comment(int snum,
        else
                len = rpcstr_push(temp, printer->info_2->comment, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -1882,7 +2226,7 @@ void spoolss_notify_location(int snum,
 
        len = rpcstr_push(temp, printer->info_2->location,sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -1921,7 +2265,7 @@ void spoolss_notify_sepfile(int snum,
 
        len = rpcstr_push(temp, printer->info_2->sepfile, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -1948,7 +2292,7 @@ void spoolss_notify_print_processor(int snum,
 
        len = rpcstr_push(temp,  printer->info_2->printprocessor, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -1975,7 +2319,7 @@ void spoolss_notify_parameters(int snum,
 
        len = rpcstr_push(temp,  printer->info_2->parameters, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -2002,7 +2346,7 @@ void spoolss_notify_datatype(int snum,
 
        len = rpcstr_push(temp, printer->info_2->datatype, sizeof(pstring)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -2162,7 +2506,7 @@ static void spoolss_notify_username(int snum,
 
        len = rpcstr_push(temp, queue->fs_user, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -2202,7 +2546,7 @@ static void spoolss_notify_job_name(int snum,
 
        len = rpcstr_push(temp, queue->fs_file, sizeof(temp)-2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -2252,7 +2596,7 @@ static void spoolss_notify_job_status_string(int snum,
 
        len = rpcstr_push(temp, p, sizeof(temp) - 2, STR_TERMINATE);
 
-       data->notify_data.data.length = len / 2 - 1;
+       data->notify_data.data.length = len;
        data->notify_data.data.string = (uint16 *)talloc(mem_ctx, len);
        
        if (!data->notify_data.data.string) {
@@ -2376,8 +2720,6 @@ static void spoolss_notify_submitted_time(int snum,
        SSVAL(p, 14, st.milliseconds);
 }
 
-#define END 65535
-
 struct s_notify_info_data_table
 {
        uint16 type;
@@ -2389,59 +2731,61 @@ struct s_notify_info_data_table
                    NT_PRINTER_INFO_LEVEL *printer, TALLOC_CTX *mem_ctx);
 };
 
+/* A table describing the various print notification constants and
+   whether the notification data is a pointer to a variable sized
+   buffer, a one value uint32 or a two value uint32. */
+
 struct s_notify_info_data_table notify_info_data_table[] =
 {
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SERVER_NAME,         "PRINTER_NOTIFY_SERVER_NAME",         POINTER,   spoolss_notify_server_name },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME,        "PRINTER_NOTIFY_PRINTER_NAME",        POINTER,   spoolss_notify_printer_name },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME,          "PRINTER_NOTIFY_SHARE_NAME",          POINTER,   spoolss_notify_share_name },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME,           "PRINTER_NOTIFY_PORT_NAME",           POINTER,   spoolss_notify_port_name },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME,         "PRINTER_NOTIFY_DRIVER_NAME",         POINTER,   spoolss_notify_driver_name },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT,             "PRINTER_NOTIFY_COMMENT",             POINTER,   spoolss_notify_comment },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION,            "PRINTER_NOTIFY_LOCATION",            POINTER,   spoolss_notify_location },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEVMODE,             "PRINTER_NOTIFY_DEVMODE",             POINTER,   spoolss_notify_devmode },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SEPFILE,             "PRINTER_NOTIFY_SEPFILE",             POINTER,   spoolss_notify_sepfile },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINT_PROCESSOR,     "PRINTER_NOTIFY_PRINT_PROCESSOR",     POINTER,   spoolss_notify_print_processor },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PARAMETERS,          "PRINTER_NOTIFY_PARAMETERS",          POINTER,   spoolss_notify_parameters },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DATATYPE,            "PRINTER_NOTIFY_DATATYPE",            POINTER,   spoolss_notify_datatype },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SECURITY_DESCRIPTOR, "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", POINTER,   spoolss_notify_security_desc },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_ATTRIBUTES,          "PRINTER_NOTIFY_ATTRIBUTES",          ONE_VALUE, spoolss_notify_attributes },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRIORITY,            "PRINTER_NOTIFY_PRIORITY",            ONE_VALUE, spoolss_notify_priority },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEFAULT_PRIORITY,    "PRINTER_NOTIFY_DEFAULT_PRIORITY",    ONE_VALUE, spoolss_notify_default_priority },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_START_TIME,          "PRINTER_NOTIFY_START_TIME",          ONE_VALUE, spoolss_notify_start_time },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_UNTIL_TIME,          "PRINTER_NOTIFY_UNTIL_TIME",          ONE_VALUE, spoolss_notify_until_time },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS,              "PRINTER_NOTIFY_STATUS",              ONE_VALUE, spoolss_notify_status },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS_STRING,       "PRINTER_NOTIFY_STATUS_STRING",       POINTER,   NULL },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_CJOBS,               "PRINTER_NOTIFY_CJOBS",               ONE_VALUE, spoolss_notify_cjobs },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_AVERAGE_PPM,         "PRINTER_NOTIFY_AVERAGE_PPM",         ONE_VALUE, spoolss_notify_average_ppm },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_PAGES,         "PRINTER_NOTIFY_TOTAL_PAGES",         POINTER,   NULL },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PAGES_PRINTED,       "PRINTER_NOTIFY_PAGES_PRINTED",       POINTER,   NULL },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_BYTES,         "PRINTER_NOTIFY_TOTAL_BYTES",         POINTER,   NULL },
-{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_BYTES_PRINTED,       "PRINTER_NOTIFY_BYTES_PRINTED",       POINTER,   NULL },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRINTER_NAME,            "JOB_NOTIFY_PRINTER_NAME",            POINTER,   spoolss_notify_printer_name },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_MACHINE_NAME,            "JOB_NOTIFY_MACHINE_NAME",            POINTER,   spoolss_notify_server_name },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PORT_NAME,               "JOB_NOTIFY_PORT_NAME",               POINTER,   spoolss_notify_port_name },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_USER_NAME,               "JOB_NOTIFY_USER_NAME",               POINTER,   spoolss_notify_username },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_NOTIFY_NAME,             "JOB_NOTIFY_NOTIFY_NAME",             POINTER,   spoolss_notify_username },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DATATYPE,                "JOB_NOTIFY_DATATYPE",                POINTER,   spoolss_notify_datatype },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRINT_PROCESSOR,         "JOB_NOTIFY_PRINT_PROCESSOR",         POINTER,   spoolss_notify_print_processor },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PARAMETERS,              "JOB_NOTIFY_PARAMETERS",              POINTER,   spoolss_notify_parameters },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DRIVER_NAME,             "JOB_NOTIFY_DRIVER_NAME",             POINTER,   spoolss_notify_driver_name },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DEVMODE,                 "JOB_NOTIFY_DEVMODE",                 POINTER,   spoolss_notify_devmode },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_STATUS,                  "JOB_NOTIFY_STATUS",                  ONE_VALUE, spoolss_notify_job_status },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_STATUS_STRING,           "JOB_NOTIFY_STATUS_STRING",           POINTER,   spoolss_notify_job_status_string },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_SECURITY_DESCRIPTOR,     "JOB_NOTIFY_SECURITY_DESCRIPTOR",     POINTER,   NULL },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DOCUMENT,                "JOB_NOTIFY_DOCUMENT",                POINTER,   spoolss_notify_job_name },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRIORITY,                "JOB_NOTIFY_PRIORITY",                ONE_VALUE, spoolss_notify_priority },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_POSITION,                "JOB_NOTIFY_POSITION",                ONE_VALUE, spoolss_notify_job_position },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_SUBMITTED,               "JOB_NOTIFY_SUBMITTED",               POINTER,   spoolss_notify_submitted_time },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_START_TIME,              "JOB_NOTIFY_START_TIME",              ONE_VALUE, spoolss_notify_start_time },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_UNTIL_TIME,              "JOB_NOTIFY_UNTIL_TIME",              ONE_VALUE, spoolss_notify_until_time },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TIME,                    "JOB_NOTIFY_TIME",                    ONE_VALUE, spoolss_notify_job_time },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TOTAL_PAGES,             "JOB_NOTIFY_TOTAL_PAGES",             ONE_VALUE, spoolss_notify_total_pages },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PAGES_PRINTED,           "JOB_NOTIFY_PAGES_PRINTED",           ONE_VALUE, spoolss_notify_pages_printed },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TOTAL_BYTES,             "JOB_NOTIFY_TOTAL_BYTES",             ONE_VALUE, spoolss_notify_job_size },
-{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_BYTES_PRINTED,           "JOB_NOTIFY_BYTES_PRINTED",           ONE_VALUE, NULL },
-{ END,                 END,                                "",                                   END,       NULL }
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SERVER_NAME,         "PRINTER_NOTIFY_SERVER_NAME",         NOTIFY_STRING,   spoolss_notify_server_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINTER_NAME,        "PRINTER_NOTIFY_PRINTER_NAME",        NOTIFY_STRING,   spoolss_notify_printer_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SHARE_NAME,          "PRINTER_NOTIFY_SHARE_NAME",          NOTIFY_STRING,   spoolss_notify_share_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PORT_NAME,           "PRINTER_NOTIFY_PORT_NAME",           NOTIFY_STRING,   spoolss_notify_port_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DRIVER_NAME,         "PRINTER_NOTIFY_DRIVER_NAME",         NOTIFY_STRING,   spoolss_notify_driver_name },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_COMMENT,             "PRINTER_NOTIFY_COMMENT",             NOTIFY_STRING,   spoolss_notify_comment },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_LOCATION,            "PRINTER_NOTIFY_LOCATION",            NOTIFY_STRING,   spoolss_notify_location },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEVMODE,             "PRINTER_NOTIFY_DEVMODE",             NOTIFY_POINTER,   spoolss_notify_devmode },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SEPFILE,             "PRINTER_NOTIFY_SEPFILE",             NOTIFY_STRING,   spoolss_notify_sepfile },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRINT_PROCESSOR,     "PRINTER_NOTIFY_PRINT_PROCESSOR",     NOTIFY_STRING,   spoolss_notify_print_processor },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PARAMETERS,          "PRINTER_NOTIFY_PARAMETERS",          NOTIFY_STRING,   spoolss_notify_parameters },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DATATYPE,            "PRINTER_NOTIFY_DATATYPE",            NOTIFY_STRING,   spoolss_notify_datatype },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_SECURITY_DESCRIPTOR, "PRINTER_NOTIFY_SECURITY_DESCRIPTOR", NOTIFY_POINTER,   spoolss_notify_security_desc },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_ATTRIBUTES,          "PRINTER_NOTIFY_ATTRIBUTES",          NOTIFY_ONE_VALUE, spoolss_notify_attributes },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PRIORITY,            "PRINTER_NOTIFY_PRIORITY",            NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_DEFAULT_PRIORITY,    "PRINTER_NOTIFY_DEFAULT_PRIORITY",    NOTIFY_ONE_VALUE, spoolss_notify_default_priority },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_START_TIME,          "PRINTER_NOTIFY_START_TIME",          NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_UNTIL_TIME,          "PRINTER_NOTIFY_UNTIL_TIME",          NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS,              "PRINTER_NOTIFY_STATUS",              NOTIFY_ONE_VALUE, spoolss_notify_status },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_STATUS_STRING,       "PRINTER_NOTIFY_STATUS_STRING",       NOTIFY_POINTER,   NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_CJOBS,               "PRINTER_NOTIFY_CJOBS",               NOTIFY_ONE_VALUE, spoolss_notify_cjobs },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_AVERAGE_PPM,         "PRINTER_NOTIFY_AVERAGE_PPM",         NOTIFY_ONE_VALUE, spoolss_notify_average_ppm },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_PAGES,         "PRINTER_NOTIFY_TOTAL_PAGES",         NOTIFY_POINTER,   NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_PAGES_PRINTED,       "PRINTER_NOTIFY_PAGES_PRINTED",       NOTIFY_POINTER,   NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_TOTAL_BYTES,         "PRINTER_NOTIFY_TOTAL_BYTES",         NOTIFY_POINTER,   NULL },
+{ PRINTER_NOTIFY_TYPE, PRINTER_NOTIFY_BYTES_PRINTED,       "PRINTER_NOTIFY_BYTES_PRINTED",       NOTIFY_POINTER,   NULL },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRINTER_NAME,            "JOB_NOTIFY_PRINTER_NAME",            NOTIFY_STRING,   spoolss_notify_printer_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_MACHINE_NAME,            "JOB_NOTIFY_MACHINE_NAME",            NOTIFY_STRING,   spoolss_notify_server_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PORT_NAME,               "JOB_NOTIFY_PORT_NAME",               NOTIFY_STRING,   spoolss_notify_port_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_USER_NAME,               "JOB_NOTIFY_USER_NAME",               NOTIFY_STRING,   spoolss_notify_username },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_NOTIFY_NAME,             "JOB_NOTIFY_NOTIFY_NAME",             NOTIFY_STRING,   spoolss_notify_username },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DATATYPE,                "JOB_NOTIFY_DATATYPE",                NOTIFY_STRING,   spoolss_notify_datatype },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRINT_PROCESSOR,         "JOB_NOTIFY_PRINT_PROCESSOR",         NOTIFY_STRING,   spoolss_notify_print_processor },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PARAMETERS,              "JOB_NOTIFY_PARAMETERS",              NOTIFY_STRING,   spoolss_notify_parameters },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DRIVER_NAME,             "JOB_NOTIFY_DRIVER_NAME",             NOTIFY_STRING,   spoolss_notify_driver_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DEVMODE,                 "JOB_NOTIFY_DEVMODE",                 NOTIFY_POINTER,   spoolss_notify_devmode },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_STATUS,                  "JOB_NOTIFY_STATUS",                  NOTIFY_ONE_VALUE, spoolss_notify_job_status },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_STATUS_STRING,           "JOB_NOTIFY_STATUS_STRING",           NOTIFY_STRING,   spoolss_notify_job_status_string },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_SECURITY_DESCRIPTOR,     "JOB_NOTIFY_SECURITY_DESCRIPTOR",     NOTIFY_POINTER,   NULL },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_DOCUMENT,                "JOB_NOTIFY_DOCUMENT",                NOTIFY_STRING,   spoolss_notify_job_name },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PRIORITY,                "JOB_NOTIFY_PRIORITY",                NOTIFY_ONE_VALUE, spoolss_notify_priority },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_POSITION,                "JOB_NOTIFY_POSITION",                NOTIFY_ONE_VALUE, spoolss_notify_job_position },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_SUBMITTED,               "JOB_NOTIFY_SUBMITTED",               NOTIFY_POINTER,   spoolss_notify_submitted_time },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_START_TIME,              "JOB_NOTIFY_START_TIME",              NOTIFY_ONE_VALUE, spoolss_notify_start_time },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_UNTIL_TIME,              "JOB_NOTIFY_UNTIL_TIME",              NOTIFY_ONE_VALUE, spoolss_notify_until_time },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TIME,                    "JOB_NOTIFY_TIME",                    NOTIFY_ONE_VALUE, spoolss_notify_job_time },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TOTAL_PAGES,             "JOB_NOTIFY_TOTAL_PAGES",             NOTIFY_ONE_VALUE, spoolss_notify_total_pages },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_PAGES_PRINTED,           "JOB_NOTIFY_PAGES_PRINTED",           NOTIFY_ONE_VALUE, spoolss_notify_pages_printed },
+{ JOB_NOTIFY_TYPE,     JOB_NOTIFY_TOTAL_BYTES,             "JOB_NOTIFY_TOTAL_BYTES",             NOTIFY_ONE_VALUE, spoolss_notify_job_size },
 };
 
 /*******************************************************************
@@ -2452,43 +2796,46 @@ static uint32 size_of_notify_info_data(uint16 type, uint16 field)
 {
        int i=0;
 
-       while (notify_info_data_table[i].type != END)
-       {
-               if ( (notify_info_data_table[i].type == type ) &&
-                    (notify_info_data_table[i].field == field ) )
-               {
-                       return (notify_info_data_table[i].size);
+       for (i = 0; i < sizeof(notify_info_data_table); i++) {
+               if (notify_info_data_table[i].type == type &&
+                   notify_info_data_table[i].field == field) {
+                       switch(notify_info_data_table[i].size) {
+                       case NOTIFY_ONE_VALUE:
+                       case NOTIFY_TWO_VALUE:
+                               return 1;
+                       case NOTIFY_STRING:
+                               return 2;
+
+                       /* The only pointer notify data I have seen on
+                          the wire is the submitted time and this has
+                          the notify size set to 4. -tpot */
+
+                       case NOTIFY_POINTER:
+                               return 4;
+                       }
                }
-               i++;
        }
-       return (65535);
+
+       DEBUG(5, ("invalid notify data type %d/%d\n", type, field));
+
+       return 0;
 }
 
 /*******************************************************************
  Return the type of notify_info_data.
 ********************************************************************/
 
-static BOOL type_of_notify_info_data(uint16 type, uint16 field)
+static int type_of_notify_info_data(uint16 type, uint16 field)
 {
        int i=0;
 
-       while (notify_info_data_table[i].type != END)
-       {
-               if ( (notify_info_data_table[i].type == type ) &&
-                    (notify_info_data_table[i].field == field ) )
-               {
-                       if (notify_info_data_table[i].size == POINTER)
-                       {
-                               return (False);
-                       }
-                       else
-                       {
-                               return (True);
-                       }
-               }
-               i++;
+       for (i = 0; i < sizeof(notify_info_data_table); i++) {
+               if (notify_info_data_table[i].type == type &&
+                   notify_info_data_table[i].field == field)
+                       return notify_info_data_table[i].size;
        }
-       return (False);
+
+       return False;
 }
 
 /****************************************************************************
@@ -2496,21 +2843,18 @@ static BOOL type_of_notify_info_data(uint16 type, uint16 field)
 
 static int search_notify(uint16 type, uint16 field, int *value)
 {      
-       int j;
-       BOOL found;
+       int i;
 
-       for (j=0, found=False; found==False && notify_info_data_table[j].type != END ; j++)
-       {
-               if ( (notify_info_data_table[j].type  == type  ) &&
-                    (notify_info_data_table[j].field == field ) )
-                       found=True;
+       for (i = 0; i < sizeof(notify_info_data_table); i++) {
+               if (notify_info_data_table[i].type == type &&
+                   notify_info_data_table[i].field == field &&
+                   notify_info_data_table[i].fn != NULL) {
+                       *value = i;
+                       return True;
+               }
        }
-       *value=--j;
-
-       if ( found && (notify_info_data_table[j].fn != NULL) )
-               return True;
-       else
-               return False;   
+       
+       return False;   
 }
 
 /****************************************************************************
@@ -2521,7 +2865,12 @@ void construct_info_data(SPOOL_NOTIFY_INFO_DATA *info_data, uint16 type, uint16
        info_data->type     = type;
        info_data->field    = field;
        info_data->reserved = 0;
-       info_data->id       = id;
+
+       if (type == JOB_NOTIFY_TYPE)
+               info_data->id = id;
+       else 
+               info_data->id = 0;
+
        info_data->size     = size_of_notify_info_data(type, field);
        info_data->enc_type = type_of_notify_info_data(type, field);
 }
@@ -2570,7 +2919,7 @@ static BOOL construct_notify_printer_info(SPOOL_NOTIFY_INFO *info, int
 
                current_data=&info->data[info->count];
 
-               construct_info_data(current_data, type, field, id);             
+               construct_info_data(current_data, type, field, id);
 
                DEBUG(10,("construct_notify_printer_info: calling [%s]  snum=%d  printername=[%s])\n",
                                notify_info_data_table[j].name, snum, printer->info_2->printername ));
@@ -2812,7 +3161,6 @@ static WERROR printer_notify_info(pipes_struct *p, POLICY_HND *hnd, SPOOL_NOTIFY
 WERROR _spoolss_rfnpcnex( pipes_struct *p, SPOOL_Q_RFNPCNEX *q_u, SPOOL_R_RFNPCNEX *r_u)
 {
        POLICY_HND *handle = &q_u->handle;
-/*     uint32 change = q_u->change; - notused. */
 /*     SPOOL_NOTIFY_OPTION *option = q_u->option; - notused. */
        SPOOL_NOTIFY_INFO *info = &r_u->info;
 
@@ -2830,17 +3178,19 @@ WERROR _spoolss_rfnpcnex( pipes_struct *p, SPOOL_Q_RFNPCNEX *q_u, SPOOL_R_RFNPCN
 
        DEBUG(4,("Printer type %x\n",Printer->printer_type));
 
-       /* jfm: the change value isn't used right now.
-        *      we will honour it when
-        *      a) we'll be able to send notification to the client
-        *      b) we'll have a way to communicate between the spoolss process.
-        *
-        *      same thing for option->flags
+       /*
+        *      We are now using the change value, and 
         *      I should check for PRINTER_NOTIFY_OPTIONS_REFRESH but as
         *      I don't have a global notification system, I'm sending back all the
         *      informations even when _NOTHING_ has changed.
         */
 
+       /* We need to keep track of the change value to send back in 
+           RRPCN replies otherwise our updates are ignored. */
+
+       if (Printer->notify.client_connected)
+               Printer->notify.change = q_u->change;
+
        /* just ignore the SPOOL_NOTIFY_OPTION */
        
        switch (Printer->printer_type) {
@@ -2932,7 +3282,7 @@ static BOOL construct_printer_info_0(PRINTER_INFO_0 *printer, int snum)
 
        printer->global_counter = global_counter;
        printer->total_pages = 0;
-#if 0  /* JERRY */
+#ifndef EMULATE_WIN2K_HACK     /* JERRY */
        printer->major_version = 0x0004;        /* NT 4 */
        printer->build_version = 0x0565;        /* build 1381 */
 #else
@@ -2971,7 +3321,6 @@ static BOOL construct_printer_info_0(PRINTER_INFO_0 *printer, int snum)
  * construct_printer_info_1
  * fill a printer_info_1 struct
  ********************************************************************/
-
 static BOOL construct_printer_info_1(uint32 flags, PRINTER_INFO_1 *printer, int snum)
 {
        pstring chaine;
@@ -3046,8 +3395,10 @@ static DEVICEMODE *construct_dev_mode(int snum)
        if (printer->info_2->devmode)
                ntdevmode = dup_nt_devicemode(printer->info_2->devmode);
 
-       if (ntdevmode == NULL)
+       if (ntdevmode == NULL) {
+               DEBUG(5, ("BONG! There was no device mode!\n"));
                goto fail;
+       }
 
        DEBUGADD(8,("loading DEVICEMODE\n"));
 
@@ -3408,10 +3759,26 @@ static WERROR enum_all_printers_info_1_remote(fstring name, NEW_BUFFER *buffer,
  enum_all_printers_info_1_network.
 *********************************************************************/
 
-static WERROR enum_all_printers_info_1_network(NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
+static WERROR enum_all_printers_info_1_network(fstring name, NEW_BUFFER *buffer, uint32 offered, uint32 *needed, uint32 *returned)
 {
+       char *s = name;
+
        DEBUG(4,("enum_all_printers_info_1_network\n"));        
        
+       /* If we respond to a enum_printers level 1 on our name with flags
+          set to PRINTER_ENUM_REMOTE with a list of printers then these
+          printers incorrectly appear in the APW browse list.
+          Specifically the printers for the server appear at the workgroup
+          level where all the other servers in the domain are
+          listed. Windows responds to this call with a
+          WERR_CAN_NOT_COMPLETE so we should do the same. */ 
+
+       if (name[0] == '\\' && name[1] == '\\')
+                s = name + 2;
+
+       if (is_myname_or_ipaddr(s))
+                return WERR_CAN_NOT_COMPLETE;
+
        return enum_all_printers_info_1(PRINTER_ENUM_UNKNOWN_8, buffer, offered, needed, returned);
 }
 
@@ -3449,9 +3816,9 @@ static WERROR enum_all_printers_info_2(NEW_BUFFER *buffer, uint32 offered, uint3
        }
        
        /* check the required size. */  
-       for (i=0; i<*returned; i++)
+       for (i=0; i<*returned; i++) 
                (*needed) += spoolss_size_printer_info_2(&printers[i]);
-
+       
        if (!alloc_buffer_size(buffer, *needed)) {
                for (i=0; i<*returned; i++) {
                        free_devmode(printers[i].devmode);
@@ -3498,7 +3865,7 @@ static WERROR enumprinters_level1( uint32 flags, fstring name,
                return enum_all_printers_info_1_remote(name, buffer, offered, needed, returned);
 
        if (flags & PRINTER_ENUM_NETWORK)
-               return enum_all_printers_info_1_network(buffer, offered, needed, returned);
+               return enum_all_printers_info_1_network(name, buffer, offered, needed, returned);
 
        return WERR_OK; /* NT4sp5 does that */
 }
@@ -3681,7 +4048,7 @@ static WERROR getprinter_level_2(int snum, NEW_BUFFER *buffer, uint32 offered, u
        
        /* check the required size. */  
        *needed += spoolss_size_printer_info_2(printer);
-
+       
        if (!alloc_buffer_size(buffer, *needed)) {
                free_printer_info_2(printer);
                return WERR_INSUFFICIENT_BUFFER;
@@ -4728,6 +5095,7 @@ static BOOL check_printer_ok(NT_PRINTER_INFO_LEVEL_2 *info, int snum)
 
 static BOOL add_printer_hook(NT_PRINTER_INFO_LEVEL *printer)
 {
+       extern userdom_struct current_user_info;
        char *cmd = lp_addprinter_cmd();
        char **qlines;
        pstring command;
@@ -4742,13 +5110,13 @@ static BOOL add_printer_hook(NT_PRINTER_INFO_LEVEL *printer)
                        get_called_name());
        /* change \ to \\ for the shell */
        all_string_sub(driverlocation,"\\","\\\\",sizeof(pstring));
-
+       standard_sub_basic(current_user_info.smb_name, remote_machine,sizeof(remote_machine));
+       
        slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
                        cmd, printer->info_2->printername, printer->info_2->sharename,
                        printer->info_2->portname, printer->info_2->drivername,
                        printer->info_2->location, driverlocation, remote_machine);
 
-       /* Convert script args to unix-codepage */
        DEBUG(10,("Running [%s]\n", command));
        ret = smbrun(command, &fd);
        DEBUGADD(10,("returned [%d]\n", ret));
@@ -5039,13 +5407,10 @@ static WERROR update_printer(pipes_struct *p, POLICY_HND *handle, uint32 level,
        int snum;
        NT_PRINTER_INFO_LEVEL *printer = NULL, *old_printer = NULL;
        Printer_entry *Printer = find_printer_index_by_hnd(p, handle);
-       PRINTER_MESSAGE_INFO msg;
        WERROR result;
 
        DEBUG(8,("update_printer\n"));
-       
-       ZERO_STRUCT(msg);
-       
+
        result = WERR_OK;
 
        if (level!=2) {
@@ -5159,8 +5524,11 @@ static WERROR update_printer(pipes_struct *p, POLICY_HND *handle, uint32 level,
                 * bound to the printer, simulating what happens in the Windows arch.
                 */
                if (!strequal(printer->info_2->drivername, old_printer->info_2->drivername)){
-                       set_driver_init(printer, 2);
-                       msg.flags |= PRINTER_MESSAGE_DRIVER;
+                       if (!set_driver_init(printer, 2)) {
+                               DEBUG(5,("update_printer: Error restoring driver initialization data for driver [%s]!\n",
+                                       printer->info_2->drivername));
+                       }
+                       notify_printer_driver(snum, printer->info_2->drivername);
                }
        }
 
@@ -5171,28 +5539,18 @@ static WERROR update_printer(pipes_struct *p, POLICY_HND *handle, uint32 level,
           all the possible changes                                         */
 
        if (!strequal(printer->info_2->comment, old_printer->info_2->comment))
-               msg.flags |= PRINTER_MESSAGE_COMMENT;
+               notify_printer_comment(snum, printer->info_2->comment);
 
        if (!strequal(printer->info_2->sharename, old_printer->info_2->sharename))
-               msg.flags |= PRINTER_MESSAGE_SHARENAME;
+               notify_printer_sharename(snum, printer->info_2->sharename);
 
        if (!strequal(printer->info_2->portname, old_printer->info_2->portname))
-               msg.flags |= PRINTER_MESSAGE_PORT;
+               notify_printer_port(snum, printer->info_2->portname);
 
        if (!strequal(printer->info_2->location, old_printer->info_2->location))
-               msg.flags |= PRINTER_MESSAGE_LOCATION;
-
-       ZERO_STRUCT(msg);
-       
-       msg.low = PRINTER_CHANGE_ADD_PRINTER;
-       fstrcpy(msg.printer_name, printer->info_2->printername);
-
-       /* only send a notify if something changed */
-       if (msg.flags) {
-               srv_spoolss_sendnotify(msg.printer_name, 0, PRINTER_CHANGE_ADD_PRINTER, msg.flags);
-       }
+               notify_printer_location(snum, printer->info_2->location);
 
- done:
+done:
        free_a_printer(&printer, 2);
        free_a_printer(&old_printer, 2);
 
@@ -5310,7 +5668,7 @@ static void fill_job_info_1(JOB_INFO_1 *job_info, print_queue_struct *queue,
 
 static BOOL fill_job_info_2(JOB_INFO_2 *job_info, print_queue_struct *queue,
                             int position, int snum, 
-                           NT_PRINTER_INFO_LEVEL *ntprinter, 
+                           NT_PRINTER_INFO_LEVEL *ntprinter,
                            DEVICEMODE *devmode)
 {
        pstring temp_name;
@@ -5427,7 +5785,7 @@ static WERROR enumjobs_level2(print_queue_struct *queue, int snum,
                *returned = 0;
                goto done;
        }
-
+               
        if (!(devmode = construct_dev_mode(snum))) {
                *returned = 0;
                result = WERR_NOMEM;
@@ -5470,6 +5828,7 @@ static WERROR enumjobs_level2(print_queue_struct *queue, int snum,
        SAFE_FREE(info);
 
        return result;
+
 }
 
 /****************************************************************************
@@ -5538,8 +5897,6 @@ WERROR _spoolss_setjob(pipes_struct *p, SPOOL_Q_SETJOB *q_u, SPOOL_R_SETJOB *r_u
 {
        POLICY_HND *handle = &q_u->handle;
        uint32 jobid = q_u->jobid;
-/*     uint32 level = q_u->level; - notused. */
-/*     JOB_INFO *ctr = &q_u->ctr; - notused. */
        uint32 command = q_u->command;
 
        struct current_user user;
@@ -5597,9 +5954,7 @@ static WERROR enumprinterdrivers_level1(fstring servername, fstring architecture
 
        *returned=0;
 
-#define MAX_VERSION 4
-
-       for (version=0; version<MAX_VERSION; version++) {
+       for (version=0; version<DRIVER_MAX_VERSION; version++) {
                list=NULL;
                ndrivers=get_ntdrivers(&list, architecture, version);
                DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
@@ -5678,9 +6033,7 @@ static WERROR enumprinterdrivers_level2(fstring servername, fstring architecture
 
        *returned=0;
 
-#define MAX_VERSION 4
-
-       for (version=0; version<MAX_VERSION; version++) {
+       for (version=0; version<DRIVER_MAX_VERSION; version++) {
                list=NULL;
                ndrivers=get_ntdrivers(&list, architecture, version);
                DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
@@ -5760,9 +6113,7 @@ static WERROR enumprinterdrivers_level3(fstring servername, fstring architecture
 
        *returned=0;
 
-#define MAX_VERSION 4
-
-       for (version=0; version<MAX_VERSION; version++) {
+       for (version=0; version<DRIVER_MAX_VERSION; version++) {
                list=NULL;
                ndrivers=get_ntdrivers(&list, architecture, version);
                DEBUGADD(4,("we have:[%d] drivers in environment [%s] and version [%d]\n", ndrivers, architecture, version));
@@ -6415,9 +6766,6 @@ static WERROR spoolss_addprinterex_level_2( pipes_struct *p, const UNISTR2 *uni_
        }
 
        update_c_setprinter(False);
-
-       srv_spoolss_sendnotify(printer->info_2->printername, 0, PRINTER_CHANGE_ADD_PRINTER, 0x0);
-
        free_a_printer(&printer,2);
 
        return WERR_OK;
@@ -6462,10 +6810,12 @@ WERROR _spoolss_addprinterdriver(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVER *q_u,
        WERROR err = WERR_OK;
        NT_PRINTER_DRIVER_INFO_LEVEL driver;
        struct current_user user;
-       
+       fstring driver_name;
+       uint32 version;
+
        ZERO_STRUCT(driver);
 
-       get_current_user(&user, p);     
+       get_current_user(&user, p);
        
        if (!convert_printer_driver_info(info, &driver, level)) {
                err = WERR_NOMEM;
@@ -6489,11 +6839,131 @@ WERROR _spoolss_addprinterdriver(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVER *q_u,
                goto done;
        }
 
- done:
+       /* BEGIN_ADMIN_LOG */
+        switch(level) {
+           case 3:
+               sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.",
+                       driver.info_3->name,drv_ver_to_os[driver.info_3->cversion],uidtoname(user.uid));
+               fstrcpy(driver_name, driver.info_3->name);
+               break;
+           case 6:   
+               sys_adminlog(LOG_INFO,"Added printer driver. Print driver name: %s. Print driver OS: %s. Administrator name: %s.",
+                       driver.info_6->name,drv_ver_to_os[driver.info_6->version],uidtoname(user.uid));
+               fstrcpy(driver_name, driver.info_6->name);
+               break;
+        }
+       /* END_ADMIN_LOG */
+
+       /* 
+        * I think this is where he DrvUpgradePrinter() hook would be
+        * be called in a driver's interface DLL on a Windows NT 4.0/2k
+        * server.  Right now, we just need to send ourselves a message
+        * to update each printer bound to this driver.   --jerry       
+        */
+        
+       if (!srv_spoolss_drv_upgrade_printer(driver_name)) {
+               DEBUG(0,("_spoolss_addprinterdriver: Failed to send message about upgrading driver [%s]!\n",
+                       driver_name));
+       }
+
+       /*
+        * Based on the version (e.g. driver destination dir: 0=9x,2=Nt/2k,3=2k/Xp),
+        * decide if the driver init data should be deleted. The rules are:
+        *  1) never delete init data if it is a 9x driver, they don't use it anyway
+        *  2) delete init data only if there is no 2k/Xp driver
+        *  3) always delete init data
+        * The generalized rule is always use init data from the highest order driver.
+        * It is necessary to follow the driver install by an initialization step to
+        * finish off this process.
+       */
+       if (level == 3)
+               version = driver.info_3->cversion;
+       else if (level == 6)
+               version = driver.info_6->version;
+       else
+               version = -1;
+       switch (version) {
+               /*
+                * 9x printer driver - never delete init data
+               */
+               case 0: 
+                       DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for 9x driver [%s]\n",
+                                       driver_name));
+                       break;
+               
+               /*
+                * Nt or 2k (compatiblity mode) printer driver - only delete init data if
+                * there is no 2k/Xp driver init data for this driver name.
+               */
+               case 2:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL driver1;
+
+                       if (!W_ERROR_IS_OK(get_a_printer_driver(&driver1, 3, driver_name, "Windows NT x86", 3))) {
+                               /*
+                                * No 2k/Xp driver found, delete init data (if any) for the new Nt driver.
+                               */
+                               if (!del_driver_init(driver_name))
+                                       DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) Nt failed!\n", driver_name));
+                       } else {
+                               /*
+                                * a 2k/Xp driver was found, don't delete init data because Nt driver will use it.
+                               */
+                               free_a_printer_driver(driver1,3);
+                               DEBUG(10,("_spoolss_addprinterdriver: init data not deleted for Nt driver [%s]\n", 
+                                               driver_name));
+                       }
+               }
+               break;
+
+               /*
+                * 2k or Xp printer driver - always delete init data
+               */
+               case 3: 
+                       if (!del_driver_init(driver_name))
+                               DEBUG(6,("_spoolss_addprinterdriver: del_driver_init(%s) 2k/Xp failed!\n", driver_name));
+                       break;
+
+               default:
+                       DEBUG(0,("_spoolss_addprinterdriver: invalid level=%d\n", level));
+                       break;
+       }
+
+       
+done:
        free_a_printer_driver(driver, level);
        return err;
 }
 
+/********************************************************************
+ * spoolss_addprinterdriverex
+ ********************************************************************/
+
+WERROR _spoolss_addprinterdriverex(pipes_struct *p, SPOOL_Q_ADDPRINTERDRIVEREX *q_u, SPOOL_R_ADDPRINTERDRIVEREX *r_u)
+{
+       SPOOL_Q_ADDPRINTERDRIVER q_u_local;
+       SPOOL_R_ADDPRINTERDRIVER r_u_local;
+       
+       /* 
+        * we only support the semantics of AddPrinterDriver()
+        * i.e. only copy files that are newer than existing ones
+        */
+       
+       if ( q_u->copy_flags != APD_COPY_NEW_FILES )
+               return WERR_ACCESS_DENIED;
+       
+       /* just pass the information off to _spoolss_addprinterdriver() */
+       ZERO_STRUCT(q_u_local);
+       ZERO_STRUCT(r_u_local);
+
+       q_u_local.server_name_ptr = q_u->server_name_ptr;
+       copy_unistr2(&q_u_local.server_name, &q_u->server_name);
+       q_u_local.level = q_u->level;
+       memcpy( &q_u_local.info, &q_u->info, sizeof(SPOOL_PRINTER_DRIVER_INFO_LEVEL) );
+       
+       return _spoolss_addprinterdriver( p, &q_u_local, &r_u_local );
+}
+
 /****************************************************************************
 ****************************************************************************/
 
@@ -6632,23 +7102,6 @@ WERROR _spoolss_enumprinterdata(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATA *q_u, S
        if ( (in_value_len==0) && (in_data_len==0) ) {
                DEBUGADD(6,("Activating NT mega-hack to find sizes\n"));
 
-#if 0
-               /*
-                * NT can ask for a specific parameter size - we need to return NO_MORE_ITEMS
-                * if this parameter size doesn't exist.
-                * Ok - my opinion here is that the client is not asking for the greatest
-                * possible size of all the parameters, but is asking specifically for the size needed
-                * for this specific parameter. In that case we can remove the loop below and
-                * simplify this lookup code considerably. JF - comments welcome. JRA.
-                */
-
-               if (!get_specific_param_by_index(*printer, 2, idx, value, &data, &type, &data_len)) {
-                       SAFE_FREE(data);
-                       free_a_printer(&printer, 2);
-                       return WERR_NO_MORE_ITEMS;
-               }
-#endif
-
                SAFE_FREE(data);
 
                param_index=0;
@@ -6692,7 +7145,7 @@ WERROR _spoolss_enumprinterdata(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATA *q_u, S
                if((*out_value=(uint16 *)talloc_zero(p->mem_ctx, in_value_len*sizeof(uint8))) == NULL)
                        return WERR_NOMEM;
 
-               *out_value_len = rpcstr_push((char *)*out_value, "", in_value_len, 0);
+               *out_value_len = (uint32)rpcstr_push((char *)*out_value, "", in_value_len, 0);
 
                /* the data is counted in bytes */
                *out_max_data_len = in_data_len;
@@ -6720,7 +7173,7 @@ WERROR _spoolss_enumprinterdata(pipes_struct *p, SPOOL_Q_ENUMPRINTERDATA *q_u, S
                return WERR_NOMEM;
        }
        
-       *out_value_len = rpcstr_push((char *)*out_value,value, in_value_len, 0);
+       *out_value_len = (uint32)rpcstr_push((char *)*out_value,value, in_value_len, 0);
 
        *out_type=type;
 
@@ -6747,10 +7200,8 @@ WERROR _spoolss_setprinterdata( pipes_struct *p, SPOOL_Q_SETPRINTERDATA *q_u, SP
        POLICY_HND *handle = &q_u->handle;
        UNISTR2 *value = &q_u->value;
        uint32 type = q_u->type;
-/*     uint32 max_len = q_u->max_len; - notused. */
        uint8 *data = q_u->data;
        uint32 real_len = q_u->real_len;
-/*     uint32 numeric_data = q_u->numeric_data; - notused. */
 
        NT_PRINTER_INFO_LEVEL *printer = NULL;
        NT_PRINTER_PARAM *param = NULL, old_param;
@@ -7022,8 +7473,6 @@ done:
 WERROR _spoolss_setform(pipes_struct *p, SPOOL_Q_SETFORM *q_u, SPOOL_R_SETFORM *r_u)
 {
        POLICY_HND *handle = &q_u->handle;
-/*     UNISTR2 *uni_name = &q_u->name; - notused. */
-/*     uint32 level = q_u->level; - notused. */
        FORM *form = &q_u->form;
        nt_forms_struct tmpForm;
        int snum;
@@ -7114,12 +7563,10 @@ static WERROR enumprintprocessors_level_1(NEW_BUFFER *buffer, uint32 offered, ui
 
 WERROR _spoolss_enumprintprocessors(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCESSORS *q_u, SPOOL_R_ENUMPRINTPROCESSORS *r_u)
 {
-/*     UNISTR2 *name = &q_u->name; - notused. */
-/*     UNISTR2 *environment = &q_u->environment; - notused. */
        uint32 level = q_u->level;
-    NEW_BUFFER *buffer = NULL;
+       NEW_BUFFER *buffer = NULL;
        uint32 offered = q_u->offered;
-    uint32 *needed = &r_u->needed;
+       uint32 *needed = &r_u->needed;
        uint32 *returned = &r_u->returned;
 
        /* that's an [in out] buffer */
@@ -7183,8 +7630,6 @@ static WERROR enumprintprocdatatypes_level_1(NEW_BUFFER *buffer, uint32 offered,
 
 WERROR _spoolss_enumprintprocdatatypes(pipes_struct *p, SPOOL_Q_ENUMPRINTPROCDATATYPES *q_u, SPOOL_R_ENUMPRINTPROCDATATYPES *r_u)
 {
-/*     UNISTR2 *name = &q_u->name; - notused. */
-/*     UNISTR2 *processor = &q_u->processor; - notused. */
        uint32 level = q_u->level;
        NEW_BUFFER *buffer = NULL;
        uint32 offered = q_u->offered;
@@ -7279,11 +7724,10 @@ static WERROR enumprintmonitors_level_2(NEW_BUFFER *buffer, uint32 offered, uint
 
 WERROR _spoolss_enumprintmonitors(pipes_struct *p, SPOOL_Q_ENUMPRINTMONITORS *q_u, SPOOL_R_ENUMPRINTMONITORS *r_u)
 {
-/*     UNISTR2 *name = &q_u->name; - notused. */
        uint32 level = q_u->level;
-    NEW_BUFFER *buffer = NULL;
+       NEW_BUFFER *buffer = NULL;
        uint32 offered = q_u->offered;
-    uint32 *needed = &r_u->needed;
+       uint32 *needed = &r_u->needed;
        uint32 *returned = &r_u->returned;
 
        /* that's an [in out] buffer */
@@ -7427,7 +7871,7 @@ static WERROR getjob_level_2(print_queue_struct *queue, int count, int snum, uin
        free_job_info_2(info_2);        /* Also frees devmode */
        SAFE_FREE(info_2);
        free_a_printer(&ntprinter, 2);
-       
+
        return ret;
 }
 
@@ -7594,6 +8038,34 @@ WERROR _spoolss_setprinterdataex(pipes_struct *p, SPOOL_Q_SETPRINTERDATAEX *q_u,
        return _spoolss_setprinterdata(p, &q_u_local, &r_u_local);
 }
 
+
+/********************************************************************
+ * spoolss_deleteprinterdataex
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterdataex(pipes_struct *p, SPOOL_Q_DELETEPRINTERDATAEX *q_u, SPOOL_R_DELETEPRINTERDATAEX *r_u)
+{
+       SPOOL_Q_DELETEPRINTERDATA q_u_local;
+       SPOOL_R_DELETEPRINTERDATA r_u_local;
+       fstring key;
+       
+        /* From MSDN documentation of SetPrinterDataEx: pass request to
+           SetPrinterData if key is "PrinterDriverData" */
+
+        unistr2_to_ascii(key, &q_u->keyname, sizeof(key) - 1);
+
+        if (strcmp(key, "PrinterDriverData") != 0)
+               return WERR_INVALID_PARAM;
+       
+       memcpy(&q_u_local.handle, &q_u->handle, sizeof(POLICY_HND));
+       copy_unistr2(&q_u_local.valuename, &q_u->valuename);
+       
+       return _spoolss_deleteprinterdata( p, &q_u_local, &r_u_local );
+}
+
+
+
+
 /********************************************************************
  * spoolss_enumprinterkey
  ********************************************************************/
@@ -7660,6 +8132,34 @@ WERROR _spoolss_enumprinterkey(pipes_struct *p, SPOOL_Q_ENUMPRINTERKEY *q_u, SPO
         return WERR_BADFILE;
 }
 
+/********************************************************************
+ * spoolss_deleteprinterkey
+ ********************************************************************/
+
+WERROR _spoolss_deleteprinterkey(pipes_struct *p, SPOOL_Q_DELETEPRINTERKEY *q_u, SPOOL_R_DELETEPRINTERKEY *r_u)
+{
+       Printer_entry   *Printer = find_printer_index_by_hnd(p, &q_u->handle);
+       fstring key;
+       
+       if (!Printer) {
+               DEBUG(2,("_spoolss_deleteprinterkey: Invalid handle (%s:%u:%u).\n", OUR_HANDLE(&q_u->handle)));
+               return WERR_BADFID;
+       }
+       
+        unistr2_to_ascii(key, &q_u->keyname, sizeof(key) - 1);
+
+        if (strcmp(key, "PrinterDriverData") != 0)
+               return WERR_INVALID_PARAM;
+               
+       /* 
+        * this is what 2k returns when you try to delete the "PrinterDriverData"
+        * key
+        */
+        
+       return WERR_ACCESS_DENIED;      
+}
+
+
 /********************************************************************
  * spoolss_enumprinterdataex
  ********************************************************************/
@@ -7801,7 +8301,7 @@ static WERROR getprintprocessordirectory_level_1(UNISTR2 *name,
 
        unistr2_to_ascii(long_archi, environment, sizeof(long_archi)-1);
 
-       if (get_short_archi(short_archi, long_archi)==FALSE)
+       if (get_short_archi(short_archi, long_archi)==False)
                return WERR_INVALID_ENVIRONMENT;
 
        if((info=(PRINTPROCESSOR_DIRECTORY_1 *)malloc(sizeof(PRINTPROCESSOR_DIRECTORY_1))) == NULL)
@@ -7834,6 +8334,7 @@ WERROR _spoolss_getprintprocessordirectory(pipes_struct *p, SPOOL_Q_GETPRINTPROC
        NEW_BUFFER *buffer = NULL;
        uint32 offered = q_u->offered;
        uint32 *needed = &r_u->needed;
+       WERROR result;
 
        /* that's an [in out] buffer */
        spoolss_move_buffer(q_u->buffer, &r_u->buffer);
@@ -7845,12 +8346,13 @@ WERROR _spoolss_getprintprocessordirectory(pipes_struct *p, SPOOL_Q_GETPRINTPROC
 
        switch(level) {
        case 1:
-               return getprintprocessordirectory_level_1
+               result = getprintprocessordirectory_level_1
                  (&q_u->name, &q_u->environment, buffer, offered, needed);
        default:
-               return WERR_UNKNOWN_LEVEL;
+               result = WERR_UNKNOWN_LEVEL;
        }
 
-       return WERR_ACCESS_DENIED;
+       return result;
 }
 
+