Changes from APPLIANCE_HEAD:
[samba.git] / source3 / printing / nt_printing.c
index 3e15082f2105ff5f22471d4fcffa152c6b34cd53..2572a98bdea495315df7d9fa91ddaece0b6511a5 100644 (file)
@@ -1,20 +1,21 @@
-/* 
+#define OLD_NTDOMAIN 1
+/*
  *  Unix SMB/Netbios implementation.
  *  Version 1.9.
  *  RPC Pipe client / server routines
  *  Copyright (C) Andrew Tridgell              1992-2000,
  *  Copyright (C) Jean François Micouleau      1998-2000.
- *  
+ *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 #include "includes.h"
 
 extern int DEBUGLEVEL;
+extern pstring global_myname;
+extern DOM_SID global_sid_World;
+
+static TDB_CONTEXT *tdb; /* used for driver files */
+
+#define FORMS_PREFIX "FORMS/"
+#define DRIVERS_PREFIX "DRIVERS/"
+#define PRINTERS_PREFIX "PRINTERS/"
+
+#define DATABASE_VERSION 1
+
+/* Map generic permissions to printer object specific permissions */
+
+struct generic_mapping printer_generic_mapping = {
+       PRINTER_READ,
+       PRINTER_WRITE,
+       PRINTER_EXECUTE,
+       PRINTER_ALL_ACCESS
+};
+
+/* We need one default form to support our default printer. Msoft adds the
+forms it wants and in the ORDER it wants them (note: DEVMODE papersize is an
+array index). Letter is always first, so (for the current code) additions
+always put things in the correct order. */
+static nt_forms_struct default_forms[] = {
+       {"Letter", 0x2, 0x34b5b, 0x44367, 0x0, 0x0, 0x34b5b, 0x44367},
+};
+
 
 /****************************************************************************
-parse a form line.
+open the NT printing tdb
 ****************************************************************************/
-static BOOL parse_form_entry(char *line, nt_forms_struct *buf)
+BOOL nt_printing_init(void)
 {
-#define NAMETOK   0
-#define FLAGTOK   1
-#define WIDTHTOK  2
-#define LENGTHTOK 3
-#define LEFTTOK   4
-#define TOPTOK    5
-#define RIGHTTOK  6
-#define BOTTOMTOK 7
-#define MAXTOK 8
-       char *tok[MAXTOK];
-       int count = 0;
-
-       tok[0] = strtok(line,":");
-
-       if (!tok[0]) return False;
-       
-       /* strip the comment lines */
-       if (tok[0][0]=='#') return (False);     
-       count++;
-       
-       while ( ((tok[count] = strtok(NULL,":")) != NULL ) && count<MAXTOK-1)
-       {
-               count++;
+       static pid_t local_pid;
+       char *vstring = "INFO/version";
+
+       if (tdb && local_pid == sys_getpid()) return True;
+       tdb = tdb_open(lock_path("ntdrivers.tdb"), 0, 0, O_RDWR|O_CREAT, 0600);
+       if (!tdb) {
+               DEBUG(0,("Failed to open nt drivers database\n"));
+               return False;
        }
 
-       if (count < MAXTOK-1) return False;
+       local_pid = sys_getpid();
+
+       /* handle a Samba upgrade */
+       tdb_lock_bystring(tdb, vstring);
+       if (tdb_fetch_int(tdb, vstring) != DATABASE_VERSION) {
+               tdb_traverse(tdb, (tdb_traverse_func)tdb_delete, NULL);
+               tdb_store_int(tdb, vstring, DATABASE_VERSION);
+       }
+       tdb_unlock_bystring(tdb, vstring);
+
+       return True;
+}
+
 
-       StrnCpy(buf->name,tok[NAMETOK],sizeof(buf->name)-1);
-       buf->flag=atoi(tok[FLAGTOK]);
-       buf->width=atoi(tok[WIDTHTOK]);
-       buf->length=atoi(tok[LENGTHTOK]);
-       buf->left=atoi(tok[LEFTTOK]);
-       buf->top=atoi(tok[TOPTOK]);
-       buf->right=atoi(tok[RIGHTTOK]);
-       buf->bottom=atoi(tok[BOTTOMTOK]);
-       
-       return(True);
-}  
-  
 /****************************************************************************
 get a form struct list
 ****************************************************************************/
 int get_ntforms(nt_forms_struct **list)
 {
-       char **lines;
-       char *lp_forms = lp_nt_forms();
-       int total=0;
-       int grandtotal=0;
+       TDB_DATA kbuf, newkey, dbuf;
+       nt_forms_struct form;
+       int ret;
        int i;
-       
-       lines = file_lines_load(lp_forms, NULL);
-       if (!lines) {
-               return(0);
-       }
+       int n = 0;
 
-       *list = NULL;
-
-       for (i=0; lines[i]; i++) {
-               char *line = lines[i];
-
-               *list = Realloc(*list, sizeof(nt_forms_struct)*(total+1));
-               if (! *list)
-               {
-                       total = 0;
-                       break;
-               }
-               memset( (char *)&(*list)[total], '\0', sizeof(nt_forms_struct) );
-               if ( parse_form_entry(line, &(*list)[total] ) )
-               {
-                       total++;
+       for (kbuf = tdb_firstkey(tdb);
+            kbuf.dptr;
+            newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+               if (strncmp(kbuf.dptr, FORMS_PREFIX, strlen(FORMS_PREFIX)) != 0) continue;
+               
+               dbuf = tdb_fetch(tdb, kbuf);
+               if (!dbuf.dptr) continue;
+
+               fstrcpy(form.name, kbuf.dptr+strlen(FORMS_PREFIX));
+               ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "dddddddd",
+                                &i, &form.flag, &form.width, &form.length, &form.left,
+                                &form.top, &form.right, &form.bottom);
+               safe_free(dbuf.dptr);
+               if (ret != dbuf.dsize) continue;
+
+               /* allocate space and populate the list in correct order */
+               if (i+1 > n) {
+                       *list = Realloc(*list, sizeof(nt_forms_struct)*(i+1));
+                       n = i+1;
                }
-               grandtotal++;
-       }    
+               (*list)[i] = form;
+       }
 
-       file_lines_free(lines);
+       /* we should never return a null forms list or NT gets unhappy */
+       if (n == 0) {
+               *list = (nt_forms_struct *)memdup(&default_forms[0], sizeof(default_forms));
+               n = sizeof(default_forms) / sizeof(default_forms[0]);
+       }
+       
 
-       return(total);
+       return n;
 }
 
 /****************************************************************************
@@ -113,36 +131,28 @@ write a form struct list
 ****************************************************************************/
 int write_ntforms(nt_forms_struct **list, int number)
 {
-       pstring line;
-       int fd;
-       char *file = lp_nt_forms();
-       int total=0;
-       int i;
-
-       *line=0;
-
-       DEBUG(106,("write_ntforms\n"));
-
-       unlink(file);
-       if((fd = sys_open(file, O_WRONLY|O_CREAT|O_EXCL, 0644)) == -1)
-       {
-              DEBUG(0, ("write_ntforms: Cannot create forms file [%s]. Error was %s\n", file, strerror(errno) ));
-              return(0);
-       }
-
-       for (i=0; i<number;i++)
-       {
-
-              fdprintf(fd,"%s:%d:%d:%d:%d:%d:%d:%d\n", (*list)[i].name,
-                       (*list)[i].flag, (*list)[i].width, (*list)[i].length,
-                       (*list)[i].left, (*list)[i].top, (*list)[i].right, (*list)[i].bottom);
+       pstring buf, key;
+       int len;
+       TDB_DATA kbuf,dbuf;
+       int i;
 
-              DEBUGADD(107,("adding entry [%s]\n", (*list)[i].name));
+       for (i=0;i<number;i++) {
+               /* save index, so list is rebuilt in correct order */
+               len = tdb_pack(buf, sizeof(buf), "dddddddd",
+                              i, (*list)[i].flag, (*list)[i].width, (*list)[i].length,
+                              (*list)[i].left, (*list)[i].top, (*list)[i].right,
+                              (*list)[i].bottom);
+               if (len > sizeof(buf)) break;
+               slprintf(key, sizeof(key), "%s%s", FORMS_PREFIX, (*list)[i].name);
+        dos_to_unix(key, True);            /* Convert key to unix-codepage */
+               kbuf.dsize = strlen(key)+1;
+               kbuf.dptr = key;
+               dbuf.dsize = len;
+               dbuf.dptr = buf;
+               if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) break;
        }
 
-       close(fd);
-       DEBUGADD(106,("closing file\n"));
-       return(total);
+       return i;
 }
 
 /****************************************************************************
@@ -154,29 +164,27 @@ BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count)
        BOOL update;
        fstring form_name;
 
-       /* 
-        * NT tries to add forms even when 
+       /*
+        * NT tries to add forms even when
         * they are already in the base
         * only update the values if already present
         */
 
        update=False;
        
-       unistr2_to_ascii(form_name, &(form->name), sizeof(form_name)-1);
-       for (n=0; n<*count && update==False; n++)
-       {
-               if (!strncmp((*list)[n].name, form_name, strlen(form_name)))
-               {
+       unistr2_to_ascii(form_name, &form->name, sizeof(form_name)-1);
+       for (n=0; n<*count; n++) {
+               if (!strncmp((*list)[n].name, form_name, strlen(form_name))) {
                        DEBUG(103, ("NT workaround, [%s] already exists\n", form_name));
                        update=True;
+                       break;
                }
        }
 
-       if (update==False)
-       {
+       if (update==False) {
                if((*list=Realloc(*list, (n+1)*sizeof(nt_forms_struct))) == NULL)
                        return False;
-               unistr2_to_ascii((*list)[n].name, &(form->name), sizeof((*list)[n].name)-1);
+               unistr2_to_ascii((*list)[n].name, &form->name, sizeof((*list)[n].name)-1);
                (*count)++;
        }
        
@@ -192,7 +200,55 @@ BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count)
 }
 
 /****************************************************************************
-update a form struct 
+ delete a named form struct
+****************************************************************************/
+BOOL delete_a_form(nt_forms_struct **list, UNISTR2 *del_name, int *count, uint32 *ret)
+{
+       pstring key;
+       TDB_DATA kbuf;
+       int n=0;
+       fstring form_name;
+
+       *ret = 0;
+
+       if (*count == 1) {
+               /*
+                * Don't delete the last form (no empty lists).
+                * CHECKME ! Is this correct ? JRA.
+                */
+               *ret = ERROR_INVALID_PARAMETER;
+               return False;
+       }
+
+       unistr2_to_ascii(form_name, del_name, sizeof(form_name)-1);
+
+       for (n=0; n<*count; n++) {
+               if (!strncmp((*list)[n].name, form_name, strlen(form_name))) {
+                       DEBUG(103, ("delete_a_form, [%s] in list\n", form_name));
+                       break;
+               }
+       }
+
+       if (n == *count) {
+               DEBUG(10,("delete_a_form, [%s] not found\n", form_name));
+               *ret = ERROR_INVALID_PARAMETER;
+               return False;
+       }
+
+       slprintf(key, sizeof(key), "%s%s", FORMS_PREFIX, (*list)[n].name);
+       dos_to_unix(key, True);                /* Convert key to unix-codepage */
+       kbuf.dsize = strlen(key)+1;
+       kbuf.dptr = key;
+       if (tdb_delete(tdb, kbuf) != 0) {
+               *ret = ERROR_NOT_ENOUGH_MEMORY;
+               return False;
+       }
+
+       return True;
+}
+
+/****************************************************************************
+update a form struct
 ****************************************************************************/
 void update_a_form(nt_forms_struct **list, const FORM *form, int count)
 {
@@ -218,56 +274,34 @@ void update_a_form(nt_forms_struct **list, const FORM *form, int count)
        (*list)[n].right=form->right;
        (*list)[n].bottom=form->bottom;
 }
+
 /****************************************************************************
 get the nt drivers list
 
-open the directory and look-up the matching names
+traverse the database and look-up the matching names
 ****************************************************************************/
-int get_ntdrivers(fstring **list, char *architecture)
+int get_ntdrivers(fstring **list, char *architecture, uint32 version)
 {
-       DIR *dirp;
-       char *dpname;
-       fstring name_match;
-       fstring short_archi;
-       fstring driver_name;
-       int match_len;
        int total=0;
+       fstring short_archi;
+       pstring key;
+       TDB_DATA kbuf, newkey;
 
-       DEBUG(105,("Getting the driver list from directory: [%s]\n", lp_nt_drivers_file()));
-       
-       *list=NULL;
-       dirp = opendir(lp_nt_drivers_file());
-
-       if (dirp == NULL)
-       {
-               DEBUG(0,("Error opening driver directory [%s]\n",lp_nt_drivers_file())); 
-               return(-1);
-       }
-       
        get_short_archi(short_archi, architecture);
-       slprintf(name_match, sizeof(name_match)-1, "NTdriver_%s_", short_archi);
-       match_len=strlen(name_match);
-       
-       while ((dpname = readdirname(dirp)) != NULL)
-       {
-               if (strncmp(dpname, name_match, match_len)==0)
-               {
-                       DEBUGADD(107,("Found: [%s]\n", dpname));
-                       
-                       fstrcpy(driver_name, dpname+match_len);
-                       all_string_sub(driver_name, "#", "/", 0);
+       slprintf(key, sizeof(key), "%s%s/%d/", DRIVERS_PREFIX, short_archi, version);
 
-                       if((*list = Realloc(*list, sizeof(fstring)*(total+1))) == NULL)
-                               return -1;
+       for (kbuf = tdb_firstkey(tdb);
+            kbuf.dptr;
+            newkey = tdb_nextkey(tdb, kbuf), safe_free(kbuf.dptr), kbuf=newkey) {
+               if (strncmp(kbuf.dptr, key, strlen(key)) != 0) continue;
+               
+               if((*list = Realloc(*list, sizeof(fstring)*(total+1))) == NULL)
+                       return -1;
 
-                       StrnCpy((*list)[total], driver_name, strlen(driver_name));
-                       DEBUGADD(106,("Added: [%s]\n", driver_name));           
-                       total++;
-               }
+               fstrcpy((*list)[total], kbuf.dptr+strlen(key));
+               total++;
        }
 
-       closedir(dirp);
        return(total);
 }
 
@@ -275,7 +309,7 @@ int get_ntdrivers(fstring **list, char *architecture)
 function to do the mapping between the long architecture name and
 the short one.
 ****************************************************************************/
-void get_short_archi(char *short_archi, char *long_archi)
+BOOL get_short_archi(char *short_archi, char *long_archi)
 {
        struct table {
                char *long_archi;
@@ -286,9 +320,9 @@ void get_short_archi(char *short_archi, char *long_archi)
        {
                {"Windows 4.0",          "WIN40"    },
                {"Windows NT x86",       "W32X86"   },
-               {"Windows NT R4000",     "W32mips"  },
-               {"Windows NT Alpha_AXP", "W32alpha" },
-               {"Windows NT PowerPC",   "W32ppc"   },
+               {"Windows NT R4000",     "W32MIPS"  },
+               {"Windows NT Alpha_AXP", "W32ALPHA" },
+               {"Windows NT PowerPC",   "W32PPC"   },
                {NULL,                   ""         }
        };
        
@@ -297,282 +331,1234 @@ void get_short_archi(char *short_archi, char *long_archi)
        DEBUG(107,("Getting architecture dependant directory\n"));
        do {
                i++;
-       } while ( (archi_table[i].long_archi!=NULL ) && strncmp(long_archi, archi_table[i].long_archi, strlen(long_archi)) );
+       } while ( (archi_table[i].long_archi!=NULL ) &&
+                 StrCaseCmp(long_archi, archi_table[i].long_archi) );
 
-       if (archi_table[i].long_archi==NULL)
-       {
+       if (archi_table[i].long_archi==NULL) {
                DEBUGADD(107,("Unknown architecture [%s] !\n", long_archi));
+               return FALSE;
        }
+
        StrnCpy (short_archi, archi_table[i].short_archi, strlen(archi_table[i].short_archi));
 
        DEBUGADD(108,("index: [%d]\n", i));
        DEBUGADD(108,("long architecture: [%s]\n", long_archi));
        DEBUGADD(108,("short architecture: [%s]\n", short_archi));
+       
+       return TRUE;
 }
 
 /****************************************************************************
+Determine the correct cVersion associated with an architecture and driver
 ****************************************************************************/
-static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
+static uint32 get_correct_cversion(fstring architecture, fstring driverpath_in,
+                                                                  struct current_user *user, uint32 *perr)
+{
+       int               cversion;
+       int               access_mode;
+       int               action;
+       int               ecode;
+       char              buf[PE_HEADER_SIZE];
+       ssize_t           byte_count;
+       pstring           driverpath;
+       fstring           user_name;
+       fstring           null_pw;
+       files_struct      *fsp = NULL;
+       BOOL              bad_path;
+       SMB_STRUCT_STAT   st;
+       struct passwd *pass;
+       connection_struct *conn;
+
+       ZERO_STRUCT(st);
+
+       /* If architecture is Windows 95/98, the version is always 0. */
+       if (strcmp(architecture, "WIN40") == 0) {
+               DEBUG(10,("get_correct_cversion: Driver is Win9x, cversion = 0\n"));
+               return 0;
+       }
+
+       become_root();
+       pass = getpwuid(user->uid);
+       if(pass == NULL) {
+               DEBUG(0,("get_correct_cversion: Unable to get passwd entry for uid %u\n",
+                               (unsigned int)user->uid ));
+               unbecome_root();
+               *perr = ERROR_ACCESS_DENIED;
+               return -1;
+       }
+       unbecome_root();
+
+       /* connect to the print$ share under the same account as the user connected
+        * to the rpc pipe */   
+       fstrcpy(user_name, pass->pw_name );
+       DEBUG(10,("get_correct_cversion: uid %d -> user %s\n", (int)user->uid, user_name));
+
+       /* Null password is ok - we are already an authenticated user... */
+       *null_pw = '\0';
+       conn = make_connection("print$", user_name, null_pw, 0, "A:", user->vuid, &ecode);
+
+       if (conn == NULL) {
+               DEBUG(0,("get_correct_cversion: Unable to connect\n"));
+               *perr = (uint32)ecode;
+               return -1;
+       }
+
+       /* Save who we are - we are temporarily becoming the connection user. */
+       push_sec_ctx();
+
+       if (!become_user(conn, conn->vuid)) {
+               DEBUG(0,("get_correct_cversion: Can't become user %s\n", user_name ));
+               *perr = ERROR_ACCESS_DENIED;
+               pop_sec_ctx();
+               return -1;
+       }
+
+       /* Open the driver file (Portable Executable format) and determine the
+        * deriver the cversion. */
+       slprintf(driverpath, sizeof(driverpath), "%s/%s", architecture, driverpath_in);
+
+       unix_convert(driverpath,conn,NULL,&bad_path,&st);
+
+       fsp = open_file_shared(conn, driverpath, &st,
+                                                  SET_OPEN_MODE(DOS_OPEN_RDONLY),
+                                                  (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+                                                  0, 0, &access_mode, &action);
+       if (!fsp) {
+               DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = %d\n",
+                               driverpath, errno));
+               *perr = ERROR_ACCESS_DENIED;
+               goto error_exit;
+       }
+
+       if ((byte_count = vfs_read_data(fsp, buf, DOS_HEADER_SIZE)) < DOS_HEADER_SIZE) {
+               DEBUG(3,("get_correct_cversion: File [%s] DOS header too short, bytes read = %d\n",
+                               driverpath, byte_count));
+               *perr = NT_STATUS_FILE_INVALID;
+               goto error_exit;
+       }
+
+       /* Is this really a DOS header? */
+       if (SVAL(buf,DOS_HEADER_MAGIC_OFFSET) != DOS_HEADER_MAGIC) {
+               DEBUG(6,("get_correct_cversion: File [%s] bad DOS magic = 0x%x\n",
+                               driverpath, SVAL(buf,DOS_HEADER_MAGIC_OFFSET)));
+               *perr = NT_STATUS_FILE_INVALID;
+               goto error_exit;
+       }
+
+       /* Skip OEM header (if any) and the DOS stub to start of Windows header */
+       if (fsp->conn->vfs_ops.lseek(fsp, fsp->fd, SVAL(buf,DOS_HEADER_LFANEW_OFFSET), SEEK_SET) == (SMB_OFF_T)-1) {
+               DEBUG(3,("get_correct_cversion: File [%s] too short, errno = %d\n",
+                               driverpath, errno));
+               *perr = NT_STATUS_FILE_INVALID;
+               goto error_exit;
+       }
+
+       if ((byte_count = vfs_read_data(fsp, buf, PE_HEADER_SIZE)) < PE_HEADER_SIZE) {
+               DEBUG(3,("get_correct_cversion: File [%s] Windows header too short, bytes read = %d\n",
+                               driverpath, byte_count));
+               *perr = NT_STATUS_FILE_INVALID;
+               goto error_exit;
+       }
+
+       /* The header may be a PE (Portable Executable) or an NE (New Executable) */
+       if (IVAL(buf,PE_HEADER_SIGNATURE_OFFSET) == PE_HEADER_SIGNATURE) {
+               if (SVAL(buf,PE_HEADER_MACHINE_OFFSET) == PE_HEADER_MACHINE_I386) {
+
+                       switch (SVAL(buf,PE_HEADER_MAJOR_OS_VER_OFFSET)) {
+                               case 4: cversion = 2; break;    /* Win NT 4 */
+                               case 5: cversion = 3; break;    /* Win 2000 */
+                               default:
+                                       DEBUG(6,("get_correct_cversion: PE formated file [%s] bad version = %d\n",
+                                                       driverpath, SVAL(buf,PE_HEADER_MAJOR_OS_VER_OFFSET)));
+                                       *perr = NT_STATUS_FILE_INVALID;
+                                       goto error_exit;
+                       }
+               } else {
+                       DEBUG(6,("get_correct_cversion: PE formatted file [%s] wrong machine = 0x%x\n",
+                                       driverpath, SVAL(buf,PE_HEADER_MACHINE_OFFSET)));
+                       *perr = NT_STATUS_FILE_INVALID;
+                       goto error_exit;
+               }
+
+       } else if (SVAL(buf,NE_HEADER_SIGNATURE_OFFSET) == NE_HEADER_SIGNATURE) {
+               if (CVAL(buf,NE_HEADER_TARGET_OS_OFFSET) == NE_HEADER_TARGOS_WIN ) {
+
+                       switch (CVAL(buf,NE_HEADER_MAJOR_VER_OFFSET)) {
+                               case 3: cversion = 0; break;    /* Win 3.x / Win 9x / Win ME */
+                       /*      case ?: cversion = 1; break;*/  /* Win NT 3.51 ... needs research JRR */
+                               default:
+                                       DEBUG(6,("get_correct_cversion: NE formated file [%s] bad version = %d\n",
+                                                       driverpath, CVAL(buf,NE_HEADER_MAJOR_VER_OFFSET)));
+                                       *perr = NT_STATUS_FILE_INVALID;
+                                       goto error_exit;
+                       }
+               } else {
+                       DEBUG(6,("get_correct_cversion: NE formatted file [%s] wrong target OS = 0x%x\n",
+                                       driverpath, CVAL(buf,NE_HEADER_TARGET_OS_OFFSET)));
+                       *perr = NT_STATUS_FILE_INVALID;
+                       goto error_exit;
+               }
+
+       } else {
+               DEBUG(6,("get_correct_cversion: Unknown file format [%s], signature = 0x%x\n",
+                               driverpath, IVAL(buf,PE_HEADER_SIGNATURE_OFFSET)));
+               *perr = NT_STATUS_FILE_INVALID;
+               goto error_exit;
+       }
+
+       DEBUG(10,("get_correct_cversion: Driver file [%s] cversion = %d\n",
+                       driverpath, cversion));
+
+       fsp->conn->vfs_ops.close(fsp, fsp->fd);
+       file_free(fsp);
+       close_cnum(conn, user->vuid);
+       pop_sec_ctx();
+       return cversion;
+
+
+       error_exit:
+               if(fsp) {
+                       if(fsp->fd != -1)
+                               fsp->conn->vfs_ops.close(fsp, fsp->fd);
+                       file_free(fsp);
+               }
+
+               close_cnum(conn, user->vuid);
+               pop_sec_ctx();
+               return -1;
+}
+
+/****************************************************************************
+****************************************************************************/
+static uint32 clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver,
+                                                                                        struct current_user *user)
 {
-       int fd;
-       pstring file;
        fstring architecture;
-       fstring driver_name;
-       char **dependentfiles;
+       fstring new_name;
+       char *p;
+       int i;
+       uint32 err;
 
-       /* create a file in the dir lp_nt_driver_file */
-       /* with the full printer DRIVER name */
-       /* eg: "/usr/local/samba/lib/NTdriver_HP LaserJet 6MP" */
-       /* each name is really defining an *unique* printer model */
-       /* I don't want to mangle the name to find it back when enumerating */
+       /* clean up the driver name.
+        * we can get .\driver.dll
+        * or worse c:\windows\system\driver.dll !
+        */
+       /* using an intermediate string to not have overlaping memcpy()'s */
+       if ((p = strrchr(driver->driverpath,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->driverpath, new_name);
+       }
+
+       if ((p = strrchr(driver->datafile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->datafile, new_name);
+       }
+
+       if ((p = strrchr(driver->configfile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->configfile, new_name);
+       }
+
+       if ((p = strrchr(driver->helpfile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->helpfile, new_name);
+       }
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) {
+                               fstrcpy(new_name, p+1);
+                               fstrcpy(driver->dependentfiles[i], new_name);
+                       }
+               }
+       }
+
+       get_short_archi(architecture, driver->environment);
+       
+       /* jfm:7/16/2000 the client always sends the cversion=0.
+        * The server should check which version the driver is by reading
+        * the PE header of driver->driverpath.
+        *
+        * For Windows 95/98 the version is 0 (so the value sent is correct)
+        * For Windows NT (the architecture doesn't matter)
+        *      NT 3.1: cversion=0
+        *      NT 3.5/3.51: cversion=1
+        *      NT 4: cversion=2
+        *      NT2K: cversion=3
+        */
+       if ((driver->cversion = get_correct_cversion( architecture,
+                                                                       driver->driverpath, user, &err)) == -1)
+               return err;
+
+       return NT_STATUS_NO_PROBLEMO;
+}
+       
+/****************************************************************************
+****************************************************************************/
+static uint32 clean_up_driver_struct_level_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver,
+                                                                                        struct current_user *user)
+{
+       fstring architecture;
+       fstring new_name;
+       char *p;
+       int i;
+       uint32 err;
 
-       /* il faut substituer les / par 1 autre caractere d'abord */
-       /* dans le nom de l'imprimante par un # ???*/
+       /* clean up the driver name.
+        * we can get .\driver.dll
+        * or worse c:\windows\system\driver.dll !
+        */
+       /* using an intermediate string to not have overlaping memcpy()'s */
+       if ((p = strrchr(driver->driverpath,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->driverpath, new_name);
+       }
 
-       StrnCpy(driver_name, driver->name, sizeof(driver_name)-1);
+       if ((p = strrchr(driver->datafile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->datafile, new_name);
+       }
 
-       all_string_sub(driver_name, "/", "#", 0);
+       if ((p = strrchr(driver->configfile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->configfile, new_name);
+       }
+
+       if ((p = strrchr(driver->helpfile,'\\')) != NULL) {
+               fstrcpy(new_name, p+1);
+               fstrcpy(driver->helpfile, new_name);
+       }
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       if ((p = strrchr(driver->dependentfiles[i],'\\')) != NULL) {
+                               fstrcpy(new_name, p+1);
+                               fstrcpy(driver->dependentfiles[i], new_name);
+                       }
+               }
+       }
 
        get_short_archi(architecture, driver->environment);
+
+       /* jfm:7/16/2000 the client always sends the cversion=0.
+        * The server should check which version the driver is by reading
+        * the PE header of driver->driverpath.
+        *
+        * For Windows 95/98 the version is 0 (so the value sent is correct)
+        * For Windows NT (the architecture doesn't matter)
+        *      NT 3.1: cversion=0
+        *      NT 3.5/3.51: cversion=1
+        *      NT 4: cversion=2
+        *      NT2K: cversion=3
+        */
+       if ((driver->version = get_correct_cversion(architecture,
+                                                                       driver->driverpath, user, &err)) == -1)
+               return err;
+
+       return NT_STATUS_NO_PROBLEMO;
+}
+
+/****************************************************************************
+****************************************************************************/
+uint32 clean_up_driver_struct(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
+                                                         uint32 level, struct current_user *user)
+{
+       switch (level) {
+               case 3:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
+                       driver=driver_abstract.info_3;
+                       return clean_up_driver_struct_level_3(driver, user);
+               }
+               case 6:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver;
+                       driver=driver_abstract.info_6;
+                       return clean_up_driver_struct_level_6(driver, user);
+               }
+               default:
+                       return ERROR_INVALID_PARAMETER;
+       }
+}
+
+/****************************************************************************
+ This function sucks and should be replaced. JRA.
+****************************************************************************/
+
+static void convert_level_6_to_level3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *dst, NT_PRINTER_DRIVER_INFO_LEVEL_6 *src)
+{
+    dst->cversion  = src->version;
+
+    fstrcpy( dst->name, src->name);
+    fstrcpy( dst->environment, src->environment);
+    fstrcpy( dst->driverpath, src->driverpath);
+    fstrcpy( dst->datafile, src->datafile);
+    fstrcpy( dst->configfile, src->configfile);
+    fstrcpy( dst->helpfile, src->helpfile);
+    fstrcpy( dst->monitorname, src->monitorname);
+    fstrcpy( dst->defaultdatatype, src->defaultdatatype);
+    dst->dependentfiles = src->dependentfiles;
+}
+
+#if 0 /* Debugging function */
+
+static char* ffmt(unsigned char *c){
+       int i;
+       static char ffmt_str[17];
+
+       for (i=0; i<16; i++) {
+               if ((c[i] < ' ') || (c[i] > '~'))
+                       ffmt_str[i]='.';
+               else
+                       ffmt_str[i]=c[i];
+       }
+    ffmt_str[16]='\0';
+       return ffmt_str;
+}
+
+#endif
+
+/****************************************************************************
+Version information in Microsoft files is held in a VS_VERSION_INFO structure.
+There are two case to be covered here: PE (Portable Executable) and NE (New
+Executable) files. Both files support the same INFO structure, but PE files
+store the signature in unicode, and NE files store it as !unicode.
+****************************************************************************/
+static BOOL get_file_version(files_struct *fsp, char *fname,uint32 *major,
+                                                        uint32 *minor)
+{
+       int     i;
+       char    *buf;
+       ssize_t byte_count;
+
+       if ((buf=malloc(PE_HEADER_SIZE)) == NULL) {
+               DEBUG(0,("get_file_version: PE file [%s] PE Header malloc failed bytes = %d\n",
+                               fname, PE_HEADER_SIZE));
+               goto error_exit;
+       }
+
+       /* Note: DOS_HEADER_SIZE < malloc'ed PE_HEADER_SIZE */
+       if ((byte_count = vfs_read_data(fsp, buf, DOS_HEADER_SIZE)) < DOS_HEADER_SIZE) {
+               DEBUG(3,("get_file_version: File [%s] DOS header too short, bytes read = %d\n",
+                               fname, byte_count));
+               goto no_version_info;
+       }
+
+       /* Is this really a DOS header? */
+       if (SVAL(buf,DOS_HEADER_MAGIC_OFFSET) != DOS_HEADER_MAGIC) {
+               DEBUG(6,("get_file_version: File [%s] bad DOS magic = 0x%x\n",
+                               fname, SVAL(buf,DOS_HEADER_MAGIC_OFFSET)));
+               goto no_version_info;
+       }
+
+       /* Skip OEM header (if any) and the DOS stub to start of Windows header */
+       if (fsp->conn->vfs_ops.lseek(fsp, fsp->fd, SVAL(buf,DOS_HEADER_LFANEW_OFFSET), SEEK_SET) == (SMB_OFF_T)-1) {
+               DEBUG(3,("get_file_version: File [%s] too short, errno = %d\n",
+                               fname, errno));
+               /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+               goto no_version_info;
+       }
+
+       if ((byte_count = vfs_read_data(fsp, buf, PE_HEADER_SIZE)) < PE_HEADER_SIZE) {
+               DEBUG(3,("get_file_version: File [%s] Windows header too short, bytes read = %d\n",
+                               fname, byte_count));
+               /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+               goto no_version_info;
+       }
+
+       /* The header may be a PE (Portable Executable) or an NE (New Executable) */
+       if (IVAL(buf,PE_HEADER_SIGNATURE_OFFSET) == PE_HEADER_SIGNATURE) {
+               int num_sections;
+               int section_table_bytes;
                
-       slprintf(file, sizeof(file)-1, "%s/NTdriver_%s_%s",
-                lp_nt_drivers_file(), architecture, driver_name);
+               if (SVAL(buf,PE_HEADER_MACHINE_OFFSET) != PE_HEADER_MACHINE_I386) {
+                       DEBUG(3,("get_file_version: PE file [%s] wrong machine = 0x%x\n",
+                                       fname, SVAL(buf,PE_HEADER_MACHINE_OFFSET)));
+                       /* At this point, we assume the file is in error. It still could be somthing
+                        * else besides a PE file, but it unlikely at this point.
+                        */
+                       goto error_exit;
+               }
 
-       unlink(file);
-       if((fd = sys_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) == -1)
-       {
-               DEBUG(0, ("add_a_printer_driver_3: Cannot create driver file [%s]. Error was %s\n", file, strerror(errno) ));
-               return(2);
+               /* get the section table */
+               num_sections        = SVAL(buf,PE_HEADER_NUMBER_OF_SECTIONS);
+               section_table_bytes = num_sections * PE_HEADER_SECT_HEADER_SIZE;
+               free(buf);
+               if ((buf=malloc(section_table_bytes)) == NULL) {
+                       DEBUG(0,("get_file_version: PE file [%s] section table malloc failed bytes = %d\n",
+                                       fname, section_table_bytes));
+                       goto error_exit;
+               }
+
+               if ((byte_count = vfs_read_data(fsp, buf, section_table_bytes)) < section_table_bytes) {
+                       DEBUG(3,("get_file_version: PE file [%s] Section header too short, bytes read = %d\n",
+                                       fname, byte_count));
+                       goto error_exit;
+               }
+
+               /* Iterate the section table looking for the resource section ".rsrc" */
+               for (i = 0; i < num_sections; i++) {
+                       int sec_offset = i * PE_HEADER_SECT_HEADER_SIZE;
+
+                       if (strcmp(".rsrc", &buf[sec_offset+PE_HEADER_SECT_NAME_OFFSET]) == 0) {
+                               int section_pos   = IVAL(buf,sec_offset+PE_HEADER_SECT_PTR_DATA_OFFSET);
+                               int section_bytes = IVAL(buf,sec_offset+PE_HEADER_SECT_SIZE_DATA_OFFSET);
+
+                               free(buf);
+                               if ((buf=malloc(section_bytes)) == NULL) {
+                                       DEBUG(0,("get_file_version: PE file [%s] version malloc failed bytes = %d\n",
+                                                       fname, section_bytes));
+                                       goto error_exit;
+                               }
+
+                               /* Seek to the start of the .rsrc section info */
+                               if (fsp->conn->vfs_ops.lseek(fsp, fsp->fd, section_pos, SEEK_SET) == (SMB_OFF_T)-1) {
+                                       DEBUG(3,("get_file_version: PE file [%s] too short for section info, errno = %d\n",
+                                                       fname, errno));
+                                       goto error_exit;
+                               }
+
+                               if ((byte_count = vfs_read_data(fsp, buf, section_bytes)) < section_bytes) {
+                                       DEBUG(3,("get_file_version: PE file [%s] .rsrc section too short, bytes read = %d\n",
+                                                       fname, byte_count));
+                                       goto error_exit;
+                               }
+
+                               for (i=0; i<section_bytes-VS_VERSION_INFO_UNICODE_SIZE; i++) {
+                                       /* Scan for 1st 3 unicoded bytes followed by word aligned magic value */
+                                       if (buf[i] == 'V' && buf[i+1] == '\0' && buf[i+2] == 'S') {
+                                               /* Align to next long address */
+                                               int pos = (i + sizeof(VS_SIGNATURE)*2 + 3) & 0xfffffffc;
+
+                                               if (IVAL(buf,pos) == VS_MAGIC_VALUE) {
+                                                       *major = IVAL(buf,pos+VS_MAJOR_OFFSET);
+                                                       *minor = IVAL(buf,pos+VS_MINOR_OFFSET);
+                                                       
+                                                       DEBUG(6,("get_file_version: PE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n",
+                                                                         fname, *major, *minor,
+                                                                         (*major>>16)&0xffff, *major&0xffff,
+                                                                         (*minor>>16)&0xffff, *minor&0xffff));
+                                                       free(buf);
+                                                       return True;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               /* Version info not found, fall back to origin date/time */
+               DEBUG(10,("get_file_version: PE file [%s] has no version info\n", fname));
+               free(buf);
+               return False;
+
+       } else if (SVAL(buf,NE_HEADER_SIGNATURE_OFFSET) == NE_HEADER_SIGNATURE) {
+               if (CVAL(buf,NE_HEADER_TARGET_OS_OFFSET) != NE_HEADER_TARGOS_WIN ) {
+                       DEBUG(3,("get_file_version: NE file [%s] wrong target OS = 0x%x\n",
+                                       fname, CVAL(buf,NE_HEADER_TARGET_OS_OFFSET)));
+                       /* At this point, we assume the file is in error. It still could be somthing
+                        * else besides a NE file, but it unlikely at this point. */
+                       goto error_exit;
+               }
+
+               /* Allocate a bit more space to speed up things */
+               free(buf);
+               if ((buf=malloc(VS_NE_BUF_SIZE)) == NULL) {
+                       DEBUG(0,("get_file_version: NE file [%s] malloc failed bytes  = %d\n",
+                                       fname, PE_HEADER_SIZE));
+                       goto error_exit;
+               }
+
+               /* This is a HACK! I got tired of trying to sort through the messy
+                * 'NE' file format. If anyone wants to clean this up please have at
+                * it, but this works. 'NE' files will eventually fade away. JRR */
+               while((byte_count = vfs_read_data(fsp, buf, VS_NE_BUF_SIZE)) > 0) {
+                       /* Cover case that should not occur in a well formed 'NE' .dll file */
+                       if (byte_count-VS_VERSION_INFO_SIZE <= 0) break;
+
+                       for(i=0; i<byte_count; i++) {
+                               /* Fast skip past data that can't possibly match */
+                               if (buf[i] != 'V') continue;
+
+                               /* Potential match data crosses buf boundry, move it to beginning
+                                * of buf, and fill the buf with as much as it will hold. */
+                               if (i>byte_count-VS_VERSION_INFO_SIZE) {
+                                       int bc;
+
+                                       memcpy(buf, &buf[i], byte_count-i);
+                                       if ((bc = vfs_read_data(fsp, &buf[byte_count-i], VS_NE_BUF_SIZE-
+                                                                  (byte_count-i))) < 0) {
+
+                                               DEBUG(0,("get_file_version: NE file [%s] Read error, errno=%d\n",
+                                                                fname, errno));
+                                               goto error_exit;
+                                       }
+
+                                       byte_count = bc + (byte_count - i);
+                                       if (byte_count<VS_VERSION_INFO_SIZE) break;
+
+                                       i = 0;
+                               }
+
+                               /* Check that the full signature string and the magic number that
+                                * follows exist (not a perfect solution, but the chances that this
+                                * occurs in code is, well, remote. Yes I know I'm comparing the 'V'
+                                * twice, as it is simpler to read the code. */
+                               if (strcmp(&buf[i], VS_SIGNATURE) == 0) {
+                                       /* Compute skip alignment to next long address */
+                                       int skip = -(fsp->conn->vfs_ops.lseek(fsp, fsp->fd, 0, SEEK_CUR) - (byte_count - i) +
+                                                                sizeof(VS_SIGNATURE)) & 3;
+                                       if (IVAL(buf,i+sizeof(VS_SIGNATURE)+skip) != 0xfeef04bd) continue;
+
+                                       *major = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MAJOR_OFFSET);
+                                       *minor = IVAL(buf,i+sizeof(VS_SIGNATURE)+skip+VS_MINOR_OFFSET);
+                                       DEBUG(6,("get_file_version: NE file [%s] Version = %08x:%08x (%d.%d.%d.%d)\n",
+                                                         fname, *major, *minor,
+                                                         (*major>>16)&0xffff, *major&0xffff,
+                                                         (*minor>>16)&0xffff, *minor&0xffff));
+                                       free(buf);
+                                       return True;
+                               }
+                       }
+               }
+
+               /* Version info not found, fall back to origin date/time */
+               DEBUG(0,("get_file_version: NE file [%s] Version info not found\n", fname));
+               free(buf);
+               return False;
+
+       } else
+               /* Assume this isn't an error... the file just looks sort of like a PE/NE file */
+               DEBUG(3,("get_file_version: File [%s] unknown file format, signature = 0x%x\n",
+                               fname, IVAL(buf,PE_HEADER_SIGNATURE_OFFSET)));
+
+       no_version_info:
+               free(buf);
+               return False;
+
+       error_exit:
+               free(buf);
+               return -1;
+}
+
+/****************************************************************************
+Drivers for Microsoft systems contain multiple files. Often, multiple drivers
+share one or more files. During the MS installation process files are checked
+to insure that only a newer version of a shared file is installed over an
+older version. There are several possibilities for this comparison. If there
+is no previous version, the new one is newer (obviously). If either file is
+missing the version info structure, compare the creation date (on Unix use
+the modification date). Otherwise chose the numerically larger version number.
+****************************************************************************/
+static int file_version_is_newer(connection_struct *conn, fstring new_file,
+                                                               fstring old_file)
+{
+       BOOL   use_version = True;
+       pstring filepath;
+
+       uint32 new_major;
+       uint32 new_minor;
+       time_t new_create_time;
+
+       uint32 old_major;
+       uint32 old_minor;
+       time_t old_create_time;
+
+       int access_mode;
+       int action;
+       files_struct    *fsp = NULL;
+       SMB_STRUCT_STAT st;
+       SMB_STRUCT_STAT stat_buf;
+       BOOL bad_path;
+
+       ZERO_STRUCT(st);
+       ZERO_STRUCT(stat_buf);
+       new_create_time = (time_t)0;
+       old_create_time = (time_t)0;
+
+       /* Get file version info (if available) for previous file (if it exists) */
+       pstrcpy(filepath, old_file);
+
+       unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
+
+       fsp = open_file_shared(conn, filepath, &stat_buf,
+                                                  SET_OPEN_MODE(DOS_OPEN_RDONLY),
+                                                  (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+                                                  0, 0, &access_mode, &action);
+       if (!fsp) {
+               /* Old file not found, so by definition new file is in fact newer */
+               DEBUG(10,("file_version_is_newer: Can't open old file [%s], errno = %d\n",
+                               filepath, errno));
+               return True;
+
+       } else {
+               int ret = get_file_version(fsp, old_file, &old_major, &old_minor);
+               if (ret == -1) goto error_exit;
+
+               if (!ret) {
+                       DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n",
+                                        old_file));
+                       use_version = False;
+                       if (fsp->conn->vfs_ops.fstat(fsp, fsp->fd, &st) == -1) goto error_exit;
+                       old_create_time = st.st_mtime;
+                       DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", old_create_time));
+               }
+       }
+       fsp->conn->vfs_ops.close(fsp, fsp->fd);
+       file_free(fsp);
+
+
+       /* Get file version info (if available) for new file */
+       pstrcpy(filepath, new_file);
+       unix_convert(filepath,conn,NULL,&bad_path,&stat_buf);
+
+       fsp = open_file_shared(conn, filepath, &stat_buf,
+                                                  SET_OPEN_MODE(DOS_OPEN_RDONLY),
+                                                  (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),
+                                                  0, 0, &access_mode, &action);
+       if (!fsp) {
+               /* New file not found, this shouldn't occur if the caller did its job */
+               DEBUG(3,("file_version_is_newer: Can't open new file [%s], errno = %d\n",
+                               filepath, errno));
+               goto error_exit;
+
+       } else {
+               int ret = get_file_version(fsp, new_file, &new_major, &new_minor);
+               if (ret == -1) goto error_exit;
+
+               if (!ret) {
+                       DEBUG(6,("file_version_is_newer: Version info not found [%s], use mod time\n",
+                                        new_file));
+                       use_version = False;
+                       if (fsp->conn->vfs_ops.fstat(fsp, fsp->fd, &st) == -1) goto error_exit;
+                       new_create_time = st.st_mtime;
+                       DEBUGADD(6,("file_version_is_newer: mod time = %ld sec\n", new_create_time));
+               }
+       }
+       fsp->conn->vfs_ops.close(fsp, fsp->fd);
+       file_free(fsp);
+
+       if (use_version) {
+               /* Compare versions and choose the larger version number */
+               if (new_major > old_major ||
+                       (new_major == old_major && new_minor > old_minor)) {
+                       
+                       DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+                       return True;
+               }
+               else {
+                       DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+                       return False;
+               }
+
+       } else {
+               /* Compare modification time/dates and choose the newest time/date */
+               if (new_create_time > old_create_time) {
+                       DEBUG(6,("file_version_is_newer: Replacing [%s] with [%s]\n", old_file, new_file));
+                       return True;
+               }
+               else {
+                       DEBUG(6,("file_version_is_newer: Leaving [%s] unchanged\n", old_file));
+                       return False;
+               }
+       }
+
+       error_exit:
+               if(fsp) {
+                       file_free(fsp);
+                       if(fsp->fd != -1)
+                               fsp->conn->vfs_ops.close(fsp, fsp->fd);
+               }
+               return -1;
+}
+
+/****************************************************************************
+****************************************************************************/
+BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, uint32 level, struct current_user *user, uint32 *perr)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 converted_driver;
+       fstring architecture;
+       pstring new_dir;
+       pstring old_name;
+       pstring new_name;
+       fstring user_name;
+       fstring null_pw;
+       connection_struct *conn;
+       pstring inbuf;
+       pstring outbuf;
+       struct passwd *pass;
+       int ecode;
+       int ver = 0;
+       int outsize = 0;
+       int i;
+
+       *perr = 0;
+       memset(inbuf, '\0', sizeof(inbuf));
+       memset(outbuf, '\0', sizeof(outbuf));
+
+       if (level==3)
+               driver=driver_abstract.info_3;
+       else if (level==6) {
+               convert_level_6_to_level3(&converted_driver, driver_abstract.info_6);
+               driver = &converted_driver;
+       } else {
+               DEBUG(0,("move_driver_to_download_area: Unknown info level (%u)\n", (unsigned int)level ));
+               return False;
+       }
+
+       get_short_archi(architecture, driver->environment);
+
+       become_root();
+       pass = getpwuid(user->uid);
+       if(pass == NULL) {
+               DEBUG(0,("move_driver_to_download_area: Unable to get passwd entry for uid %u\n",
+                               (unsigned int)user->uid ));
+               unbecome_root();
+               return False;
+       }
+       unbecome_root();
+
+       /* connect to the print$ share under the same account as the user connected to the rpc pipe */  
+       fstrcpy(user_name, pass->pw_name );
+       DEBUG(10,("move_driver_to_download_area: uid %d -> user %s\n", (int)user->uid, user_name));
+
+       /* Null password is ok - we are already an authenticated user... */
+       *null_pw = '\0';
+       conn = make_connection("print$", user_name, null_pw, 0, "A:", user->vuid, &ecode);
+
+       if (conn == NULL) {
+               DEBUG(0,("move_driver_to_download_area: Unable to connect\n"));
+               *perr = (uint32)ecode;
+               return False;
        }
 
        /*
-        * cversion must be 2.
-        * when adding a printer ON the SERVER
-        * rpcAddPrinterDriver defines it to zero
-        * which is wrong !!!
+        * Save who we are - we are temporarily becoming the connection user.
+        */
+
+       push_sec_ctx();
+
+       if (!become_user(conn, conn->vuid)) {
+               DEBUG(0,("move_driver_to_download_area: Can't become user %s\n", user_name ));
+               pop_sec_ctx();
+               return False;
+       }
+
+       /*
+        * make the directories version and version\driver_name
+        * under the architecture directory.
+        */
+       DEBUG(5,("Creating first directory\n"));
+       slprintf(new_dir, sizeof(new_dir), "%s/%d", architecture, driver->cversion);
+       mkdir_internal(conn, inbuf, outbuf, new_dir);
+
+       /* For each driver file, archi\filexxx.yyy, if there is a duplicate file
+        * listed for this driver which has already been moved, skip it (note:
+        * drivers may list the same file name several times. Then check if the
+        * file already exists in archi\cversion\, if so, check that the version
+        * info (or time stamps if version info is unavailable) is newer (or the
+        * date is later). If it is, move it to archi\cversion\filexxx.yyy.
+        * Otherwise, delete the file.
         *
-        * JFM, 4/14/99
+        * If a file is not moved to archi\cversion\ because of an error, all the
+        * rest of the 'unmoved' driver files are removed from archi\. If one or
+        * more of the driver's files was already moved to archi\cversion\, it
+        * potentially leaves the driver in a partially updated state. Version
+        * trauma will most likely occur if an client attempts to use any printer
+        * bound to the driver. Perhaps a rewrite to make sure the moves can be
+        * done is appropriate... later JRR
         */
-       driver->cversion=2;
-       
-       fdprintf(fd, "version:         %d\n", driver->cversion);
-       fdprintf(fd, "name:            %s\n", driver->name);
-       fdprintf(fd, "environment:     %s\n", driver->environment);
-       fdprintf(fd, "driverpath:      %s\n", driver->driverpath);
-       fdprintf(fd, "datafile:        %s\n", driver->datafile);
-       fdprintf(fd, "configfile:      %s\n", driver->configfile);
-       fdprintf(fd, "helpfile:        %s\n", driver->helpfile);
-       fdprintf(fd, "monitorname:     %s\n", driver->monitorname);
-       fdprintf(fd, "defaultdatatype: %s\n", driver->defaultdatatype);
-
-       /* and the dependants files */
-       
-       dependentfiles=driver->dependentfiles;
-       
-       while ( **dependentfiles != '\0' )
-       {
-               fdprintf(fd, "dependentfile:   %s\n", *dependentfiles);
-               dependentfiles++;
+
+       DEBUG(5,("Moving files now !\n"));
+
+       if (driver->driverpath && strlen(driver->driverpath)) {
+               slprintf(new_name, sizeof(new_name), "%s/%s", architecture, driver->driverpath);        
+               slprintf(old_name, sizeof(old_name), "%s/%s", new_dir, driver->driverpath);     
+               if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                       if ((outsize = rename_internals(conn, inbuf, outbuf, new_name, old_name, True)) != 0) {
+                               DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                               new_name, old_name));
+                               *perr = (uint32)SVAL(outbuf,smb_err);
+                               unlink_internals(conn, inbuf, outbuf, 0, new_name);
+                               ver = -1;
+                       }
+               }
+               else
+                       unlink_internals(conn, inbuf, outbuf, 0, new_name);
        }
+
+       if (driver->datafile && strlen(driver->datafile)) {
+               if (!strequal(driver->datafile, driver->driverpath)) {
+                       slprintf(new_name, sizeof(new_name), "%s/%s", architecture, driver->datafile);  
+                       slprintf(old_name, sizeof(old_name), "%s/%s", new_dir, driver->datafile);       
+                       if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                               if ((outsize = rename_internals(conn, inbuf, outbuf, new_name, old_name, True)) != 0) {
+                                       DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                                       new_name, old_name));
+                                       *perr = (uint32)SVAL(outbuf,smb_err);
+                                       unlink_internals(conn, inbuf, outbuf, 0, new_name);
+                                       ver = -1;
+                               }
+                       }
+                       else
+                               unlink_internals(conn, inbuf, outbuf, 0, new_name);
+               }
+       }
+
+       if (driver->configfile && strlen(driver->configfile)) {
+               if (!strequal(driver->configfile, driver->driverpath) &&
+                       !strequal(driver->configfile, driver->datafile)) {
+                       slprintf(new_name, sizeof(new_name), "%s/%s", architecture, driver->configfile);        
+                       slprintf(old_name, sizeof(old_name), "%s/%s", new_dir, driver->configfile);     
+                       if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                               if ((outsize = rename_internals(conn, inbuf, outbuf, new_name, old_name, True)) != 0) {
+                                       DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                                       new_name, old_name));
+                                       *perr = (uint32)SVAL(outbuf,smb_err);
+                                       unlink_internals(conn, inbuf, outbuf, 0, new_name);
+                                       ver = -1;
+                               }
+                       }
+                       else
+                               unlink_internals(conn, inbuf, outbuf, 0, new_name);
+               }
+       }
+
+       if (driver->helpfile && strlen(driver->helpfile)) {
+               if (!strequal(driver->helpfile, driver->driverpath) &&
+                       !strequal(driver->helpfile, driver->datafile) &&
+                       !strequal(driver->helpfile, driver->configfile)) {
+                       slprintf(new_name, sizeof(new_name), "%s/%s", architecture, driver->helpfile);  
+                       slprintf(old_name, sizeof(old_name), "%s/%s", new_dir, driver->helpfile);       
+                       if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                               if ((outsize = rename_internals(conn, inbuf, outbuf, new_name, old_name, True)) != 0) {
+                                       DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                                       new_name, old_name));
+                                       *perr = (uint32)SVAL(outbuf,smb_err);
+                                       unlink_internals(conn, inbuf, outbuf, 0, new_name);
+                                       ver = -1;
+                               }
+                       }
+                       else
+                               unlink_internals(conn, inbuf, outbuf, 0, new_name);
+               }
+       }
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       if (!strequal(driver->dependentfiles[i], driver->driverpath) &&
+                               !strequal(driver->dependentfiles[i], driver->datafile) &&
+                               !strequal(driver->dependentfiles[i], driver->configfile) &&
+                               !strequal(driver->dependentfiles[i], driver->helpfile)) {
+                               int j;
+                               for (j=0; j < i; j++) {
+                                       if (strequal(driver->dependentfiles[i], driver->dependentfiles[j])) {
+                                               goto NextDriver;
+                                       }
+                               }
+
+                               slprintf(new_name, sizeof(new_name), "%s/%s", architecture, driver->dependentfiles[i]); 
+                               slprintf(old_name, sizeof(old_name), "%s/%s", new_dir, driver->dependentfiles[i]);      
+                               if (ver != -1 && (ver=file_version_is_newer(conn, new_name, old_name)) > 0) {
+                                       if ((outsize = rename_internals(conn, inbuf, outbuf, new_name, old_name, True)) != 0) {
+                                               DEBUG(0,("move_driver_to_download_area: Unable to rename [%s] to [%s]\n",
+                                                               new_name, old_name));
+                                               *perr = (uint32)SVAL(outbuf,smb_err);
+                                               unlink_internals(conn, inbuf, outbuf, 0, new_name);
+                                               ver = -1;
+                                       }
+                               }
+                               else
+                                       unlink_internals(conn, inbuf, outbuf, 0, new_name);
+                       }
+               NextDriver: ;
+               }
+       }
+
+       close_cnum(conn, user->vuid);
+       pop_sec_ctx();
+
+       return ver == -1 ? False : True;
+}
+
+/****************************************************************************
+****************************************************************************/
+static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
+{
+       int len, buflen;
+       fstring architecture;
+       pstring directory;
+       pstring temp_name;
+       pstring key;
+       char *buf;
+       int i, ret;
+       TDB_DATA kbuf, dbuf;
+
+       get_short_archi(architecture, driver->environment);
+
+       /* The names are relative. We store them in the form: \print$\arch\version\driver.xxx
+        * \\server is added in the rpc server layer.
+        * It does make sense to NOT store the server's name in the printer TDB.
+        */
+
+       slprintf(directory, sizeof(directory), "\\print$\\%s\\%d\\", architecture, driver->cversion);
+
+    /* .inf files do not always list a file for each of the four standard files. 
+     * Don't prepend a path to a null filename, or client claims:
+     *   "The server on which the printer resides does not have a suitable 
+     *   <printer driver name> printer driver installed. Click OK if you 
+     *   wish to install the driver on your local machine."
+     */
+       if (strlen(driver->driverpath)) {
+       fstrcpy(temp_name, driver->driverpath);
+       slprintf(driver->driverpath, sizeof(driver->driverpath), "%s%s", directory, temp_name);
+    }
+
+       if (strlen(driver->datafile)) {
+       fstrcpy(temp_name, driver->datafile);
+       slprintf(driver->datafile, sizeof(driver->datafile), "%s%s", directory, temp_name);
+    }
+
+       if (strlen(driver->configfile)) {
+       fstrcpy(temp_name, driver->configfile);
+       slprintf(driver->configfile, sizeof(driver->configfile), "%s%s", directory, temp_name);
+    }
+
+       if (strlen(driver->helpfile)) {
+       fstrcpy(temp_name, driver->helpfile);
+       slprintf(driver->helpfile, sizeof(driver->helpfile), "%s%s", directory, temp_name);
+    }
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+            fstrcpy(temp_name, driver->dependentfiles[i]);
+            slprintf(driver->dependentfiles[i], sizeof(driver->dependentfiles[i]), "%s%s", directory, temp_name);
+               }
+       }
+
+       slprintf(key, sizeof(key), "%s%s/%d/%s", DRIVERS_PREFIX, architecture, driver->cversion, driver->name);
+       dos_to_unix(key, True);                /* Convert key to unix-codepage */
+
+       DEBUG(5,("add_a_printer_driver_3: Adding driver with key %s\n", key ));
+
+       buf = NULL;
+       len = buflen = 0;
+
+ again:
+       len = 0;
+       len += tdb_pack(buf+len, buflen-len, "dffffffff",
+                       driver->cversion,
+                       driver->name,
+                       driver->environment,
+                       driver->driverpath,
+                       driver->datafile,
+                       driver->configfile,
+                       driver->helpfile,
+                       driver->monitorname,
+                       driver->defaultdatatype);
+
+       if (driver->dependentfiles) {
+               for (i=0; *driver->dependentfiles[i]; i++) {
+                       len += tdb_pack(buf+len, buflen-len, "f",
+                                       driver->dependentfiles[i]);
+               }
+       }
+
+       if (len != buflen) {
+               buf = (char *)Realloc(buf, len);
+               buflen = len;
+               goto again;
+       }
+
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+       dbuf.dptr = buf;
+       dbuf.dsize = len;
        
-       close(fd);      
-       return(0);
+       ret = tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
+
+       if (ret)
+               DEBUG(0,("add_a_printer_driver_3: Adding driver with key %s failed.\n", key ));
+
+       safe_free(buf);
+       return ret;
 }
 
 /****************************************************************************
 ****************************************************************************/
 static uint32 add_a_printer_driver_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver)
 {
-       int fd;
-       pstring file;
-       fstring architecture;
-       fstring driver_name;
-       char **dependentfiles;
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 info3;
+
+       ZERO_STRUCT(info3);
+       info3.cversion = driver->version;
+       fstrcpy(info3.name,driver->name);
+       fstrcpy(info3.environment,driver->environment);
+       fstrcpy(info3.driverpath,driver->driverpath);
+       fstrcpy(info3.datafile,driver->datafile);
+       fstrcpy(info3.configfile,driver->configfile);
+       fstrcpy(info3.helpfile,driver->helpfile);
+       fstrcpy(info3.monitorname,driver->monitorname);
+       fstrcpy(info3.defaultdatatype,driver->defaultdatatype);
+       info3.dependentfiles = driver->dependentfiles;
+
+       return add_a_printer_driver_3(&info3);
+}
 
-       /* create a file in the dir lp_nt_driver_file */
-       /* with the full printer DRIVER name */
-       /* eg: "/usr/local/samba/lib/NTdriver_HP LaserJet 6MP" */
-       /* each name is really defining an *unique* printer model */
-       /* I don't want to mangle the name to find it back when enumerating */
 
-       /* il faut substituer les / par 1 autre caractere d'abord */
-       /* dans le nom de l'imprimante par un # ???*/
+/****************************************************************************
+****************************************************************************/
+static uint32 get_a_printer_driver_3_default(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring in_prt, fstring in_arch)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 info;
 
-       StrnCpy(driver_name, driver->name, sizeof(driver_name)-1);
+       ZERO_STRUCT(info);
 
-       all_string_sub(driver_name, "/", "#", 0);
+       fstrcpy(info.name, in_prt);
+       fstrcpy(info.defaultdatatype, "RAW");
+       
+       fstrcpy(info.driverpath, "");
+       fstrcpy(info.datafile, "");
+       fstrcpy(info.configfile, "");
+       fstrcpy(info.helpfile, "");
 
-       get_short_archi(architecture, driver->environment);
-               
-       slprintf(file, sizeof(file)-1, "%s/NTdriver_%s_%s",
-                lp_nt_drivers_file(), architecture, driver_name);
+       if ((info.dependentfiles=(fstring *)malloc(2*sizeof(fstring))) == NULL)
+               return ERROR_NOT_ENOUGH_MEMORY;
 
-       unlink(file);
-       if((fd = sys_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) == -1)
-       {
-               DEBUG(0, ("add_a_printer_driver_3: Cannot create driver file [%s]. Error was %s\n", file, strerror(errno) ));
-               return(2);
-       }
+       memset(info.dependentfiles, '\0', 2*sizeof(fstring));
+       fstrcpy(info.dependentfiles[0], "");
 
-       /*
-        * cversion must be 2.
-        * when adding a printer ON the SERVER
-        * rpcAddPrinterDriver defines it to zero
-        * which is wrong !!!
-        *
-        * JFM, 4/14/99
-        */
-       driver->version=2;
-       
-       fdprintf(fd, "version:         %d\n", driver->version);
-       fdprintf(fd, "name:            %s\n", driver->name);
-       fdprintf(fd, "environment:     %s\n", driver->environment);
-       fdprintf(fd, "driverpath:      %s\n", driver->driverpath);
-       fdprintf(fd, "datafile:        %s\n", driver->datafile);
-       fdprintf(fd, "configfile:      %s\n", driver->configfile);
-       fdprintf(fd, "helpfile:        %s\n", driver->helpfile);
-       fdprintf(fd, "monitorname:     %s\n", driver->monitorname);
-       fdprintf(fd, "defaultdatatype: %s\n", driver->defaultdatatype);
-
-       /* and the dependants files */
-       
-       dependentfiles=driver->dependentfiles;
+       *info_ptr = memdup(&info, sizeof(info));
        
-       while ( **dependentfiles != '\0' )
-       {
-               fdprintf(fd, "dependentfile:   %s\n", *dependentfiles);
-               dependentfiles++;
-       }
-       
-       close(fd);      
-       return(0);
+       return 0;       
 }
 
 /****************************************************************************
 ****************************************************************************/
-static uint32 get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring in_prt, fstring in_arch)
+static uint32 get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring in_prt, fstring in_arch, uint32 version)
 {
-       char **lines;
-       int lcount;
-       pstring file;
-       fstring driver_name;
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 driver;
+       TDB_DATA kbuf, dbuf;
        fstring architecture;
-       NT_PRINTER_DRIVER_INFO_LEVEL_3 *info = NULL;
-       fstring p;
-       char *v;
-       int i=0;
-       char **dependentfiles=NULL;
-       
-       /*
-        * replace all the / by # in the driver name
-        * get the short architecture name
-        * construct the driver file name
-        */
-       StrnCpy(driver_name, in_prt, sizeof(driver_name)-1);
-       all_string_sub(driver_name, "/", "#", 0);
+       int len = 0;
+       int i;
+       pstring key;
+
+       ZERO_STRUCT(driver);
 
        get_short_archi(architecture, in_arch);
-               
-       slprintf(file, sizeof(file)-1, "%s/NTdriver_%s_%s",
-                lp_nt_drivers_file(), architecture, driver_name);
 
-       lines = file_lines_load(file, NULL);
+       DEBUG(8,("get_a_printer_driver_3: [%s%s/%d/%s]\n", DRIVERS_PREFIX, architecture, version, in_prt));
+
+       slprintf(key, sizeof(key), "%s%s/%d/%s", DRIVERS_PREFIX, architecture, version, in_prt);
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+       
+       dbuf = tdb_fetch(tdb, kbuf);
+#if 0
+       if (!dbuf.dptr) return get_a_printer_driver_3_default(info_ptr, in_prt, in_arch);
+#else
+       if (!dbuf.dptr) return 5;
+#endif
+       len += tdb_unpack(dbuf.dptr, dbuf.dsize, "dffffffff",
+                         &driver.cversion,
+                         driver.name,
+                         driver.environment,
+                         driver.driverpath,
+                         driver.datafile,
+                         driver.configfile,
+                         driver.helpfile,
+                         driver.monitorname,
+                         driver.defaultdatatype);
+
+       i=0;
+       while (len < dbuf.dsize) {
+               driver.dependentfiles = (fstring *)Realloc(driver.dependentfiles,
+                                                        sizeof(fstring)*(i+2));
+               if (driver.dependentfiles == NULL)
+                       break;
 
-       if (!lines) {
-               DEBUG(2, ("get_a_printer_driver_3: Cannot open printer driver file [%s]. Error was %s\n", file, strerror(errno) ));
-               return(2);
+               len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "f",
+                                 &driver.dependentfiles[i]);
+               i++;
        }
+       if (driver.dependentfiles != NULL)
+               fstrcpy(driver.dependentfiles[i], "");
 
-       /* the file exists, allocate some memory */
-       if((info=(NT_PRINTER_DRIVER_INFO_LEVEL_3 *)malloc(sizeof(NT_PRINTER_DRIVER_INFO_LEVEL_3))) == NULL)
-               goto err;
-
-       ZERO_STRUCTP(info);
-       
-       for (lcount=0; lines[lcount]; lcount++) {
-               char *line = lines[lcount];
-               v=strncpyn(p, line, sizeof(p), ':');
-               if (v==NULL)
-               {
-                       DEBUG(1, ("malformed printer driver entry (no :)\n"));
-                       continue;
-               }
-               
-               v++;
-               
-               trim_string(v, " ", NULL);
-               trim_string(v, NULL, " ");
-               trim_string(v, NULL, "\n");
-               /* don't check if v==NULL as an empty arg is valid */
-               
-               if (!strncmp(p, "version", strlen("version")))
-                       info->cversion=atoi(v);
-
-               if (!strncmp(p, "name", strlen("name")))
-                       StrnCpy(info->name, v, strlen(v));
-
-               if (!strncmp(p, "environment", strlen("environment")))
-                       StrnCpy(info->environment, v, strlen(v));
-
-               if (!strncmp(p, "driverpath", strlen("driverpath")))
-                       StrnCpy(info->driverpath, v, strlen(v));
-
-               if (!strncmp(p, "datafile", strlen("datafile")))
-                       StrnCpy(info->datafile, v, strlen(v));
-
-               if (!strncmp(p, "configfile", strlen("configfile")))
-                       StrnCpy(info->configfile, v, strlen(v));
+       safe_free(dbuf.dptr);
 
-               if (!strncmp(p, "helpfile", strlen("helpfile")))
-                       StrnCpy(info->helpfile, v, strlen(v));
+       if (len != dbuf.dsize) {
+               if (driver.dependentfiles != NULL)
+                       safe_free(driver.dependentfiles);
 
-               if (!strncmp(p, "monitorname", strlen("monitorname")))
-                       StrnCpy(info->monitorname, v, strlen(v));
-
-               if (!strncmp(p, "defaultdatatype", strlen("defaultdatatype")))
-                       StrnCpy(info->defaultdatatype, v, strlen(v));
-
-               if (!strncmp(p, "dependentfile", strlen("dependentfile")))
-               {
-                       if((dependentfiles=(char **)Realloc(dependentfiles, sizeof(char *)*(i+1))) == NULL)
-                               goto err;
-                       
-                       if((dependentfiles[i]=(char *)malloc( sizeof(char)* (strlen(v)+1) )) == NULL)
-                               goto err;
-                       
-                       StrnCpy(dependentfiles[i], v, strlen(v) );
-                       i++;
-               }
+               return get_a_printer_driver_3_default(info_ptr, in_prt, in_arch);
        }
-       
-       file_lines_free(lines);
-       
-       dependentfiles=(char **)Realloc(dependentfiles, sizeof(char *)*(i+1));
-       dependentfiles[i]=(char *)malloc( sizeof(char) );
-       *dependentfiles[i]='\0';
-       
-       info->dependentfiles=dependentfiles;
-       
-       *info_ptr=info;
-       
-       return (0);     
 
-  err:
+       *info_ptr = (NT_PRINTER_DRIVER_INFO_LEVEL_3 *)memdup(&driver, sizeof(driver));
 
-       if (lines)
-               file_lines_free(lines);
-       if(info)
-               free(info);
-
-       if(dependentfiles) {
-               for(;i >= 0; i--)
-                       if(dependentfiles[i])
-                               free(dependentfiles[i]);
+       return 0;
+}
 
-               free(dependentfiles);
+/****************************************************************************
+****************************************************************************/
+uint32 get_a_printer_driver_9x_compatible(pstring line, fstring model)
+{
+       NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
+       TDB_DATA kbuf;
+       pstring key;
+       int i;
+       line[0] = '\0';
+
+       slprintf(key, sizeof(key), "%s%s/%d/%s", DRIVERS_PREFIX, "WIN40", 0, model);
+       DEBUG(10,("driver key: [%s]\n", key));
+       
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+       if (!tdb_exists(tdb, kbuf)) return False;
+
+       ZERO_STRUCT(info3);
+       get_a_printer_driver_3(&info3, model, "Windows 4.0", 0);
+       
+    DEBUGADD(10,("info3->name            [%s]\n", info3->name));
+    DEBUGADD(10,("info3->datafile        [%s]\n", info3->datafile));
+    DEBUGADD(10,("info3->helpfile        [%s]\n", info3->helpfile));
+    DEBUGADD(10,("info3->monitorname     [%s]\n", info3->monitorname));
+    DEBUGADD(10,("info3->defaultdatatype [%s]\n", info3->defaultdatatype));
+       for (i=0; info3->dependentfiles && *info3->dependentfiles[i]; i++) {
+    DEBUGADD(10,("info3->dependentfiles  [%s]\n", info3->dependentfiles[i]));
+    }
+    DEBUGADD(10,("info3->environment     [%s]\n", info3->environment));
+    DEBUGADD(10,("info3->driverpath      [%s]\n", info3->driverpath));
+    DEBUGADD(10,("info3->configfile      [%s]\n", info3->configfile));
+
+       /*pstrcat(line, info3->name);             pstrcat(line, ":");*/
+       trim_string(info3->configfile, "\\print$\\WIN40\\0\\", 0);
+       pstrcat(line, info3->configfile);
+    pstrcat(line, ":");
+       trim_string(info3->datafile, "\\print$\\WIN40\\0\\", 0);
+       pstrcat(line, info3->datafile);
+    pstrcat(line, ":");
+       trim_string(info3->helpfile, "\\print$\\WIN40\\0\\", 0);
+       pstrcat(line, info3->helpfile);
+    pstrcat(line, ":");
+       trim_string(info3->monitorname, "\\print$\\WIN40\\0\\", 0);
+       pstrcat(line, info3->monitorname);
+    pstrcat(line, ":");
+       pstrcat(line, "RAW");                /*info3->defaultdatatype);*/
+    pstrcat(line, ":");
+
+       for (i=0; info3->dependentfiles &&
+                *info3->dependentfiles[i]; i++) {
+               if (i) pstrcat(line, ",");               /* don't end in a "," */
+               trim_string(info3->dependentfiles[i], "\\print$\\WIN40\\0\\", 0);
+               pstrcat(line, info3->dependentfiles[i]);
        }
+       
+       free(info3);
 
-       return (2);
+       return True;    
 }
 
 /****************************************************************************
@@ -580,18 +1566,18 @@ debugging function, dump at level 6 the struct in the logs
 ****************************************************************************/
 static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
 {
-       uint32 success;
+       uint32 result;
        NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
-       char **dependentfiles;  
+       int i;
        
        DEBUG(106,("Dumping printer driver at level [%d]\n", level));
        
        switch (level)
        {
-               case 3: 
+               case 3:
                {
                        if (driver.info_3 == NULL)
-                               success=5;
+                               result=5;
                        else {
                                info3=driver.info_3;
                        
@@ -605,82 +1591,104 @@ static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32
                                DEBUGADD(106,("monitorname:[%s]\n",     info3->monitorname));
                                DEBUGADD(106,("defaultdatatype:[%s]\n", info3->defaultdatatype));
                                
-                               dependentfiles=info3->dependentfiles;
-       
-                               while ( **dependentfiles != '\0' )
-                               {
-                                       DEBUGADD(106,("dependentfile:[%s]\n", *dependentfiles));
-                                       dependentfiles++;
+                               for (i=0; info3->dependentfiles &&
+                                         *info3->dependentfiles[i]; i++) {
+                                       DEBUGADD(106,("dependentfile:[%s]\n",
+                                                     info3->dependentfiles[i]));
                                }
-                               success=0;
+                               result=0;
                        }
                        break;
                }
                default:
                        DEBUGADD(1,("Level not implemented\n"));
-                       success=1;
+                       result=1;
                        break;
        }
        
-       return (success);
+       return result;
 }
 
 /****************************************************************************
 ****************************************************************************/
-static void add_a_devicemode(NT_DEVICEMODE *nt_devmode, int fd)
+static int pack_devicemode(NT_DEVICEMODE *nt_devmode, char *buf, int buflen)
 {
-       int i;
-       
-       fdprintf(fd, "formname: %s\n",      nt_devmode->formname);
-       fdprintf(fd, "specversion: %d\n",   nt_devmode->specversion);
-       fdprintf(fd, "driverversion: %d\n", nt_devmode->driverversion);
-       fdprintf(fd, "size: %d\n",          nt_devmode->size);
-       fdprintf(fd, "driverextra: %d\n",   nt_devmode->driverextra);
-       fdprintf(fd, "fields: %d\n",        nt_devmode->fields);
-       fdprintf(fd, "orientation: %d\n",   nt_devmode->orientation);
-       fdprintf(fd, "papersize: %d\n",     nt_devmode->papersize);
-       fdprintf(fd, "paperlength: %d\n",   nt_devmode->paperlength);
-       fdprintf(fd, "paperwidth: %d\n",    nt_devmode->paperwidth);
-       fdprintf(fd, "scale: %d\n",         nt_devmode->scale);
-       fdprintf(fd, "copies: %d\n",        nt_devmode->copies);
-       fdprintf(fd, "defaultsource: %d\n", nt_devmode->defaultsource);
-       fdprintf(fd, "printquality: %d\n",  nt_devmode->printquality);
-       fdprintf(fd, "color: %d\n",         nt_devmode->color);
-       fdprintf(fd, "duplex: %d\n",        nt_devmode->duplex);
-       fdprintf(fd, "yresolution: %d\n",   nt_devmode->yresolution);
-       fdprintf(fd, "ttoption: %d\n",      nt_devmode->ttoption);
-       fdprintf(fd, "collate: %d\n",       nt_devmode->collate);
-       fdprintf(fd, "icmmethod: %d\n",     nt_devmode->icmmethod);
-       fdprintf(fd, "icmintent: %d\n",     nt_devmode->icmintent);
-       fdprintf(fd, "mediatype: %d\n",     nt_devmode->mediatype);
-       fdprintf(fd, "dithertype: %d\n",    nt_devmode->dithertype);
-       
-       if (nt_devmode->private != NULL)
-       {
-               fdprintf(fd, "private: ");              
-               for (i=0; i<nt_devmode->driverextra; i++)
-                       fdprintf(fd, "%02X", nt_devmode->private[i]);
-               fdprintf(fd, "\n");     
+       int len = 0;
+
+       len += tdb_pack(buf+len, buflen-len, "p", nt_devmode);
+
+       if (!nt_devmode) return len;
+
+       len += tdb_pack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp",
+                       nt_devmode->devicename,
+                       nt_devmode->formname,
+
+                       nt_devmode->specversion,
+                       nt_devmode->driverversion,
+                       nt_devmode->size,
+                       nt_devmode->driverextra,
+                       nt_devmode->orientation,
+                       nt_devmode->papersize,
+                       nt_devmode->paperlength,
+                       nt_devmode->paperwidth,
+                       nt_devmode->scale,
+                       nt_devmode->copies,
+                       nt_devmode->defaultsource,
+                       nt_devmode->printquality,
+                       nt_devmode->color,
+                       nt_devmode->duplex,
+                       nt_devmode->yresolution,
+                       nt_devmode->ttoption,
+                       nt_devmode->collate,
+                       nt_devmode->logpixels,
+                       
+                       nt_devmode->fields,
+                       nt_devmode->bitsperpel,
+                       nt_devmode->pelswidth,
+                       nt_devmode->pelsheight,
+                       nt_devmode->displayflags,
+                       nt_devmode->displayfrequency,
+                       nt_devmode->icmmethod,
+                       nt_devmode->icmintent,
+                       nt_devmode->mediatype,
+                       nt_devmode->dithertype,
+                       nt_devmode->reserved1,
+                       nt_devmode->reserved2,
+                       nt_devmode->panningwidth,
+                       nt_devmode->panningheight,
+                       nt_devmode->private);
+
+       
+       if (nt_devmode->private) {
+               len += tdb_pack(buf+len, buflen-len, "B",
+                               nt_devmode->driverextra,
+                               nt_devmode->private);
        }
+
+       DEBUG(8,("Packed devicemode [%s]\n", nt_devmode->formname));
+
+       return len;
 }
 
 /****************************************************************************
 ****************************************************************************/
-static void save_specifics(NT_PRINTER_PARAM *param, int fd)
+static int pack_specifics(NT_PRINTER_PARAM *param, char *buf, int buflen)
 {
-       int i;
-       
-       while (param != NULL)
-       {
-               fdprintf(fd, "specific: %s#%d#%d#", param->value, param->type, param->data_len);
-               
-               for (i=0; i<param->data_len; i++)
-                       fdprintf(fd, "%02X", param->data[i]);
-               
-               fdprintf(fd, "\n");
-       
+       int len = 0;
+
+       while (param != NULL) {
+               len += tdb_pack(buf+len, buflen-len, "pfdB",
+                               param,
+                               param->value,
+                               param->type,
+                               param->data_len,
+                               param->data);
                param=param->next;      
        }
+
+       len += tdb_pack(buf+len, buflen-len, "p", param);
+
+       return len;
 }
 
 
@@ -688,67 +1696,31 @@ static void save_specifics(NT_PRINTER_PARAM *param, int fd)
 delete a printer - this just deletes the printer info file, any open
 handles are not affected
 ****************************************************************************/
-uint32 del_a_printer(char *portname)
+uint32 del_a_printer(char *sharename)
 {
-       pstring file;
-               
-       slprintf(file, sizeof(file), "%s/NTprinter_%s",
-                lp_nt_drivers_file(), portname);
-       if (unlink(file) != 0) return 2;
+       pstring key;
+       TDB_DATA kbuf;
+
+       slprintf(key, sizeof(key), "%s%s", PRINTERS_PREFIX, sharename);
+       dos_to_unix(key, True);                /* Convert key to unix-codepage */
+
+       kbuf.dptr=key;
+       kbuf.dsize=strlen(key)+1;
+
+       tdb_delete(tdb, kbuf);
        return 0;
 }
 
 /****************************************************************************
 ****************************************************************************/
-static uint32 add_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
+static uint32 update_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
 {
-       int fd;
-       pstring file;
-       fstring printer_name;
-       NT_DEVICEMODE *nt_devmode;
+       pstring key;
+       char *buf;
+       int buflen, len, ret;
+       TDB_DATA kbuf, dbuf;
        
        /*
-        * JFM: one day I'll forget.
-        * below that's info->portname because that's the SAMBA sharename
-        * and I made NT 'thinks' it's the portname
-        * the info->sharename is the thing you can name when you add a printer
-        * that's the short-name when you create shared printer for 95/98
-        * So I've made a limitation in SAMBA: you can only have 1 printer model
-        * behind a SAMBA share.
-        */
-
-
-       StrnCpy(printer_name, info->portname, sizeof(printer_name)-1);
-               
-       slprintf(file, sizeof(file)-1, "%s/NTprinter_%s",
-                lp_nt_drivers_file(), printer_name);
-
-       /* create a file in the dir lp_nt_driver_file */
-       /* with the full printer name */
-       /* eg: "/usr/local/samba/lib/NTprinter_HP LaserJet 6MP" */
-       /* each name is really defining an *unique* printer model */
-       /* I don't want to mangle the name to find it back when enumerating */
-       
-       unlink(file);
-       if((fd = sys_open(file, O_WRONLY|O_CREAT|O_EXCL, 0644)) == -1)
-       {
-               DEBUG(0, ("add_a_printer_2: Cannot create printer file [%s]. Error was %s\n", file, strerror(errno) ));
-               return(2);
-       }
-
-       fdprintf(fd, "attributes: %d\n", info->attributes);
-       fdprintf(fd, "priority: %d\n", info->priority);
-       fdprintf(fd, "default_priority: %d\n", info->default_priority);
-       fdprintf(fd, "starttime: %d\n", info->starttime);
-       fdprintf(fd, "untiltime: %d\n", info->untiltime);
-       fdprintf(fd, "status: %d\n", info->status);
-       fdprintf(fd, "cjobs: %d\n", info->cjobs);
-       fdprintf(fd, "averageppm: %d\n", info->averageppm);
-       fdprintf(fd, "changeid: %d\n", info->changeid);
-       fdprintf(fd, "c_setprinter: %d\n", info->c_setprinter);
-       fdprintf(fd, "setuptime: %d\n", (int)info->setuptime);
-
-       /* 
         * in addprinter: no servername and the printer is the name
         * in setprinter: servername is \\server
         *                and printer is \\server\\printer
@@ -757,100 +1729,96 @@ static uint32 add_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
         * we currently don't support things like path=\\other_server\printer
         */
 
-       if (info->servername[0]!='\0')
-       {
+       if (info->servername[0]!='\0') {
                trim_string(info->printername, info->servername, NULL);
                trim_string(info->printername, "\\", NULL);
                info->servername[0]='\0';
        }
 
-       fdprintf(fd, "servername: %s\n", info->servername);
-       fdprintf(fd, "printername: %s\n", info->printername);
-       fdprintf(fd, "sharename: %s\n", info->sharename);
-       fdprintf(fd, "portname: %s\n", info->portname);
-       fdprintf(fd, "drivername: %s\n", info->drivername);
-       fdprintf(fd, "location: %s\n", info->location);
-       fdprintf(fd, "sepfile: %s\n", info->sepfile);
-       fdprintf(fd, "printprocessor: %s\n", info->printprocessor);
-       fdprintf(fd, "datatype: %s\n", info->datatype);
-       fdprintf(fd, "parameters: %s\n", info->parameters);
-
-       /* store the devmode and the private part if it exist */
-       nt_devmode=info->devmode;
-       if (nt_devmode!=NULL)
-       {
-               add_a_devicemode(nt_devmode, fd);
-       }
-       
-       /* and store the specific parameters */
-       if (info->specific != NULL)
-       {
-               save_specifics(info->specific, fd);
+       /*
+        * JFM: one day I'll forget.
+        * below that's info->portname because that's the SAMBA sharename
+        * and I made NT 'thinks' it's the portname
+        * the info->sharename is the thing you can name when you add a printer
+        * that's the short-name when you create shared printer for 95/98
+        * So I've made a limitation in SAMBA: you can only have 1 printer model
+        * behind a SAMBA share.
+        */
+
+       buf = NULL;
+       buflen = 0;
+
+ again:        
+       len = 0;
+       len += tdb_pack(buf+len, buflen-len, "dddddddddddfffffPfffff",
+                       info->attributes,
+                       info->priority,
+                       info->default_priority,
+                       info->starttime,
+                       info->untiltime,
+                       info->status,
+                       info->cjobs,
+                       info->averageppm,
+                       info->changeid,
+                       info->c_setprinter,
+                       info->setuptime,
+                       info->servername,
+                       info->printername,
+                       info->sharename,
+                       info->portname,
+                       info->drivername,
+                       info->comment,
+                       info->location,
+                       info->sepfile,
+                       info->printprocessor,
+                       info->datatype,
+                       info->parameters);
+
+       len += pack_devicemode(info->devmode, buf+len, buflen-len);
+       len += pack_specifics(info->specific, buf+len, buflen-len);
+
+       if (buflen != len) {
+               buf = (char *)Realloc(buf, len);
+               buflen = len;
+               goto again;
        }
        
-       close(fd);
-       
-       return (0);     
-}
 
-/****************************************************************************
-fill a NT_PRINTER_PARAM from a text file
+       slprintf(key, sizeof(key), "%s%s", PRINTERS_PREFIX, info->sharename);
+       dos_to_unix(key, True);                /* Convert key to unix-codepage */
 
-used when reading from disk.
-****************************************************************************/
-static BOOL dissect_and_fill_a_param(NT_PRINTER_PARAM *param, char *v)
-{
-       char *tok[5];
-       int count = 0;
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+       dbuf.dptr = buf;
+       dbuf.dsize = len;
 
-       DEBUG(105,("dissect_and_fill_a_param\n"));      
-               
-       tok[count] = strtok(v,"#");
-       count++;
-       
-       while ( ((tok[count] = strtok(NULL,"#")) != NULL ) && count<4)
-       {
-               count++;
-       }
+       ret = tdb_store(tdb, kbuf, dbuf, TDB_REPLACE);
 
-       StrnCpy(param->value, tok[0], sizeof(param->value)-1);
-       param->type=atoi(tok[1]);
-       param->data_len=atoi(tok[2]);
-       if((param->data=(uint8 *)malloc(param->data_len * sizeof(uint8))) == NULL)
-               return False;
-       strhex_to_str(param->data, 2*(param->data_len), tok[3]);                
-       param->next=NULL;       
+       if (ret == -1)
+               DEBUG(8, ("error updating printer to tdb on disk\n"));
 
-       DEBUGADD(105,("value:[%s], len:[%d]\n", param->value, param->data_len));
-       return True;
-}
+       safe_free(buf);
 
-/****************************************************************************
-fill a NT_PRINTER_PARAM from a text file
+       DEBUG(8,("packed printer [%s] with driver [%s] portname=[%s] len=%d\n",
+                info->sharename, info->drivername, info->portname, len));
 
-used when reading from disk.
-****************************************************************************/
-void dump_a_param(NT_PRINTER_PARAM *param)
-{
-       DEBUG(105,("dump_a_param\n"));
-       DEBUGADD(106,("value [%s]\n", param->value));
-       DEBUGADD(106,("type [%d]\n", param->type));
-       DEBUGADD(106,("data len [%d]\n", param->data_len));
+       return ret;
 }
 
+
 /****************************************************************************
 ****************************************************************************/
-BOOL add_a_specific_param(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_PARAM *param)
+void add_a_specific_param(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_PARAM **param)
 {
        NT_PRINTER_PARAM *current;
        
        DEBUG(108,("add_a_specific_param\n"));  
 
-       param->next=NULL;
+       (*param)->next=NULL;
        
        if (info_2->specific == NULL)
        {
-               info_2->specific=param;
+               info_2->specific=*param;
        }
        else
        {
@@ -858,9 +1826,10 @@ BOOL add_a_specific_param(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_PARAM *par
                while (current->next != NULL) {
                        current=current->next;
                }               
-               current->next=param;
+               current->next=*param;
        }
-       return (True);
+
+       *param = NULL;
 }
 
 /****************************************************************************
@@ -875,27 +1844,25 @@ BOOL unlink_specific_param_if_exist(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_
        
        if (current==NULL) return (False);
        
-       if ( !strcmp(current->value, param->value) && 
-           (strlen(current->value)==strlen(param->value)) )
-       {
+       if ( !strcmp(current->value, param->value) &&
+           (strlen(current->value)==strlen(param->value)) ) {
                DEBUG(109,("deleting first value\n"));
                info_2->specific=current->next;
                safe_free(current->data);
-               free(current);
+               safe_free(current);
                DEBUG(109,("deleted first value\n"));
                return (True);
        }
 
        current=previous->next;
                
-       while ( current!=NULL )
-       {
+       while ( current!=NULL ) {
                if (!strcmp(current->value, param->value) &&
-                   strlen(current->value)==strlen(param->value) )
-               {
+                   strlen(current->value)==strlen(param->value) ) {
                        DEBUG(109,("deleting current value\n"));
                        previous->next=current->next;
-                       free(current);
+                       safe_free(current->data);
+                       safe_free(current);
                        DEBUG(109,("deleted current value\n"));
                        return(True);
                }
@@ -909,8 +1876,7 @@ BOOL unlink_specific_param_if_exist(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_
 /****************************************************************************
  Clean up and deallocate a (maybe partially) allocated NT_PRINTER_PARAM.
 ****************************************************************************/
-
-static void free_nt_printer_param(NT_PRINTER_PARAM **param_ptr)
+void free_nt_printer_param(NT_PRINTER_PARAM **param_ptr)
 {
        NT_PRINTER_PARAM *param = *param_ptr;
 
@@ -920,17 +1886,110 @@ static void free_nt_printer_param(NT_PRINTER_PARAM **param_ptr)
        DEBUG(106,("free_nt_printer_param: deleting param [%s]\n", param->value));
 
        if(param->data)
-               free(param->data);
+               safe_free(param->data);
 
-       free(param);
+       safe_free(param);
        *param_ptr = NULL;
 }
 
+/****************************************************************************
+ Malloc and return an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *construct_nt_devicemode(const fstring default_devicename)
+{
+/*
+ * should I init this ones ???
+       nt_devmode->devicename
+*/
+
+       char adevice[32];
+       NT_DEVICEMODE *nt_devmode = (NT_DEVICEMODE *)malloc(sizeof(NT_DEVICEMODE));
+
+       if (nt_devmode == NULL) {
+               DEBUG(0,("construct_nt_devicemode: malloc fail.\n"));
+               return NULL;
+       }
+
+       ZERO_STRUCTP(nt_devmode);
+
+       safe_strcpy(adevice, default_devicename, sizeof(adevice));
+       fstrcpy(nt_devmode->devicename, adevice);       
+       
+       fstrcpy(nt_devmode->formname, "Letter");
+
+       nt_devmode->specversion      = 0x0401;
+       nt_devmode->driverversion    = 0x0400;
+       nt_devmode->size             = 0x00DC;
+       nt_devmode->driverextra      = 0x0000;
+       nt_devmode->fields           = FORMNAME | TTOPTION | PRINTQUALITY |
+                                      DEFAULTSOURCE | COPIES | SCALE |
+                                      PAPERSIZE | ORIENTATION;
+       nt_devmode->orientation      = 1;
+       nt_devmode->papersize        = PAPER_LETTER;
+       nt_devmode->paperlength      = 0;
+       nt_devmode->paperwidth       = 0;
+       nt_devmode->scale            = 0x64;
+       nt_devmode->copies           = 01;
+       nt_devmode->defaultsource    = BIN_FORMSOURCE;
+       nt_devmode->printquality     = RES_HIGH;           /* 0x0258 */
+       nt_devmode->color            = COLOR_MONOCHROME;
+       nt_devmode->duplex           = DUP_SIMPLEX;
+       nt_devmode->yresolution      = 0;
+       nt_devmode->ttoption         = TT_SUBDEV;
+       nt_devmode->collate          = COLLATE_FALSE;
+       nt_devmode->icmmethod        = 0;
+       nt_devmode->icmintent        = 0;
+       nt_devmode->mediatype        = 0;
+       nt_devmode->dithertype       = 0;
+
+       /* non utilisés par un driver d'imprimante */
+       nt_devmode->logpixels        = 0;
+       nt_devmode->bitsperpel       = 0;
+       nt_devmode->pelswidth        = 0;
+       nt_devmode->pelsheight       = 0;
+       nt_devmode->displayflags     = 0;
+       nt_devmode->displayfrequency = 0;
+       nt_devmode->reserved1        = 0;
+       nt_devmode->reserved2        = 0;
+       nt_devmode->panningwidth     = 0;
+       nt_devmode->panningheight    = 0;
+       
+       nt_devmode->private=NULL;
+
+       return nt_devmode;
+}
+
+/****************************************************************************
+ Deepcopy an NT devicemode.
+****************************************************************************/
+
+NT_DEVICEMODE *dup_nt_devicemode(NT_DEVICEMODE *nt_devicemode)
+{
+       NT_DEVICEMODE *new_nt_devicemode = NULL;
+
+       if ((new_nt_devicemode = (NT_DEVICEMODE *)memdup(nt_devicemode, sizeof(NT_DEVICEMODE))) == NULL) {
+               DEBUG(0,("dup_nt_devicemode: malloc fail.\n"));
+               return NULL;
+       }
+
+       new_nt_devicemode->private = NULL;
+       if (nt_devicemode->private != NULL) {
+               if ((new_nt_devicemode->private = memdup(nt_devicemode->private, nt_devicemode->driverextra)) == NULL) {
+                       safe_free(new_nt_devicemode);
+                       DEBUG(0,("dup_nt_devicemode: malloc fail.\n"));
+                       return NULL;
+        }
+       }
+
+       return new_nt_devicemode;
+}
+
 /****************************************************************************
  Clean up and deallocate a (maybe partially) allocated NT_DEVICEMODE.
 ****************************************************************************/
 
-static void free_nt_devicemode(NT_DEVICEMODE **devmode_ptr)
+void free_nt_devicemode(NT_DEVICEMODE **devmode_ptr)
 {
        NT_DEVICEMODE *nt_devmode = *devmode_ptr;
 
@@ -940,16 +1999,15 @@ static void free_nt_devicemode(NT_DEVICEMODE **devmode_ptr)
        DEBUG(106,("free_nt_devicemode: deleting DEVMODE\n"));
 
        if(nt_devmode->private)
-               free(nt_devmode->private);
+               safe_free(nt_devmode->private);
 
-       free(nt_devmode);
+       safe_free(nt_devmode);
        *devmode_ptr = NULL;
 }
 
 /****************************************************************************
  Clean up and deallocate a (maybe partially) allocated NT_PRINTER_INFO_LEVEL_2.
 ****************************************************************************/
-
 static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr)
 {
        NT_PRINTER_INFO_LEVEL_2 *info = *info_ptr;
@@ -961,6 +2019,7 @@ static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr)
        DEBUG(106,("free_nt_printer_info_level_2: deleting info\n"));
 
        free_nt_devicemode(&info->devmode);
+       free_sec_desc_buf(&info->secdesc_buf);
 
        for(param_ptr = info->specific; param_ptr; ) {
                NT_PRINTER_PARAM *tofree = param_ptr;
@@ -969,254 +2028,245 @@ static void free_nt_printer_info_level_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr)
                free_nt_printer_param(&tofree);
        }
 
-       free(info);
+       safe_free(*info_ptr);
        *info_ptr = NULL;
 }
 
+
 /****************************************************************************
 ****************************************************************************/
-static uint32 get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstring sharename)
+static int unpack_devicemode(NT_DEVICEMODE **nt_devmode, char *buf, int buflen)
 {
-       pstring file;
-       fstring printer_name;
-       NT_PRINTER_INFO_LEVEL_2 *info = NULL;
-       NT_DEVICEMODE *nt_devmode = NULL;
-       NT_PRINTER_PARAM *param = NULL;
-       fstring p;
-       char *v = NULL;
-       char **lines;
-       int i;
-               
-       /*
-        * the sharename argument is the SAMBA sharename
-        */
-       StrnCpy(printer_name, sharename, sizeof(printer_name)-1);
-               
-       slprintf(file, sizeof(file)-1, "%s/NTprinter_%s",
-                lp_nt_drivers_file(), printer_name);
-
-       lines = file_lines_load(file,NULL);
-       if(lines == NULL) {
-               DEBUG(2, ("get_a_printer_2: Cannot open printer file [%s]. Error was %s\n", file, strerror(errno) ));
-               return(2);
-       }
-
-       /* the file exists, allocate some memory */
-       if((info=(NT_PRINTER_INFO_LEVEL_2 *)malloc(sizeof(NT_PRINTER_INFO_LEVEL_2))) == NULL)
-               goto err;
-
-       ZERO_STRUCTP(info);
-
-       if((nt_devmode=(NT_DEVICEMODE *)malloc(sizeof(NT_DEVICEMODE))) == NULL)
-               goto err;
-
-       ZERO_STRUCTP(nt_devmode);
-       init_devicemode(nt_devmode);
-       
-       info->devmode=nt_devmode;
-
-       for (i=0; lines[i]; i++) {
-               char *line = lines[i];
-
-               if (!*line) continue;
-
-               v=strncpyn(p, line, sizeof(p), ':');
-               if (v==NULL)
-               {
-                       DEBUG(1, ("malformed printer entry (no `:')\n"));
-                       DEBUGADD(2, ("line [%s]\n", line));             
-                       continue;
-               }
-               
-               v++;
-               
-               trim_string(v, " ", NULL);
-               trim_string(v, NULL, " ");
-               trim_string(v, NULL, "\n");
-               
-               /* don't check if v==NULL as an empty arg is valid */
-               
-               DEBUGADD(115, ("[%s]:[%s]\n", p, v));
-
-               /*
-                * The PRINTER_INFO_2 fields
+       int len = 0;
+       int extra_len = 0;
+       NT_DEVICEMODE devmode;
+
+       ZERO_STRUCT(devmode);
+
+       len += tdb_unpack(buf+len, buflen-len, "p", nt_devmode);
+
+       if (!*nt_devmode) return len;
+
+       len += tdb_unpack(buf+len, buflen-len, "ffwwwwwwwwwwwwwwwwwwddddddddddddddp",
+                         devmode.devicename,
+                         devmode.formname,
+
+                         &devmode.specversion,
+                         &devmode.driverversion,
+                         &devmode.size,
+                         &devmode.driverextra,
+                         &devmode.orientation,
+                         &devmode.papersize,
+                         &devmode.paperlength,
+                         &devmode.paperwidth,
+                         &devmode.scale,
+                         &devmode.copies,
+                         &devmode.defaultsource,
+                         &devmode.printquality,
+                         &devmode.color,
+                         &devmode.duplex,
+                         &devmode.yresolution,
+                         &devmode.ttoption,
+                         &devmode.collate,
+                         &devmode.logpixels,
+                       
+                         &devmode.fields,
+                         &devmode.bitsperpel,
+                         &devmode.pelswidth,
+                         &devmode.pelsheight,
+                         &devmode.displayflags,
+                         &devmode.displayfrequency,
+                         &devmode.icmmethod,
+                         &devmode.icmintent,
+                         &devmode.mediatype,
+                         &devmode.dithertype,
+                         &devmode.reserved1,
+                         &devmode.reserved2,
+                         &devmode.panningwidth,
+                         &devmode.panningheight,
+                         &devmode.private);
+       
+       if (devmode.private) {
+               /* the len in tdb_unpack is an int value and
+                * devmoce.driverextra is only a short
                 */
+               len += tdb_unpack(buf+len, buflen-len, "B", &extra_len, &devmode.private);
+               devmode.driverextra=(uint16)extra_len;
                
-               if (!strncmp(p, "attributes", strlen("attributes")))
-                       info->attributes=atoi(v);
-
-               if (!strncmp(p, "priority", strlen("priority")))
-                       info->priority=atoi(v);
-
-               if (!strncmp(p, "default_priority", strlen("default_priority")))
-                       info->default_priority=atoi(v);
-
-               if (!strncmp(p, "starttime", strlen("starttime")))
-                       info->starttime=atoi(v);
-
-               if (!strncmp(p, "untiltime", strlen("untiltime")))
-                       info->untiltime=atoi(v);
-
-               if (!strncmp(p, "status", strlen("status")))
-                       info->status=atoi(v);
-
-               if (!strncmp(p, "cjobs", strlen("cjobs")))
-                       info->cjobs=atoi(v);
-
-               if (!strncmp(p, "averageppm", strlen("averageppm")))
-                       info->averageppm=atoi(v);
-               
-               if (!strncmp(p, "changeid", strlen("changeid")))
-                       info->changeid=atoi(v);
-               
-               if (!strncmp(p, "c_setprinter", strlen("c_setprinter")))
-                       info->c_setprinter=atoi(v);
-               
-               if (!strncmp(p, "setuptime", strlen("setuptime")))
-                       info->setuptime=atoi(v);
-               
-               if (!strncmp(p, "servername", strlen("servername")))
-                       StrnCpy(info->servername, v, strlen(v));
-
-               if (!strncmp(p, "printername", strlen("printername")))
-                       StrnCpy(info->printername, v, strlen(v));
-
-               if (!strncmp(p, "sharename", strlen("sharename")))
-                       StrnCpy(info->sharename, v, strlen(v));
-
-               if (!strncmp(p, "portname", strlen("portname")))
-                       StrnCpy(info->portname, v, strlen(v));
-
-               if (!strncmp(p, "drivername", strlen("drivername")))
-                       StrnCpy(info->drivername, v, strlen(v));
-
-               if (!strncmp(p, "location", strlen("location")))
-                       StrnCpy(info->location, v, strlen(v));
-
-               if (!strncmp(p, "sepfile", strlen("sepfile")))
-                       StrnCpy(info->sepfile, v, strlen(v));
-
-               if (!strncmp(p, "printprocessor", strlen("printprocessor")))
-                       StrnCpy(info->printprocessor, v, strlen(v));
-
-               if (!strncmp(p, "datatype", strlen("datatype")))
-                       StrnCpy(info->datatype, v, strlen(v));
-
-               if (!strncmp(p, "parameters", strlen("parameters")))
-                       StrnCpy(info->parameters, v, strlen(v));
-
-               /*
-                * The DEVICEMODE fields
-                */
-
-               if (!strncmp(p, "formname", strlen("formname")))
-                       StrnCpy(nt_devmode->formname, v, strlen(v));
-                       
-               if (!strncmp(p, "specversion", strlen("specversion")))
-                       nt_devmode->specversion=atoi(v);
-
-               if (!strncmp(p, "driverversion", strlen("driverversion")))
-                       nt_devmode->driverversion=atoi(v);
+               /* check to catch an invalid TDB entry so we don't segfault */
+               if (devmode.driverextra == 0) {
+                       devmode.private = NULL;
+               }
+       }
 
-               if (!strncmp(p, "size", strlen("size")))
-                       nt_devmode->size=atoi(v);
+       *nt_devmode = (NT_DEVICEMODE *)memdup(&devmode, sizeof(devmode));
 
-               if (!strncmp(p, "driverextra", strlen("driverextra")))
-                       nt_devmode->driverextra=atoi(v);
+       DEBUG(8,("Unpacked devicemode [%s](%s)\n", devmode.devicename, devmode.formname));
+       if (devmode.private)
+               DEBUG(8,("with a private section of %d bytes\n", devmode.driverextra));
 
-               if (!strncmp(p, "fields", strlen("fields")))
-                       nt_devmode->fields=atoi(v);
+       return len;
+}
 
-               if (!strncmp(p, "orientation", strlen("orientation")))
-                       nt_devmode->orientation=atoi(v);
+/****************************************************************************
+****************************************************************************/
+static int unpack_specifics(NT_PRINTER_PARAM **list, char *buf, int buflen)
+{
+       int len = 0;
+       NT_PRINTER_PARAM param, *p;
 
-               if (!strncmp(p, "papersize", strlen("papersize")))
-                       nt_devmode->papersize=atoi(v);
+       *list = NULL;
 
-               if (!strncmp(p, "paperlength", strlen("paperlength")))
-                       nt_devmode->paperlength=atoi(v);
+       while (1) {
+               len += tdb_unpack(buf+len, buflen-len, "p", &p);
+               if (!p) break;
 
-               if (!strncmp(p, "paperwidth", strlen("paperwidth")))
-                       nt_devmode->paperwidth=atoi(v);
+               len += tdb_unpack(buf+len, buflen-len, "fdB",
+                                 param.value,
+                                 &param.type,
+                                 &param.data_len,
+                                 &param.data);
+               param.next = *list;
+               *list = memdup(&param, sizeof(param));
 
-               if (!strncmp(p, "scale", strlen("scale")))
-                       nt_devmode->scale=atoi(v);
+               DEBUG(8,("specific: [%s], len: %d\n", param.value, param.data_len));
+       }
 
-               if (!strncmp(p, "copies", strlen("copies")))
-                       nt_devmode->copies=atoi(v);
+       return len;
+}
 
-               if (!strncmp(p, "defaultsource", strlen("defaultsource")))
-                       nt_devmode->defaultsource=atoi(v);
 
-               if (!strncmp(p, "printquality", strlen("printquality")))
-                       nt_devmode->printquality=atoi(v);
+/****************************************************************************
+get a default printer info 2 struct
+****************************************************************************/
+static uint32 get_a_printer_2_default(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstring sharename)
+{
+       extern pstring global_myname;
+       int snum;
+       NT_PRINTER_INFO_LEVEL_2 info;
 
-               if (!strncmp(p, "color", strlen("color")))
-                       nt_devmode->color=atoi(v);
+       ZERO_STRUCT(info);
 
-               if (!strncmp(p, "duplex", strlen("duplex")))
-                       nt_devmode->duplex=atoi(v);
+       snum = lp_servicenumber(sharename);
 
-               if (!strncmp(p, "yresolution", strlen("yresolution")))
-                       nt_devmode->yresolution=atoi(v);
+       slprintf(info.servername, sizeof(info.servername), "\\\\%s", global_myname);
+       slprintf(info.printername, sizeof(info.printername), "\\\\%s\\%s", 
+                global_myname, sharename);
+       fstrcpy(info.sharename, sharename);
+       fstrcpy(info.portname, SAMBA_PRINTER_PORT_NAME);
+       fstrcpy(info.drivername, lp_printerdriver(snum));
 
-               if (!strncmp(p, "ttoption", strlen("ttoption")))
-                       nt_devmode->ttoption=atoi(v);
+       if (!*info.drivername)
+               fstrcpy(info.drivername, "NO DRIVER AVAILABLE FOR THIS PRINTER");
 
-               if (!strncmp(p, "collate", strlen("collate")))
-                       nt_devmode->collate=atoi(v);
+       DEBUG(10,("get_a_printer_2_default: driver name set to [%s]\n", info.drivername));
 
-               if (!strncmp(p, "icmmethod", strlen("icmmethod")))
-                       nt_devmode->icmmethod=atoi(v);
+       pstrcpy(info.comment, "");
+       fstrcpy(info.printprocessor, "winprint");
+       fstrcpy(info.datatype, "RAW");
 
-               if (!strncmp(p, "icmintent", strlen("icmintent")))
-                       nt_devmode->icmintent=atoi(v);
+       info.attributes = PRINTER_ATTRIBUTE_SHARED   \
+                        | PRINTER_ATTRIBUTE_LOCAL  \
+                        | PRINTER_ATTRIBUTE_RAW_ONLY \
+                        | PRINTER_ATTRIBUTE_QUEUED ;            /* attributes */
 
-               if (!strncmp(p, "mediatype", strlen("mediatype")))
-                       nt_devmode->mediatype=atoi(v);
+       info.starttime = 0; /* Minutes since 12:00am GMT */
+       info.untiltime = 0; /* Minutes since 12:00am GMT */
+       info.priority = 1;
+       info.default_priority = 1;
+       info.setuptime = (uint32)time(NULL);
 
-               if (!strncmp(p, "dithertype", strlen("dithertype")))
-                       nt_devmode->dithertype=atoi(v);
-                       
-               if (!strncmp(p, "private", strlen("private")))
-               {
-                       if((nt_devmode->private=(uint8 *)malloc(nt_devmode->driverextra*sizeof(uint8))) == NULL)
-                               goto err;
+       if ((info.devmode = construct_nt_devicemode(info.printername)) == NULL)
+               goto fail;
 
-                       strhex_to_str(nt_devmode->private, 2*nt_devmode->driverextra, v);
-               }
-               
-               /* the specific */
-               
-               if (!strncmp(p, "specific", strlen("specific")))
-               {
-                       if((param=(NT_PRINTER_PARAM *)malloc(sizeof(NT_PRINTER_PARAM))) == NULL)
-                               goto err;
+       if (!nt_printing_getsec(sharename, &info.secdesc_buf))
+               goto fail;
 
-                       ZERO_STRUCTP(param);
-                       
-                       if(!dissect_and_fill_a_param(param, v))
-                               goto err;
-                       
-                       dump_a_param(param);
-                       
-                       add_a_specific_param(info, param);
-               }
-               
+       *info_ptr = (NT_PRINTER_INFO_LEVEL_2 *)memdup(&info, sizeof(info));
+       if (! *info_ptr) {
+               DEBUG(0,("get_a_printer_2_default: malloc fail.\n"));
+               goto fail;
        }
-       file_lines_free(lines);
-       
-       *info_ptr=info;
-       
+
        return (0);     
 
-  err:
+  fail:
+
+       if (info.devmode)
+               free_nt_devicemode(&info.devmode);
+       if (info.secdesc_buf)
+               free_sec_desc_buf(&info.secdesc_buf);
+       return 2;
+}
 
-       if(lines)
-               file_lines_free(lines);
-       if(info)
-               free_nt_printer_info_level_2(&info);
-       return(2);
+/****************************************************************************
+****************************************************************************/
+static uint32 get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstring sharename)
+{
+       pstring key;
+       NT_PRINTER_INFO_LEVEL_2 info;
+       int len = 0;
+       TDB_DATA kbuf, dbuf;
+       fstring printername;
+               
+       ZERO_STRUCT(info);
+
+       slprintf(key, sizeof(key), "%s%s", PRINTERS_PREFIX, sharename);
+       dos_to_unix(key, True);                /* Convert key to unix-codepage */
+
+       kbuf.dptr = key;
+       kbuf.dsize = strlen(key)+1;
+
+       dbuf = tdb_fetch(tdb, kbuf);
+       if (!dbuf.dptr)
+               return get_a_printer_2_default(info_ptr, sharename);
+
+       len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "dddddddddddfffffPfffff",
+                       &info.attributes,
+                       &info.priority,
+                       &info.default_priority,
+                       &info.starttime,
+                       &info.untiltime,
+                       &info.status,
+                       &info.cjobs,
+                       &info.averageppm,
+                       &info.changeid,
+                       &info.c_setprinter,
+                       &info.setuptime,
+                       info.servername,
+                       info.printername,
+                       info.sharename,
+                       info.portname,
+                       info.drivername,
+                       info.comment,
+                       info.location,
+                       info.sepfile,
+                       info.printprocessor,
+                       info.datatype,
+                       info.parameters);
+
+       /* Samba has to have shared raw drivers. */
+       info.attributes |= (PRINTER_ATTRIBUTE_SHARED|PRINTER_ATTRIBUTE_RAW_ONLY);
+
+       /* Restore the stripped strings. */
+       slprintf(info.servername, sizeof(info.servername), "\\\\%s", global_myname);
+       slprintf(printername, sizeof(printername), "\\\\%s\\%s", global_myname,
+                       info.printername);
+       fstrcpy(info.printername, printername);
+
+       len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len);
+       len += unpack_specifics(&info.specific,dbuf.dptr+len, dbuf.dsize-len);
+
+       nt_printing_getsec(sharename, &info.secdesc_buf);
+
+       safe_free(dbuf.dptr);
+       *info_ptr=memdup(&info, sizeof(info));
+
+       DEBUG(9,("Unpacked printer [%s] name [%s] running driver [%s]\n",
+                sharename, info.printername, info.drivername));
+
+       
+       return 0;       
 }
 
 /****************************************************************************
@@ -1224,17 +2274,17 @@ debugging function, dump at level 6 the struct in the logs
 ****************************************************************************/
 static uint32 dump_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
 {
-       uint32 success;
+       uint32 result;
        NT_PRINTER_INFO_LEVEL_2 *info2;
        
        DEBUG(106,("Dumping printer at level [%d]\n", level));
        
        switch (level)
        {
-               case 2: 
+               case 2:
                {
                        if (printer.info_2 == NULL)
-                               success=5;
+                               result=5;
                        else
                        {
                                info2=printer.info_2;
@@ -1249,29 +2299,50 @@ static uint32 dump_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
                                DEBUGADD(106,("averageppm:[%d]\n", info2->averageppm));
                                DEBUGADD(106,("changeid:[%d]\n", info2->changeid));
                                DEBUGADD(106,("c_setprinter:[%d]\n", info2->c_setprinter));
-                               DEBUGADD(106,("setuptime:[%d]\n", (int)info2->setuptime));
+                               DEBUGADD(106,("setuptime:[%d]\n", info2->setuptime));
 
                                DEBUGADD(106,("servername:[%s]\n", info2->servername));
                                DEBUGADD(106,("printername:[%s]\n", info2->printername));
                                DEBUGADD(106,("sharename:[%s]\n", info2->sharename));
                                DEBUGADD(106,("portname:[%s]\n", info2->portname));
                                DEBUGADD(106,("drivername:[%s]\n", info2->drivername));
+                               DEBUGADD(106,("comment:[%s]\n", info2->comment));
                                DEBUGADD(106,("location:[%s]\n", info2->location));
                                DEBUGADD(106,("sepfile:[%s]\n", info2->sepfile));
                                DEBUGADD(106,("printprocessor:[%s]\n", info2->printprocessor));
                                DEBUGADD(106,("datatype:[%s]\n", info2->datatype));
                                DEBUGADD(106,("parameters:[%s]\n", info2->parameters));
-                               success=0;
+                               result=0;
                        }
                        break;
                }
                default:
                        DEBUGADD(1,("Level not implemented\n"));
-                       success=1;
+                       result=1;
                        break;
        }
        
-       return (success);
+       return result;
+}
+
+/****************************************************************************
+ Get the parameters we can substitute in an NT print job.
+****************************************************************************/
+
+void get_printer_subst_params(int snum, fstring *printername, fstring *sharename, fstring *portname)
+{
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+
+       **printername = **sharename = **portname = '\0';
+
+       if (get_a_printer(&printer, 2, lp_servicename(snum))!=0)
+               return;
+
+       fstrcpy(*printername, printer->info_2->printername);
+       fstrcpy(*sharename, printer->info_2->sharename);
+       fstrcpy(*portname, printer->info_2->portname);
+
+       free_a_printer(&printer, 2);
 }
 
 /*
@@ -1280,183 +2351,248 @@ static uint32 dump_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
  * JFM.
  */
 
+/****************************************************************************
+ Modify a printer. This is called from SETPRINTERDATA/DELETEPRINTERDATA.
+****************************************************************************/
+
+uint32 mod_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+       uint32 result;
+       
+       dump_a_printer(printer, level); 
+       
+       switch (level)
+       {
+               case 2:
+               {
+                       printer.info_2->c_setprinter++;
+                       result=update_a_printer_2(printer.info_2);
+                       break;
+               }
+               default:
+                       result=1;
+                       break;
+       }
+       
+       return result;
+}
 
 /****************************************************************************
+ Add a printer. This is called from ADDPRINTER(EX) and also SETPRINTER.
+ We split this out from mod_a_printer as it updates the id's and timestamps.
 ****************************************************************************/
+
 uint32 add_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
 {
-       uint32 success;
+       uint32 result;
        
        dump_a_printer(printer, level); 
        
        switch (level)
        {
-               case 2: 
+               case 2:
                {
-                       success=add_a_printer_2(printer.info_2);
+                       /*
+                        * Update the changestamp.
+                        * Note we must *not* do this in mod_a_printer().
+                        */
+                       NTTIME time_nt;
+                       time_t time_unix = time(NULL);
+                       unix_to_nt_time(&time_nt, time_unix);
+                       printer.info_2->changeid=time_nt.low;
+
+                       printer.info_2->c_setprinter++;
+                       result=update_a_printer_2(printer.info_2);
                        break;
                }
                default:
-                       success=1;
+                       result=1;
                        break;
        }
        
-       return (success);
+       return result;
 }
 
 /****************************************************************************
+ Get a NT_PRINTER_INFO_LEVEL struct. It returns malloced memory.
 ****************************************************************************/
-uint32 get_a_printer(NT_PRINTER_INFO_LEVEL *printer, uint32 level, fstring sharename)
+
+uint32 get_a_printer(NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level, fstring sharename)
 {
-       uint32 success;
+       uint32 result;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
        
+       *pp_printer = NULL;
+
        DEBUG(10,("get_a_printer: [%s] level %u\n", sharename, (unsigned int)level));
 
        switch (level)
        {
-               case 2: 
+               case 2:
                {
-                       printer->info_2=NULL;
-                       success=get_a_printer_2(&(printer->info_2), sharename);
+                       if ((printer = (NT_PRINTER_INFO_LEVEL *)malloc(sizeof(NT_PRINTER_INFO_LEVEL))) == NULL) {
+                               DEBUG(0,("get_a_printer: malloc fail.\n"));
+                               return 1;
+                       }
+                       ZERO_STRUCTP(printer);
+                       result=get_a_printer_2(&printer->info_2, sharename);
+                       if (result == 0) {
+                               dump_a_printer(*printer, level);
+                               *pp_printer = printer;
+                       } else {
+                               safe_free(printer);
+                       }
                        break;
                }
                default:
-                       success=1;
+                       result=1;
                        break;
        }
        
-       dump_a_printer(*printer, level);
-
-       DEBUG(10,("get_a_printer: [%s] level %u returning %u\n", sharename, (unsigned int)level, (unsigned int)success));
+       DEBUG(10,("get_a_printer: [%s] level %u returning %u\n", sharename, (unsigned int)level, (unsigned int)result));
 
-       return (success);
+       return result;
 }
 
 /****************************************************************************
+ Deletes a NT_PRINTER_INFO_LEVEL struct.
 ****************************************************************************/
-uint32 free_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+
+uint32 free_a_printer(NT_PRINTER_INFO_LEVEL **pp_printer, uint32 level)
 {
-       uint32 success;
+       uint32 result;
+       NT_PRINTER_INFO_LEVEL *printer = *pp_printer;
+
        DEBUG(104,("freeing a printer at level [%d]\n", level));
+
+       if (printer == NULL)
+               return 0;
        
        switch (level)
        {
-               case 2: 
+               case 2:
                {
-                       if (printer.info_2 != NULL)
+                       if (printer->info_2 != NULL)
                        {
-                               free_nt_printer_info_level_2(&printer.info_2);
-                               success=0;
+                               free_nt_printer_info_level_2(&printer->info_2);
+                               result=0;
                        }
                        else
                        {
-                               success=4;
+                               result=4;
                        }
                        break;
                }
                default:
-                       success=1;
+                       result=1;
                        break;
        }
-       return (success);
+
+       safe_free(printer);
+       *pp_printer = NULL;
+       return result;
 }
 
 /****************************************************************************
 ****************************************************************************/
 uint32 add_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
 {
-       uint32 success;
+       uint32 result;
        DEBUG(104,("adding a printer at level [%d]\n", level));
        dump_a_printer_driver(driver, level);
        
        switch (level)
        {
-               case 3: 
+               case 3:
                {
-                       success=add_a_printer_driver_3(driver.info_3);
+                       result=add_a_printer_driver_3(driver.info_3);
                        break;
                }
 
-               case 6: 
+               case 6:
                {
-                       success=add_a_printer_driver_6(driver.info_6);
+                       result=add_a_printer_driver_6(driver.info_6);
                        break;
                }
                default:
-                       success=1;
+                       result=1;
                        break;
        }
        
-       return (success);
+       return result;
 }
 /****************************************************************************
 ****************************************************************************/
-uint32 get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level, 
-                            fstring printername, fstring architecture)
+uint32 get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level,
+                            fstring printername, fstring architecture, uint32 version)
 {
-       uint32 success;
+       uint32 result;
        
        switch (level)
        {
-               case 3: 
+               case 3:
                {
-                       success=get_a_printer_driver_3(&(driver->info_3), 
-                                                      printername,
-                                                      architecture);
+                       result=get_a_printer_driver_3(&driver->info_3, printername, architecture, version);
                        break;
                }
                default:
-                       success=1;
+                       result=1;
                        break;
        }
        
-       if (success == 0) dump_a_printer_driver(*driver, level);
-       return (success);
+       if (result == 0)
+               dump_a_printer_driver(*driver, level);
+       return result;
 }
 
 /****************************************************************************
 ****************************************************************************/
 uint32 free_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
 {
-       uint32 success;
-       NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
-       char **dependentfiles;
+       uint32 result;
        
        switch (level)
        {
-               case 3: 
+               case 3:
                {
+                       NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
                        if (driver.info_3 != NULL)
                        {
                                info3=driver.info_3;
-                               dependentfiles=info3->dependentfiles;
-       
-                               while ( **dependentfiles != '\0' )
-                               {
-                                       free (*dependentfiles);
-                                       dependentfiles++;
-                               }
-                               
-                               /* the last one (1 char !) */
-                               free (*dependentfiles);
-                               
-                               dependentfiles=info3->dependentfiles;
-                               free (dependentfiles);
-                               
-                               free(info3);
-                               success=0;
+                               safe_free(info3->dependentfiles);
+                               ZERO_STRUCTP(info3);
+                               safe_free(info3);
+                               result=0;
+                       }
+                       else
+                       {
+                               result=4;
+                       }
+                       break;
+               }
+               case 6:
+               {
+                       NT_PRINTER_DRIVER_INFO_LEVEL_6 *info6;
+                       if (driver.info_6 != NULL)
+                       {
+                               info6=driver.info_6;
+                               safe_free(info6->dependentfiles);
+                               safe_free(info6->previousnames);
+                               ZERO_STRUCTP(info6);
+                               safe_free(info6);
+                               result=0;
                        }
                        else
                        {
-                               success=4;
+                               result=4;
                        }
                        break;
                }
                default:
-                       success=1;
+                       result=1;
                        break;
        }
-       return (success);
+       return result;
 }
 
 /****************************************************************************
@@ -1470,8 +2606,7 @@ BOOL get_specific_param_by_index(NT_PRINTER_INFO_LEVEL printer, uint32 level, ui
        
        param=printer.info_2->specific;
        
-       while (param != NULL && i <= param_index)
-       {
+       while (param != NULL && i < param_index) {
                param=param->next;
                i++;
        }
@@ -1485,6 +2620,7 @@ BOOL get_specific_param_by_index(NT_PRINTER_INFO_LEVEL printer, uint32 level, ui
        *data=(uint8 *)malloc(param->data_len*sizeof(uint8));
        if(*data == NULL)
                return False;
+       ZERO_STRUCTP(*data);
        memcpy(*data, param->data, param->data_len);
        *len=param->data_len;
        return True;
@@ -1492,7 +2628,7 @@ BOOL get_specific_param_by_index(NT_PRINTER_INFO_LEVEL printer, uint32 level, ui
 
 /****************************************************************************
 ****************************************************************************/
-BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level, 
+BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level,
                         fstring value, uint8 **data, uint32 *type, uint32 *len)
 {
        /* right now that's enough ! */ 
@@ -1504,7 +2640,11 @@ BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level,
                
        while (param != NULL)
        {
-               if ( !strcmp(value, param->value) 
+#if 1 /* JRA - I think this should be case insensitive.... */
+               if ( strequal(value, param->value)
+#else
+               if ( !strcmp(value, param->value)
+#endif
                    && strlen(value)==strlen(param->value))
                        break;
                        
@@ -1531,55 +2671,286 @@ BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level,
 }
 
 /****************************************************************************
+ Store a security desc for a printer.
 ****************************************************************************/
-void init_devicemode(NT_DEVICEMODE *nt_devmode)
+
+uint32 nt_printing_setsec(char *printername, SEC_DESC_BUF *secdesc_ctr)
 {
-/*
- * should I init this ones ???
-       nt_devmode->devicename
-*/
-       fstrcpy(nt_devmode->formname, "A4");
+       SEC_DESC_BUF *new_secdesc_ctr = NULL;
+       SEC_DESC_BUF *old_secdesc_ctr = NULL;
+       prs_struct ps;
+       TALLOC_CTX *mem_ctx = NULL;
+       fstring key;
+       uint32 status;
+
+       mem_ctx = talloc_init();
+       if (mem_ctx == NULL)
+               return False;
 
-       nt_devmode->specversion      = 0x0401;
-       nt_devmode->driverversion    = 0x0400;
-       nt_devmode->size             = 0x00DC;
-       nt_devmode->driverextra      = 0x0000;
-       nt_devmode->fields           = FORMNAME | TTOPTION | PRINTQUALITY | 
-                                      DEFAULTSOURCE | COPIES | SCALE | 
-                                      PAPERSIZE | ORIENTATION;
-       nt_devmode->orientation      = 1;
-       nt_devmode->papersize        = PAPER_A4;
-       nt_devmode->paperlength      = 0;
-       nt_devmode->paperwidth       = 0;
-       nt_devmode->scale            = 0x64;
-       nt_devmode->copies           = 01;
-       nt_devmode->defaultsource    = BIN_FORMSOURCE;
-       nt_devmode->printquality     = 0x0258;
-       nt_devmode->color            = COLOR_MONOCHROME;
-       nt_devmode->duplex           = DUP_SIMPLEX;
-       nt_devmode->yresolution      = 0;
-       nt_devmode->ttoption         = TT_SUBDEV;
-       nt_devmode->collate          = COLLATE_FALSE;
-       nt_devmode->icmmethod        = 0;
-       nt_devmode->icmintent        = 0;
-       nt_devmode->mediatype        = 0;
-       nt_devmode->dithertype       = 0;
+        /* The old owner and group sids of the security descriptor are not
+          present when new ACEs are added or removed by changing printer
+          permissions through NT.  If they are NULL in the new security
+          descriptor then copy them over from the old one. */
 
-       /* non utilisés par un driver d'imprimante */
-       nt_devmode->logpixels        = 0;
-       nt_devmode->bitsperpel       = 0;
-       nt_devmode->pelswidth        = 0;
-       nt_devmode->pelsheight       = 0;
-       nt_devmode->displayflags     = 0;
-       nt_devmode->displayfrequency = 0;
-       nt_devmode->reserved1        = 0;
-       nt_devmode->reserved2        = 0;
-       nt_devmode->panningwidth     = 0;
-       nt_devmode->panningheight    = 0;
-       
-       nt_devmode->private=NULL;
+       if (!secdesc_ctr->sec->owner_sid || !secdesc_ctr->sec->grp_sid) {
+               DOM_SID *owner_sid, *group_sid;
+               SEC_ACL *dacl, *sacl;
+               SEC_DESC *psd = NULL;
+               size_t size;
+
+               nt_printing_getsec(printername, &old_secdesc_ctr);
+
+               /* Pick out correct owner and group sids */
+
+               owner_sid = secdesc_ctr->sec->owner_sid ?
+                       secdesc_ctr->sec->owner_sid :
+                       old_secdesc_ctr->sec->owner_sid;
+
+               group_sid = secdesc_ctr->sec->grp_sid ?
+                       secdesc_ctr->sec->grp_sid :
+                       old_secdesc_ctr->sec->grp_sid;
+
+               dacl = secdesc_ctr->sec->dacl ?
+                       secdesc_ctr->sec->dacl :
+                       old_secdesc_ctr->sec->dacl;
+
+               sacl = secdesc_ctr->sec->sacl ?
+                       secdesc_ctr->sec->sacl :
+                       old_secdesc_ctr->sec->sacl;
+
+               /* Make a deep copy of the security descriptor */
+
+               psd = make_sec_desc(secdesc_ctr->sec->revision,
+                                   owner_sid, group_sid,
+                                   sacl,
+                                   dacl,
+                                   &size);
+
+               new_secdesc_ctr = make_sec_desc_buf(size, psd);
+
+               /* Free up memory */
+
+               free_sec_desc(&psd);
+               free_sec_desc_buf(&old_secdesc_ctr);
+       }
+
+       if (!new_secdesc_ctr) {
+               new_secdesc_ctr = secdesc_ctr;
+       }
+
+       /* Store the security descriptor in a tdb */
+
+       prs_init(&ps, (uint32)sec_desc_size(new_secdesc_ctr->sec) +
+                sizeof(SEC_DESC_BUF), 4, mem_ctx, MARSHALL);
+
+       if (!sec_io_desc_buf("nt_printing_setsec", &new_secdesc_ctr,
+                            &ps, 1)) {
+               status = ERROR_INVALID_FUNCTION;
+               goto out;
+       }
+
+       slprintf(key, sizeof(key), "SECDESC/%s", printername);
+
+       if (tdb_prs_store(tdb, key, &ps)==0) {
+               status = 0;
+       } else {
+               DEBUG(1,("Failed to store secdesc for %s\n", printername));
+               status = ERROR_INVALID_FUNCTION;
+       }
+
+       /* Free mallocated memory */
+
+ out:
+       free_sec_desc_buf(&old_secdesc_ctr);
+
+       if (new_secdesc_ctr != secdesc_ctr) {
+               free_sec_desc_buf(&new_secdesc_ctr);
+       }
+
+       prs_mem_free(&ps);
+       if (mem_ctx)
+               talloc_destroy(mem_ctx);
+       return status;
+}
+
+/****************************************************************************
+ Construct a default security descriptor buffer for a printer.
+****************************************************************************/
+
+static SEC_DESC_BUF *construct_default_printer_sdb(void)
+{
+       SEC_ACE ace[3];
+       SEC_ACCESS sa;
+       SEC_ACL *psa = NULL;
+       SEC_DESC_BUF *sdb = NULL;
+       SEC_DESC *psd = NULL;
+       DOM_SID owner_sid;
+       size_t sd_size;
+       enum SID_NAME_USE name_type;
+
+       /* Create an ACE where Everyone is allowed to print */
+
+       init_sec_access(&sa, PRINTER_ACE_PRINT);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+                    sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+       /* Make the security descriptor owned by the Administrators group
+          on the PDC of the domain. */
+
+       if (winbind_lookup_name(lp_workgroup(), &owner_sid, &name_type)) {
+               sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+       } else {
+
+               /* Backup plan - make printer owned by admins or root.  This should
+                  emulate a lanman printer as security settings can't be
+                  changed. */
+
+               if (!lookup_name( "Printer Administrators", &owner_sid, &name_type) &&
+                       !lookup_name( "Administrators", &owner_sid, &name_type) &&
+                       !lookup_name( "Administrator", &owner_sid, &name_type) &&
+                       !lookup_name("root", &owner_sid, &name_type)) {
+                                               sid_copy(&owner_sid, &global_sid_World);
+               }
+       }
+
+       init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+       init_sec_ace(&ace[1], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+                    sa, SEC_ACE_FLAG_OBJECT_INHERIT |
+                    SEC_ACE_FLAG_INHERIT_ONLY);
+
+       init_sec_access(&sa, PRINTER_ACE_FULL_CONTROL);
+       init_sec_ace(&ace[2], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
+                    sa, SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+       /* The ACL revision number in rpc_secdesc.h differs from the one
+          created by NT when setting ACE entries in printer
+          descriptors.  NT4 complains about the property being edited by a
+          NT5 machine. */
+
+#define NT4_ACL_REVISION 0x2
+
+       if ((psa = make_sec_acl(NT4_ACL_REVISION, 3, ace)) != NULL) {
+               psd = make_sec_desc(SEC_DESC_REVISION,
+                                   &owner_sid, NULL,
+                                   NULL, psa, &sd_size);
+               free_sec_acl(&psa);
+       }
+
+       if (!psd) {
+               DEBUG(0,("construct_default_printer_sd: Failed to make SEC_DESC.\n"));
+               return NULL;
+       }
+
+       sdb = make_sec_desc_buf(sd_size, psd);
+
+       DEBUG(4,("construct_default_printer_sdb: size = %u.\n",
+                (unsigned int)sd_size));
+
+       free_sec_desc(&psd);
+       return sdb;
 }
 
+/****************************************************************************
+ Get a security desc for a printer.
+****************************************************************************/
+
+BOOL nt_printing_getsec(char *printername, SEC_DESC_BUF **secdesc_ctr)
+{
+       prs_struct ps;
+       TALLOC_CTX *mem_ctx = NULL;
+       fstring key;
+       char *temp;
+
+       mem_ctx = talloc_init();
+       if (mem_ctx == NULL)
+               return False;
+
+       if ((temp = strchr(printername + 2, '\\'))) {
+               printername = temp + 1;
+       }
+
+       /* Fetch security descriptor from tdb */
+
+       slprintf(key, sizeof(key), "SECDESC/%s", printername);
+
+       if (tdb_prs_fetch(tdb, key, &ps, mem_ctx)!=0 ||
+           !sec_io_desc_buf("nt_printing_getsec", secdesc_ctr, &ps, 1)) {
+
+               DEBUG(4,("using default secdesc for %s\n", printername));
+
+               if (!(*secdesc_ctr = construct_default_printer_sdb())) {
+                       talloc_destroy(mem_ctx);
+                       return False;
+               }
+
+               talloc_destroy(mem_ctx);
+               return True;
+       }
+
+       /* If security descriptor is owned by S-1-1-0 and winbindd is up,
+          this security descriptor has been created when winbindd was
+          down.  Take ownership of security descriptor. */
+
+       if (sid_equal((*secdesc_ctr)->sec->owner_sid, &global_sid_World)) {
+               DOM_SID owner_sid;
+               enum SID_NAME_USE name_type;
+
+               /* Change sd owner to workgroup administrator */
+
+               if (winbind_lookup_name(lp_workgroup(), &owner_sid,
+                                       &name_type)) {
+                       SEC_DESC_BUF *new_secdesc_ctr = NULL;
+                       SEC_DESC *psd = NULL;
+                       size_t size;
+
+                       /* Create new sd */
+
+                       sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN);
+
+                       psd = make_sec_desc((*secdesc_ctr)->sec->revision,
+                                           &owner_sid,
+                                           (*secdesc_ctr)->sec->grp_sid,
+                                           (*secdesc_ctr)->sec->sacl,
+                                           (*secdesc_ctr)->sec->dacl,
+                                           &size);
+
+                       new_secdesc_ctr = make_sec_desc_buf(size, psd);
+
+                       free_sec_desc(&psd);
+
+                       /* Swap with other one */
+
+                       free_sec_desc_buf(secdesc_ctr);
+                       *secdesc_ctr = new_secdesc_ctr;
+
+                       /* Set it */
+
+                       nt_printing_setsec(printername, *secdesc_ctr);
+               }
+       }
+
+       if (DEBUGLEVEL >= 10) {
+               SEC_ACL *acl = (*secdesc_ctr)->sec->dacl;
+               int i;
+
+               DEBUG(10, ("secdesc_ctr for %s has %d aces:\n", 
+                          printername, acl->num_aces));
+
+               for (i = 0; i < acl->num_aces; i++) {
+                       fstring sid_str;
+
+                       sid_to_string(sid_str, &acl->ace[i].sid);
+
+                       DEBUG(10, ("%s %d %d 0x%08x\n", sid_str,
+                                  acl->ace[i].type, acl->ace[i].flags, 
+                                  acl->ace[i].info.mask)); 
+               }
+       }
+
+       prs_mem_free(&ps);
+       talloc_destroy(mem_ctx);
+       return True;
+}
 
 /* error code:
        0: everything OK
@@ -1596,7 +2967,7 @@ void init_devicemode(NT_DEVICEMODE *nt_devmode)
        Why ? Simply because it's easier and it makes sense !
        
        Now explanation: You have 3 printers behind your samba server,
-       2 of them are the same make and model (laser A and B). But laser B 
+       2 of them are the same make and model (laser A and B). But laser B
        has an 3000 sheet feeder and laser A doesn't such an option.
        Your third printer is an old dot-matrix model for the accounting :-).
        
@@ -1611,10 +2982,129 @@ void init_devicemode(NT_DEVICEMODE *nt_devmode)
                NTdriver_printer model X
                NTdriver_printer model Y
 
-jfm: I should use this comment for the text file to explain 
+jfm: I should use this comment for the text file to explain
        same thing for the forms BTW.
        Je devrais mettre mes commentaires en francais, ca serait mieux :-)
 
 */
 
+/* Convert generic access rights to printer object specific access rights.
+   It turns out that NT4 security descriptors use generic access rights and
+   NT5 the object specific ones. */
+
+void map_printer_permissions(SEC_DESC *sd)
+{
+       int i;
+
+       for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+               se_map_generic(&sd->dacl->ace[i].info.mask,
+                              &printer_generic_mapping);
+       }
+}
+
+/****************************************************************************
+ Check a user has permissions to perform the given operation.  We use some
+ constants defined in include/rpc_spoolss.h that look relevant to check
+ the various actions we perform when checking printer access.
+
+   PRINTER_ACCESS_ADMINISTER:
+       print_queue_pause, print_queue_resume, update_printer_sec,
+       update_printer, spoolss_addprinterex_level_2,
+       _spoolss_setprinterdata
+
+   PRINTER_ACCESS_USE:
+       print_job_start
+
+   PRINTER_ACCESS_ADMINISTER (should really be JOB_ACCESS_ADMINISTER):
+       print_job_delete, print_job_pause, print_job_resume,
+       print_queue_purge
+
+ ****************************************************************************/
+BOOL print_access_check(struct current_user *user, int snum, int access_type)
+{
+       SEC_DESC_BUF *secdesc = NULL;
+       uint32 access_granted, status;
+       BOOL result;
+       char *pname;
+       extern struct current_user current_user;
+       
+       /* If user is NULL then use the current_user structure */
+
+       if (!user) user = &current_user;
+
+       /* Always allow root or printer admins to do anything */
+
+       if (user->uid == 0 ||
+           user_in_list(uidtoname(user->uid), lp_printer_admin(snum))) {
+               return True;
+       }
+
+       /* Get printer name */
+
+       pname = PRINTERNAME(snum);
+
+       if (!pname || !*pname)
+               pname = SERVICE(snum);
+
+       if (!pname || !*pname) {
+               errno = EACCES;
+               return False;
+       }
+
+       /* Get printer security descriptor */
+
+       nt_printing_getsec(pname, &secdesc);
+       
+       map_printer_permissions(secdesc->sec);
+
+       result = se_access_check(secdesc->sec, user, access_type,
+                                &access_granted, &status);
+
+       /* Check access */
+       
+       DEBUG(4, ("access check was %s\n", result ? "SUCCESS" : "FAILURE"));
+       
+       /* Free mallocated memory */
+
+       free_sec_desc_buf(&secdesc);
+
+       if (!result)
+               errno = EACCES;
+
+       return result;
+}
+
+/****************************************************************************
+ Check the time parameters allow a print operation.
+*****************************************************************************/
+
+BOOL print_time_access_check(int snum)
+{
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       BOOL ok = False;
+       time_t now = time(NULL);
+       struct tm *t;
+       uint32 mins;
+
+       if (get_a_printer(&printer, 2, lp_servicename(snum))!=0)
+               return False;
+
+       if (printer->info_2->starttime == 0 && printer->info_2->untiltime == 0)
+               ok = True;
+
+       t = gmtime(&now);
+       mins = (uint32)t->tm_hour*60 + (uint32)t->tm_min;
+
+       if (mins >= printer->info_2->starttime && mins <= printer->info_2->untiltime)
+               ok = True;
+
+       free_a_printer(&printer, 2);
+
+       if (!ok)
+               errno = EACCES;
+
+       return ok;
+}
+
 
+#undef OLD_NTDOMAIN