an attempt to get the handling of fields in printer info structures
[sfrench/samba-autobuild/.git] / source / printing / nt_printing.c
index 7fe21dc7e71d13709a1b62858078a8e9bd90aa5b..3712cfbb1447ca0527c712d71d7e840c4317f415 100644 (file)
@@ -35,11 +35,12 @@ static TDB_CONTEXT *tdb; /* used for driver files */
 
 #define DATABASE_VERSION 1
 
-/* we need to have a small set of default forms to support our
-   default printer */
+/* 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},
-       {"A4", 0x2, 0x3354f, 0x4884e, 0x0, 0x0, 0x3354f, 0x4884e}
 };
 
 
@@ -49,6 +50,7 @@ open the NT printing tdb
 BOOL nt_printing_init(void)
 {
        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);
@@ -60,12 +62,12 @@ BOOL nt_printing_init(void)
        local_pid = sys_getpid();
 
        /* handle a Samba upgrade */
-       tdb_writelock(tdb);
-       if (tdb_fetch_int(tdb, "INFO/version") != DATABASE_VERSION) {
+       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, "INFO/version", DATABASE_VERSION);
+               tdb_store_int(tdb, vstring, DATABASE_VERSION);
        }
-       tdb_writeunlock(tdb);
+       tdb_unlock_bystring(tdb, vstring);
 
        return True;
 }
@@ -79,6 +81,7 @@ int get_ntforms(nt_forms_struct **list)
        TDB_DATA kbuf, newkey, dbuf;
        nt_forms_struct form;
        int ret;
+       int i;
        int n = 0;
 
        for (kbuf = tdb_firstkey(tdb); 
@@ -90,15 +93,18 @@ int get_ntforms(nt_forms_struct **list)
                if (!dbuf.dptr) continue;
 
                fstrcpy(form.name, kbuf.dptr+strlen(FORMS_PREFIX));
-               ret = tdb_unpack(dbuf.dptr, dbuf.dsize, "ddddddd",
-                                &form.flag, &form.width, &form.length, &form.left,
+               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;
 
-               *list = Realloc(*list, sizeof(nt_forms_struct)*(n+1));
-               (*list)[n] = form;
-               n++;
+               /* 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;
+               }
+               (*list)[i] = form;
        }
 
        /* we should never return a null forms list or NT gets unhappy */
@@ -122,8 +128,9 @@ int write_ntforms(nt_forms_struct **list, int number)
        int i;
 
        for (i=0;i<number;i++) {
-               len = tdb_pack(buf, sizeof(buf), "ddddddd", 
-                              (*list)[i].flag, (*list)[i].width, (*list)[i].length,
+               /* 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;
@@ -155,7 +162,7 @@ BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count)
 
        update=False;
        
-       unistr2_to_ascii(form_name, &(form->name), sizeof(form_name)-1);
+       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)))
@@ -169,7 +176,7 @@ BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count)
        {
                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)++;
        }
        
@@ -184,6 +191,53 @@ BOOL add_a_form(nt_forms_struct **list, const FORM *form, int *count)
        return True;
 }
 
+/****************************************************************************
+ 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);
+       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 
 ****************************************************************************/
@@ -285,18 +339,170 @@ BOOL get_short_archi(char *short_archi, char *long_archi)
        return TRUE;
 }
 
+/****************************************************************************
+Determine the correct cVersion associated with an architecture and driver
+****************************************************************************/
+static uint32 get_correct_cversion(fstring architecture, fstring driverpath_in)
+{
+       int  fd = -1;
+       int  service;
+       int  cversion;
+       ssize_t  byte_count;
+       char buf[PE_HEADER_SIZE];
+       pstring driverpath;
+
+       /* 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;
+       }
+       
+       /* Open the driver file (Portable Executable format) and determine the
+        * deriver the cversion.
+        */
+       if ((service = find_service("print$")) == -1) {
+               DEBUG(3,("get_correct_cversion: Can't find print$ service\n"));
+               goto error_exit;
+       }
+
+       slprintf(driverpath, sizeof(driverpath), "%s/%s/%s",
+                        lp_pathname(service), architecture, driverpath_in);
+
+       dos_to_unix(driverpath, True);
+
+       if ((fd = sys_open(driverpath, O_RDONLY, 0)) == -1) {
+               DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = %d\n",
+                               driverpath, errno));
+               goto error_exit;
+       }
+        
+       if ((byte_count = read(fd, 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));
+               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)));
+               goto error_exit;
+       }
+
+       /* Skip OEM header (if any) and the DOS stub to start of Windows header */
+       if (sys_lseek(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));
+               goto error_exit;
+       }
+
+       if ((byte_count = read(fd, 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));
+               goto error_exit;
+       }
+       close(fd);
+
+       /* 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)));
+                                       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)));
+                       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)));
+                                       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)));
+                       goto error_exit;
+               }
+
+       } else {
+               DEBUG(6,("get_correct_cversion: Unknown file format [%s], signature = 0x%x\n",
+                               driverpath, IVAL(buf,PE_HEADER_SIGNATURE_OFFSET)));
+               goto error_exit;
+       }
+
+       DEBUG(10,("get_correct_cversion: Driver file [%s] cversion = %d\n",
+                       driverpath, cversion));
+       return cversion;
+
+
+       error_exit:
+               if(fd != -1)
+                       close(fd);
+               return -1;
+}
+
 /****************************************************************************
 ****************************************************************************/
-static void clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
+static uint32 clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
 {
        fstring architecture;
        fstring new_name;
        char *p;
        int i;
+
+       /* 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.
+        * 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)
@@ -305,17 +511,21 @@ static void clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *drive
         *      NT 4: cversion=2
         *      NT2K: cversion=3
         */
+       if ((driver->cversion = get_correct_cversion(architecture,
+                                                                                       driver->driverpath)) == -1)
+               return NT_STATUS_FILE_INVALID;     /* Not the best error. Fix JRR */
 
-       get_short_archi(architecture, driver->environment);
-
-       /* if it's Windows 95/98, we keep the version at 0
-        * jfmxxx: I need to redo that more correctly for NT2K.
-        */
+       return NT_STATUS_NO_PROBLEMO;
+}
         
-       if (StrCaseCmp(driver->environment, "Windows 4.0")==0)
-               driver->cversion=0;
-       else
-               driver->cversion=2;
+/****************************************************************************
+****************************************************************************/
+static uint32 clean_up_driver_struct_level_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver)
+{
+       fstring architecture;
+       fstring new_name;
+       char *p;
+       int i;
 
        /* clean up the driver name.
         * we can get .\driver.dll
@@ -350,42 +560,77 @@ static void clean_up_driver_struct_level_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *drive
                        }
                }
        }
-}
 
-/****************************************************************************
-****************************************************************************/
-static void clean_up_driver_struct_level_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver)
-{
+       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)) == -1)
+               return NT_STATUS_FILE_INVALID;     /* Not the best error. Fix JRR */
 
+       return NT_STATUS_NO_PROBLEMO;
 }
 
 /****************************************************************************
 ****************************************************************************/
-void clean_up_driver_struct(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, uint32 level)
+uint32 clean_up_driver_struct(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, uint32 level)
 {
        switch (level) {
                case 3:
                {
                        NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver;
                        driver=driver_abstract.info_3;
-                       clean_up_driver_struct_level_3(driver);
+                       return clean_up_driver_struct_level_3(driver);
                        break;
                }
                case 6:
                {
                        NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver;
                        driver=driver_abstract.info_6;
-                       clean_up_driver_struct_level_6(driver);
+                       return clean_up_driver_struct_level_6(driver);
                        break;
                }
+               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;
+}
+
+
 /****************************************************************************
 ****************************************************************************/
-BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract, uint32 level, struct current_user *user)
+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;
@@ -400,31 +645,43 @@ BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
        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;
-       
-       get_short_archi(architecture, driver->environment);
+       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;
+       }
 
-       /* connect to the print$ share under the same account as the user connected to the rpc pipe */  
-       fstrcpy(user_name, uidtoname(user->uid));
-       DEBUG(10,("move_driver_to_download_area: uid %d -> user %s\n", (int)user->uid, user_name));
+       get_short_archi(architecture, driver->environment);
 
        become_root();
-       smb_pass = getsmbpwnam(user_name);
+       smb_pass = getsmbpwuid(user->uid);
        if(smb_pass == NULL) {
-               DEBUG(0,("move_driver_to_download_area: Unable to get smbpasswd entry for user %s\n",
-                               user_name ));
+               DEBUG(0,("move_driver_to_download_area: Unable to get smbpasswd 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, smb_pass->smb_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;
        }
 
@@ -461,6 +718,8 @@ BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
         */
 
        DEBUG(5,("Moving file now !\n"));
+
+       if (driver->driverpath && strlen(driver->driverpath)) {
        slprintf(old_name, sizeof(old_name), "%s\\%s", architecture, driver->driverpath);       
        slprintf(new_name, sizeof(new_name), "%s\\%s", new_dir, driver->driverpath);    
        if ((outsize = rename_internals(conn, inbuf, outbuf, old_name, new_name, True)) != 0) {
@@ -468,9 +727,12 @@ BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
                                old_name, new_name ));
                close_cnum(conn, user->vuid);
                pop_sec_ctx();
+               *perr = (uint32)SVAL(outbuf,smb_err);
                return False;
        }
+       }
 
+       if (driver->datafile && strlen(driver->datafile)) {
        if (!strequal(driver->datafile, driver->driverpath)) {
                slprintf(old_name, sizeof(old_name), "%s\\%s", architecture, driver->datafile); 
                slprintf(new_name, sizeof(new_name), "%s\\%s", new_dir, driver->datafile);      
@@ -479,10 +741,13 @@ BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
                                        old_name, new_name ));
                        close_cnum(conn, user->vuid);
                        pop_sec_ctx();
+                       *perr = (uint32)SVAL(outbuf,smb_err);
                        return False;
                }
        }
+       }
 
+       if (driver->configfile && strlen(driver->configfile)) {
        if (!strequal(driver->configfile, driver->driverpath) &&
                !strequal(driver->configfile, driver->datafile)) {
                slprintf(old_name, sizeof(old_name), "%s\\%s", architecture, driver->configfile);       
@@ -492,10 +757,13 @@ BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
                                old_name, new_name ));
                        close_cnum(conn, user->vuid);
                        pop_sec_ctx();
+                       *perr = (uint32)SVAL(outbuf,smb_err);
                        return False;
                }
        }
+       }
 
+       if (driver->helpfile && strlen(driver->helpfile)) {
        if (!strequal(driver->helpfile, driver->driverpath) &&
                        !strequal(driver->helpfile, driver->datafile) &&
                        !strequal(driver->helpfile, driver->configfile)) {
@@ -506,9 +774,11 @@ BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
                                old_name, new_name ));
                        close_cnum(conn, user->vuid);
                        pop_sec_ctx();
+                       *perr = (uint32)SVAL(outbuf,smb_err);
                        return False;
                }
        }
+       }
 
        if (driver->dependentfiles) {
                for (i=0; *driver->dependentfiles[i]; i++) {
@@ -530,10 +800,11 @@ BOOL move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract,
                                                old_name, new_name ));
                                        close_cnum(conn, user->vuid);
                                        pop_sec_ctx();
+                                       *perr = (uint32)SVAL(outbuf,smb_err);
                                        return False;
                                }
                        }
-               NextDriver:
+               NextDriver: ;
                }
        }
 
@@ -587,6 +858,8 @@ static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
 
        slprintf(key, sizeof(key), "%s%s/%d/%s", DRIVERS_PREFIX, architecture, driver->cversion, driver->name);
 
+       DEBUG(5,("add_a_printer_driver_3: Adding driver with key %s\n", key ));
+
        buf = NULL;
        len = buflen = 0;
 
@@ -624,6 +897,9 @@ static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
        
        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;
 }
@@ -636,6 +912,7 @@ static uint32 add_a_printer_driver_6(NT_PRINTER_DRIVER_INFO_LEVEL_6 *driver)
 
        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);
@@ -955,14 +1232,12 @@ uint32 del_a_printer(char *sharename)
 
 /****************************************************************************
 ****************************************************************************/
-static uint32 add_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
+static uint32 update_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
 {
        pstring key;
        char *buf;
        int buflen, len, ret;
        TDB_DATA kbuf, dbuf;
-       NTTIME time_nt;
-       time_t time_unix = time(NULL);
        
        /* 
         * in addprinter: no servername and the printer is the name
@@ -989,10 +1264,6 @@ static uint32 add_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
         * behind a SAMBA share.
         */
 
-       unix_to_nt_time(&time_nt, time_unix);
-       info->changeid=time_nt.low;
-       info->c_setprinter++;
-
        buf = NULL;
        buflen = 0;
 
@@ -1160,9 +1431,8 @@ NT_DEVICEMODE *construct_nt_devicemode(const fstring default_devicename)
 
        ZERO_STRUCTP(nt_devmode);
 
-       snprintf(adevice, sizeof(adevice), "\\\\%s\\%s", global_myname, default_devicename);
-       fstrcpy(nt_devmode->devicename, adevice);
-       
+       safe_strcpy(adevice, default_devicename, sizeof(adevice));
+       fstrcpy(nt_devmode->devicename, adevice);       
        
        fstrcpy(nt_devmode->formname, "Letter");
 
@@ -1180,7 +1450,7 @@ NT_DEVICEMODE *construct_nt_devicemode(const fstring default_devicename)
        nt_devmode->scale            = 0x64;
        nt_devmode->copies           = 01;
        nt_devmode->defaultsource    = BIN_FORMSOURCE;
-       nt_devmode->printquality     = 0x0258;
+       nt_devmode->printquality     = RES_HIGH;           /* 0x0258 */
        nt_devmode->color            = COLOR_MONOCHROME;
        nt_devmode->duplex           = DUP_SIMPLEX;
        nt_devmode->yresolution      = 0;
@@ -1392,8 +1662,10 @@ static uint32 get_a_printer_2_default(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstrin
 
        snum = lp_servicenumber(sharename);
 
-       fstrcpy(info.servername, global_myname);
-       fstrcpy(info.printername, sharename);
+       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));
        pstrcpy(info.comment, "");
@@ -1402,12 +1674,14 @@ static uint32 get_a_printer_2_default(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstrin
 
        info.attributes = PRINTER_ATTRIBUTE_SHARED   \
                         | PRINTER_ATTRIBUTE_LOCAL  \
-                        | PRINTER_ATTRIBUTE_RAW_ONLY ;            /* attributes */
+                        | PRINTER_ATTRIBUTE_RAW_ONLY \
+                        | PRINTER_ATTRIBUTE_QUEUED ;            /* attributes */
 
        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 ((info.devmode = construct_nt_devicemode(info.printername)) == NULL)
                goto fail;
@@ -1449,12 +1723,8 @@ static uint32 get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstring sharen
        kbuf.dsize = strlen(key)+1;
 
        dbuf = tdb_fetch(tdb, kbuf);
-#if 1 /* JRATEST */
        if (!dbuf.dptr)
                return get_a_printer_2_default(info_ptr, sharename);
-#else
-       if (!dbuf.dptr) return 1;
-#endif
 
        len += tdb_unpack(dbuf.dptr+len, dbuf.dsize-len, "dddddddddddfffffPfffff",
                        &info.attributes,
@@ -1486,9 +1756,7 @@ static uint32 get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstring sharen
        len += unpack_devicemode(&info.devmode,dbuf.dptr+len, dbuf.dsize-len);
        len += unpack_specifics(&info.specific,dbuf.dptr+len, dbuf.dsize-len);
 
-#if 1 /* JRATEST */
        nt_printing_getsec(sharename, &info.secdesc_buf);
-#endif /* JRATEST */
 
        safe_free(dbuf.dptr);
        *info_ptr=memdup(&info, sizeof(info));
@@ -1583,7 +1851,36 @@ void get_printer_subst_params(int snum, fstring *printername, fstring *sharename
  */
 
 /****************************************************************************
+ Modify a printer. This is called from SETPRINTERDATA/DELETEPRINTERDATA.
+****************************************************************************/
+
+uint32 mod_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
+{
+       uint32 success;
+       
+       dump_a_printer(printer, level); 
+       
+       switch (level)
+       {
+               case 2:
+               {
+                       printer.info_2->c_setprinter++;
+                       success=update_a_printer_2(printer.info_2);
+                       break;
+               }
+               default:
+                       success=1;
+                       break;
+       }
+       
+       return (success);
+}
+
+/****************************************************************************
+ 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;
@@ -1594,7 +1891,17 @@ uint32 add_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
        {
                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++;
+                       success=update_a_printer_2(printer.info_2);
                        break;
                }
                default:
@@ -1724,9 +2031,7 @@ uint32 get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level,
        {
                case 3: 
                {
-                       success=get_a_printer_driver_3(&(driver->info_3), 
-                                                      printername,
-                                                      architecture, version);
+                       success=get_a_printer_driver_3(&driver->info_3, printername, architecture, version);
                        break;
                }
                default:
@@ -1734,7 +2039,8 @@ uint32 get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level,
                        break;
        }
        
-       if (success == 0) dump_a_printer_driver(*driver, level);
+       if (success == 0)
+               dump_a_printer_driver(*driver, level);
        return (success);
 }
 
@@ -1833,7 +2139,11 @@ BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level,
                
        while (param != NULL)
        {
+#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;
                        
@@ -1865,69 +2175,22 @@ BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level,
 
 uint32 nt_printing_setsec(char *printername, SEC_DESC_BUF *secdesc_ctr)
 {
-       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;
-
-        /* 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. */
-
-       if (!secdesc_ctr->sec->owner_sid || !secdesc_ctr->sec->grp_sid) {
-               DOM_SID *owner_sid, *group_sid;
-               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;
-
-               /* Make a deep copy of the security descriptor */
-
-               psd = make_sec_desc(secdesc_ctr->sec->revision,
-                                   secdesc_ctr->sec->type,
-                                   owner_sid, group_sid,
-                                   secdesc_ctr->sec->sacl,
-                                   secdesc_ctr->sec->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;
-       }
+       if (mem_ctx == NULL) return False;
 
        /* Store the security descriptor in a tdb */
 
-       prs_init(&ps, (uint32)sec_desc_size(new_secdesc_ctr->sec) + 
+       prs_init(&ps, (uint32)sec_desc_size(secdesc_ctr->sec) + 
                 sizeof(SEC_DESC_BUF), 4, mem_ctx, MARSHALL);
 
-       if (!sec_io_desc_buf("nt_printing_setsec", &new_secdesc_ctr, 
-                            &ps, 1)) {
+       if (!sec_io_desc_buf("nt_printing_setsec", &secdesc_ctr, &ps, 1)) {
                status = ERROR_INVALID_FUNCTION;
-               goto out;
+               goto done;
        }
 
        slprintf(key, sizeof(key), "SECDESC/%s", printername);
@@ -1941,16 +2204,11 @@ uint32 nt_printing_setsec(char *printername, SEC_DESC_BUF *secdesc_ctr)
 
        /* Free mallocated memory */
 
out:
-       free_sec_desc_buf(&old_secdesc_ctr);
done:
+       prs_mem_free(&ps);
 
-       if (new_secdesc_ctr != secdesc_ctr) {
-               free_sec_desc_buf(&new_secdesc_ctr);
-       }
+       if (mem_ctx) talloc_destroy(mem_ctx);
 
-       prs_mem_free(&ps);
-       if (mem_ctx)
-               talloc_destroy(mem_ctx);
        return status;
 }
 
@@ -2146,30 +2404,47 @@ jfm: I should use this comment for the text file to explain
 */
 
 /****************************************************************************
- Check a user has permissions to perform the given operation 
+ 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
+
+   JOB_ACCESS_ADMINISTER:
+       print_job_delete, print_job_pause, print_job_resume,
+       print_queue_purge
 
-   if user is NULL then use the current_user structure
  ****************************************************************************/
-BOOL print_access_check(struct current_user *user, int snum,
-                       uint32 required_access)
+BOOL print_access_check(struct current_user *user, int snum, int access_type)
 {
        SEC_DESC_BUF *secdesc = NULL;
-       uint32 access_granted, status;
+       uint32 access_granted, status, required_access = 0;
        BOOL result;
        char *pname;
        int i;
        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 ||
+       /* 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);
 
@@ -2179,8 +2454,36 @@ BOOL print_access_check(struct current_user *user, int snum,
        }
 
        /* Get printer security descriptor */
+
        nt_printing_getsec(pname, &secdesc);
 
+       /* Check against NT4 ACE mask values.  From observation these
+          values are:
+
+              Access Type       ACE Mask    Constant
+              -------------------------------------
+              Full Control      0x10000000  PRINTER_ACE_FULL_CONTROL
+              Print             0xe0000000  PRINTER_ACE_PRINT
+              Manage Documents  0x00020000  PRINTER_ACE_MANAGE_DOCUMENTS
+       */
+
+       switch (access_type) {
+       case PRINTER_ACCESS_USE:
+               required_access = PRINTER_ACE_PRINT;
+               break;
+       case PRINTER_ACCESS_ADMINISTER:
+               required_access = PRINTER_ACE_MANAGE_DOCUMENTS | 
+                       PRINTER_ACE_PRINT;
+               break;
+       case JOB_ACCESS_ADMINISTER:
+               required_access = PRINTER_ACE_MANAGE_DOCUMENTS;
+               break;
+       default:
+               DEBUG(0, ("invalid value passed to print_access_check()\n"));
+               result = False;
+               goto done;
+       }       
+
        /* The ACE for Full Control in a printer security descriptor
           doesn't seem to map properly to the access checking model.  For
           it to work properly it should be the logical OR of all the other
@@ -2192,16 +2495,6 @@ BOOL print_access_check(struct current_user *user, int snum,
           performing the access check.  I'm sure there is a better way to
           do this! */
 
-       /* You forgot to also change the *required access* from PRINTER_ACE_FULL_CONTROL
-               to PRINTER_ACE_MANAGE_DOCUMENTS | PRINTER_ACE_PRINT before doing the check.
-               This took me 3 hours to find !!!!! JRA.
-       */
-
-       if (required_access & PRINTER_ACE_FULL_CONTROL) {
-               required_access |= (PRINTER_ACE_MANAGE_DOCUMENTS | PRINTER_ACE_PRINT);
-               required_access &= ~PRINTER_ACE_FULL_CONTROL;
-       }
-
        if (secdesc && secdesc->sec && secdesc->sec->dacl &&
            secdesc->sec->dacl->ace) {
                for(i = 0; i < secdesc->sec->dacl->num_aces; i++) {
@@ -2214,14 +2507,46 @@ BOOL print_access_check(struct current_user *user, int snum,
                }
        }
 
-       /* Check access */
+       if ((result = se_access_check(secdesc->sec, user, required_access, 
+                                     &access_granted, &status))) {
+               goto done;
+       }
+
+       /* Check against NT5 ACE mask values.  From observation these
+          values are:
+
+              Access Type       ACE Mask    Constant
+              -------------------------------------
+              Full Control      0x000f000c  PRINTER_ACE_NT5_FULL_CONTROL
+              Print             0x00020008  PRINTER_ACE_NT5_PRINT
+              Manage Documents  0x00020000  PRINTER_ACE_NT5_MANAGE_DOCUMENTS
+
+          NT5 likes to rewrite the security descriptor and change the ACE
+          masks from NT4 format to NT5 format making them unreadable by
+          NT4 clients. */
+
+       switch (access_type) {
+       case PRINTER_ACCESS_USE:
+               required_access = PRINTER_ACE_NT5_PRINT;
+               break;
+       case PRINTER_ACCESS_ADMINISTER:
+               required_access = PRINTER_ACE_NT5_FULL_CONTROL;
+               break;
+       case JOB_ACCESS_ADMINISTER:
+               required_access = PRINTER_ACE_NT5_MANAGE_DOCUMENTS;
+               break;
+       }       
 
        result = se_access_check(secdesc->sec, user, required_access, 
                                 &access_granted, &status);
 
+       /* Check access */
+       
+ done:
        DEBUG(4, ("access check was %s\n", result ? "SUCCESS" : "FAILURE"));
-
+       
        /* Free mallocated memory */
+
        free_sec_desc_buf(&secdesc);
 
        if (!result)
@@ -2256,6 +2581,9 @@ BOOL print_time_access_check(int snum)
 
        free_a_printer(&printer, 2);
 
+       if (!ok)
+               errno = EACCES;
+
        return ok;
 }