RIP BOOL. Convert BOOL -> bool. I found a few interesting
[nivanova/samba-autobuild/.git] / source3 / registry / reg_printing.c
index 145b8230c9800b949886146bd7bec35a549dd9b2..47582c51f7def8d4e8ed361dc3f6bdc4cd4ae986 100644 (file)
@@ -1,11 +1,11 @@
 /* 
  *  Unix SMB/CIFS implementation.
- *  RPC Pipe client / server routines
- *  Copyright (C) Gerald Carter                     2002.
+ *  Virtual Windows Registry Layer
+ *  Copyright (C) Gerald Carter                     2002-2005
  *
  *  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
+ *  the Free Software Foundation; either version 3 of the License, or
  *  (at your option) any later version.
  *  
  *  This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
  *  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.
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /* Implementation of registry virtual views for printing information */
 #include "includes.h"
 
 #undef DBGC_CLASS
-#define DBGC_CLASS DBGC_RPC_SRV
+#define DBGC_CLASS DBGC_REGISTRY
 
-#define MAX_TOP_LEVEL_KEYS     3
+/* registrt paths used in the print_registry[] */
 
-/* some symbolic indexes into the top_level_keys */
+#define KEY_MONITORS           "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/MONITORS"
+#define KEY_FORMS              "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/FORMS"
+#define KEY_CONTROL_PRINTERS   "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/PRINTERS"
+#define KEY_ENVIRONMENTS       "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/ENVIRONMENTS"
+#define KEY_CONTROL_PRINT      "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT"
+#define KEY_WINNT_PRINTERS     "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PRINT/PRINTERS"
+#define KEY_WINNT_PRINT                "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PRINT"
+#define KEY_PORTS              "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PORTS"
 
-#define KEY_INDEX_ENVIR                0
-#define KEY_INDEX_FORMS                1
-#define KEY_INDEX_PRINTER      2
-
-static char *top_level_keys[MAX_TOP_LEVEL_KEYS] = { 
-       "Environments", 
-       "Forms",
-       "Printers" 
+/* callback table for various registry paths below the ones we service in this module */
+       
+struct reg_dyn_tree {
+       /* full key path in normalized form */
+       const char *path;
+       
+       /* callbscks for fetch/store operations */
+       int ( *fetch_subkeys) ( const char *path, REGSUBKEY_CTR *subkeys );
+       bool (*store_subkeys) ( const char *path, REGSUBKEY_CTR *subkeys );
+       int  (*fetch_values)  ( const char *path, REGVAL_CTR *values );
+       bool (*store_values)  ( const char *path, REGVAL_CTR *values );
 };
 
+/*********************************************************************
+ *********************************************************************
+ ** Utility Functions
+ *********************************************************************
+ *********************************************************************/
 
-/**********************************************************************
- It is safe to assume that every registry path passed into on of 
- the exported functions here begins with KEY_PRINTING else
- these functions would have never been called.  This is a small utility
- function to strip the beginning of the path and make a copy that the 
- caller can modify.  Note that the caller is responsible for releasing
- the memory allocated here.
+/***********************************************************************
+ simple function to prune a pathname down to the basename of a file 
  **********************************************************************/
-
-static char* trim_reg_path( char *path )
+static char* dos_basename ( char *path )
 {
        char *p;
-       uint16 key_len = strlen(KEY_PRINTING);
        
-       /* 
-        * sanity check...this really should never be True.
-        * It is only here to prevent us from accessing outside
-        * the path buffer in the extreme case.
-        */
+       if ( !(p = strrchr( path, '\\' )) )
+               p = path;
+       else
+               p++;
+               
+       return p;
+}
+
+/*********************************************************************
+ *********************************************************************
+ ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/FORMS"
+ *********************************************************************
+ *********************************************************************/
+
+static int key_forms_fetch_keys( const char *key, REGSUBKEY_CTR *subkeys )
+{
+       char *p = reg_remaining_path( key + strlen(KEY_FORMS) );
        
-       if ( strlen(path) < key_len ) {
-               DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path));
-               DEBUG(0,("trim_reg_path: KEY_PRINTING => [%s]!\n", KEY_PRINTING));
-               return NULL;
-       }
+       /* no keys below Forms */
        
+       if ( p )
+               return -1;
+               
+       return 0;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int key_forms_fetch_values( const char *key, REGVAL_CTR *values )
+{
+       uint32          data[8];
+       int             i, num_values, form_index = 1;
+       nt_forms_struct *forms_list = NULL;
+       nt_forms_struct *form;
+               
+       DEBUG(10,("print_values_forms: key=>[%s]\n", key ? key : "NULL" ));
        
-       p = path + strlen( KEY_PRINTING );
+       num_values = get_ntforms( &forms_list );
+               
+       DEBUG(10,("hive_forms_fetch_values: [%d] user defined forms returned\n",
+               num_values));
+
+       /* handle user defined forms */
+                               
+       for ( i=0; i<num_values; i++ ) {
+               form = &forms_list[i];
+                       
+               data[0] = form->width;
+               data[1] = form->length;
+               data[2] = form->left;
+               data[3] = form->top;
+               data[4] = form->right;
+               data[5] = form->bottom;
+               data[6] = form_index++;
+               data[7] = form->flag;
+                       
+               regval_ctr_addvalue( values, form->name, REG_BINARY, (char*)data, sizeof(data) );       
+       }
+               
+       SAFE_FREE( forms_list );
+       forms_list = NULL;
+               
+       /* handle built-on forms */
+               
+       num_values = get_builtin_ntforms( &forms_list );
+               
+       DEBUG(10,("print_subpath_values_forms: [%d] built-in forms returned\n",
+               num_values));
+                       
+       for ( i=0; i<num_values; i++ ) {
+               form = &forms_list[i];
+                       
+               data[0] = form->width;
+               data[1] = form->length;
+               data[2] = form->left;
+               data[3] = form->top;
+               data[4] = form->right;
+               data[5] = form->bottom;
+               data[6] = form_index++;
+               data[7] = form->flag;
+                                       
+               regval_ctr_addvalue( values, form->name, REG_BINARY, (char*)data, sizeof(data) );
+       }
+               
+       SAFE_FREE( forms_list );
        
-       if ( *p == '\\' )
-               p++;
+       return regval_ctr_numvals( values );
+}
+
+/*********************************************************************
+ *********************************************************************
+ ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/PRINTERS"
+ ** "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PRINT/PRINTERS"
+ *********************************************************************
+ *********************************************************************/
+
+/*********************************************************************
+ strip off prefix for printers key.  DOes return a pointer to static 
+ memory.
+ *********************************************************************/
+
+static char* strip_printers_prefix( const char *key )
+{
+       char *subkeypath;
+       pstring path;
        
-       if ( *p )
-               return strdup(p);
+       pstrcpy( path, key );
+       normalize_reg_path( path );
+
+       /* normalizing the path does not change length, just key delimiters and case */
+
+       if ( strncmp( path, KEY_WINNT_PRINTERS, strlen(KEY_WINNT_PRINTERS) ) == 0 )
+               subkeypath = reg_remaining_path( key + strlen(KEY_WINNT_PRINTERS) );
        else
-               return NULL;
+               subkeypath = reg_remaining_path( key + strlen(KEY_CONTROL_PRINTERS) );
+               
+       return subkeypath;
 }
 
-/**********************************************************************
- handle enumeration of subkeys below KEY_PRINTING\Environments
+/*********************************************************************
  *********************************************************************/
  
-static int print_subpath_environments( char *key, REGSUBKEY_CTR *subkeys )
+static int key_printers_fetch_keys( const char *key, REGSUBKEY_CTR *subkeys )
 {
-       char *environments[] = {
-               "Windows 4.0",
-               "Windows NT x86",
-               "Windows NT R4000",
-               "Windows NT Alpha_AXP",
-               "Windows NT PowerPC",
-               NULL };
-       fstring *drivers = NULL;
-       int i, env_index, num_drivers;
-       BOOL valid_env = False;
-       char *base, *new_path;
-       char *keystr;
-       char *key2 = NULL;
-       int num_subkeys = -1;
-
-       DEBUG(10,("print_subpath_environments: key=>[%s]\n", key ? key : "NULL" ));
+       int n_services = lp_numservices();      
+       int snum;
+       fstring sname;
+       int i;
+       int num_subkeys = 0;
+       char *printers_key;
+       char *printername, *printerdatakey;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       fstring *subkey_names = NULL;
        
-       /* listed architectures of installed drivers */
+       DEBUG(10,("key_printers_fetch_keys: key=>[%s]\n", key ? key : "NULL" ));
        
-       if ( !key ) 
-       {
-               /* Windows 9x drivers */
-               
-               if ( get_ntdrivers( &drivers, environments[0], 0 ) )
-                       regsubkey_ctr_addkey( subkeys,  environments[0] );
-               SAFE_FREE( drivers );
-                                       
-               /* Windows NT/2k intel drivers */
+       printers_key = strip_printers_prefix( key );    
+       
+       if ( !printers_key ) {
+               /* enumerate all printers */
                
-               if ( get_ntdrivers( &drivers, environments[1], 2 ) 
-                       || get_ntdrivers( &drivers, environments[1], 3 ) )
-               {
-                       regsubkey_ctr_addkey( subkeys,  environments[1] );
+               for (snum=0; snum<n_services; snum++) {
+                       if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
+                               continue;
+
+                       /* don't report the [printers] share */
+
+                       if ( strequal( lp_servicename(snum), PRINTERS_NAME ) )
+                               continue;
+                               
+                       fstrcpy( sname, lp_servicename(snum) );
+                               
+                       regsubkey_ctr_addkey( subkeys, sname );
                }
-               SAFE_FREE( drivers );
                
-               /* Windows NT 4.0; non-intel drivers */
-               for ( i=2; environments[i]; i++ ) {
-                       if ( get_ntdrivers( &drivers, environments[i], 2 ) )
-                               regsubkey_ctr_addkey( subkeys,  environments[i] );
+               num_subkeys = regsubkey_ctr_numkeys( subkeys );
+               goto done;
+       }
+
+       /* get information for a specific printer */
+       
+       if (!reg_split_path( printers_key, &printername, &printerdatakey )) {
+               return -1;
+       }
+
+       /* validate the printer name */
+
+       for (snum=0; snum<n_services; snum++) {
+               if ( !lp_snum_ok(snum) || !lp_print_ok(snum) )
+                       continue;
+               if (strequal( lp_servicename(snum), printername ) )
+                       break;
+       }
+
+       if ( snum>=n_services
+               || !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) ) 
+       {
+               return -1;
+       }
+
+       num_subkeys = get_printer_subkeys( printer->info_2->data, printerdatakey?printerdatakey:"", &subkey_names );
+       
+       for ( i=0; i<num_subkeys; i++ )
+               regsubkey_ctr_addkey( subkeys, subkey_names[i] );
+       
+       free_a_printer( &printer, 2 );
+                       
+       /* no other subkeys below here */
+
+done:  
+       SAFE_FREE( subkey_names );
+       
+       return num_subkeys;
+}
+
+/**********************************************************************
+ Take a list of names and call add_printer_hook() if necessary
+ Note that we do this a little differently from Windows since the 
+ keyname is the sharename and not the printer name.
+ *********************************************************************/
+
+static bool add_printers_by_registry( REGSUBKEY_CTR *subkeys )
+{
+       int i, num_keys, snum;
+       char *printername;
+       NT_PRINTER_INFO_LEVEL_2 info2;
+       NT_PRINTER_INFO_LEVEL printer;
+       
+       ZERO_STRUCT( info2 );
+       printer.info_2 = &info2;
+       
+       num_keys = regsubkey_ctr_numkeys( subkeys );
+       
+       become_root();
+       for ( i=0; i<num_keys; i++ ) {
+               printername = regsubkey_ctr_specific_key( subkeys, i );
+               snum = find_service( printername );
                
+               /* just verify a valied snum for now */
+               if ( snum == -1 ) {
+                       fstrcpy( info2.printername, printername );
+                       fstrcpy( info2.sharename, printername );
+                       if ( !add_printer_hook( NULL, &printer ) ) {
+                               DEBUG(0,("add_printers_by_registry: Failed to add printer [%s]\n",
+                                       printername));
+                       }       
                }
-               SAFE_FREE( drivers );
+       }
+       unbecome_root();
 
-               num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
-               goto done;
+       return True;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static bool key_printers_store_keys( const char *key, REGSUBKEY_CTR *subkeys )
+{
+       char *printers_key;
+       char *printername, *printerdatakey;
+       NT_PRINTER_INFO_LEVEL *printer = NULL;
+       int i, num_subkeys, num_existing_keys;
+       char *subkeyname;
+       fstring *existing_subkeys = NULL;
+       
+       printers_key = strip_printers_prefix( key );
+       
+       if ( !printers_key ) {
+               /* have to deal with some new or deleted printer */
+               return add_printers_by_registry( subkeys );
        }
        
-       /* we are dealing with a subkey of "Environments */
+       if (!reg_split_path( printers_key, &printername, &printerdatakey )) {
+               return False;
+       }
        
-       key2 = strdup( key );
-       keystr = key2;
-       reg_split_path( keystr, &base, &new_path );
+       /* lookup the printer */
        
-       /* sanity check */
+       if ( !W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, printername)) ) {
+               DEBUG(0,("key_printers_store_keys: Tried to store subkey for bad printername %s\n", 
+                       printername));
+               return False;
+       }
        
-       for ( env_index=0; environments[env_index]; env_index++ ) {
-               if ( StrCaseCmp( environments[env_index], base ) == 0 ) {
-                       valid_env = True;
-                       break;
+       /* get the top level printer keys */
+       
+       num_existing_keys = get_printer_subkeys( printer->info_2->data, "", &existing_subkeys );
+       
+       for ( i=0; i<num_existing_keys; i++ ) {
+       
+               /* remove the key if it has been deleted */
+               
+               if ( !regsubkey_ctr_key_exists( subkeys, existing_subkeys[i] ) ) {
+                       DEBUG(5,("key_printers_store_keys: deleting key %s\n", 
+                               existing_subkeys[i]));
+                       delete_printer_key( printer->info_2->data, existing_subkeys[i] );
                }
        }
-               
-       if ( !valid_env )
-               return -1;
 
-       /* enumerate driver versions; environment is environments[env_index] */
+       num_subkeys = regsubkey_ctr_numkeys( subkeys );
+       for ( i=0; i<num_subkeys; i++ ) {
+               subkeyname = regsubkey_ctr_specific_key(subkeys, i);
+               /* add any missing printer keys */
+               if ( lookup_printerkey(printer->info_2->data, subkeyname) == -1 ) {
+                       DEBUG(5,("key_printers_store_keys: adding key %s\n", 
+                               existing_subkeys[i]));
+                       if ( add_new_printer_key( printer->info_2->data, subkeyname ) == -1 ) {
+                               SAFE_FREE( existing_subkeys );
+                               return False;
+                       }
+               }
+       }
        
-       if ( !new_path ) {
-               switch ( env_index ) {
-                       case 0: /* Win9x */
-                               if ( get_ntdrivers( &drivers, environments[0], 0 ) ) {
-                                       regsubkey_ctr_addkey( subkeys, "0" );
-                                       SAFE_FREE( drivers );
-                               }
-                               break;
-                       case 1: /* Windows NT/2k - intel */
-                               if ( get_ntdrivers( &drivers, environments[1], 2 ) ) {
-                                       regsubkey_ctr_addkey( subkeys, "2" );
-                                       SAFE_FREE( drivers );
-                               }
-                               if ( get_ntdrivers( &drivers, environments[1], 3 ) ) {
-                                       regsubkey_ctr_addkey( subkeys, "3" );
-                                       SAFE_FREE( drivers );
-                               }
-                               break;
-                       default: /* Windows NT - nonintel */
-                               if ( get_ntdrivers( &drivers, environments[env_index], 2 ) ) {
-                                       regsubkey_ctr_addkey( subkeys, "2" );
-                                       SAFE_FREE( drivers );
-                               }
-                       
+       /* write back to disk */
+       
+       mod_a_printer( printer, 2 );
+       
+       /* cleanup */
+       
+       if ( printer )
+               free_a_printer( &printer, 2 );
+
+       SAFE_FREE( existing_subkeys );
+
+       return True;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static void fill_in_printer_values( NT_PRINTER_INFO_LEVEL_2 *info2, REGVAL_CTR *values )
+{
+       DEVICEMODE      *devmode;
+       prs_struct      prs;
+       uint32          offset;
+       UNISTR2         data;
+       char            *p;
+       uint32 printer_status = PRINTER_STATUS_OK;
+       
+       regval_ctr_addvalue( values, "Attributes",       REG_DWORD, (char*)&info2->attributes,       sizeof(info2->attributes) );
+       regval_ctr_addvalue( values, "Priority",         REG_DWORD, (char*)&info2->priority,         sizeof(info2->attributes) );
+       regval_ctr_addvalue( values, "ChangeID",         REG_DWORD, (char*)&info2->changeid,         sizeof(info2->changeid) );
+       regval_ctr_addvalue( values, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) );
+       
+       /* lie and say everything is ok since we don't want to call print_queue_length() to get the real status */
+       regval_ctr_addvalue( values, "Status",           REG_DWORD, (char*)&printer_status,          sizeof(info2->status) );
+
+       regval_ctr_addvalue( values, "StartTime",        REG_DWORD, (char*)&info2->starttime,        sizeof(info2->starttime) );
+       regval_ctr_addvalue( values, "UntilTime",        REG_DWORD, (char*)&info2->untiltime,        sizeof(info2->untiltime) );
+
+       /* strip the \\server\ from this string */
+       if ( !(p = strrchr( info2->printername, '\\' ) ) )
+               p = info2->printername;
+       else
+               p++;
+       init_unistr2( &data, p, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, info2->location, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Location", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, info2->comment, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Description", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, info2->parameters, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Parameters", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, info2->portname, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Port", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, info2->sharename, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Share Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, info2->drivername, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Printer Driver", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, info2->sepfile, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Separator File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, "WinPrint", UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Print Processor",  REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+       init_unistr2( &data, "RAW", UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Datatype", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
+
+               
+       /* use a prs_struct for converting the devmode and security 
+          descriptor to REG_BINARY */
+       
+       prs_init( &prs, RPC_MAX_PDU_FRAG_LEN, values, MARSHALL);
+
+       /* stream the device mode */
+               
+       if ( (devmode = construct_dev_mode( info2->sharename )) != NULL ) {
+               if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) {
+                       offset = prs_offset( &prs );
+                       regval_ctr_addvalue( values, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset );
                }
+       }
                
-               num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
-               goto done;
+       prs_mem_clear( &prs );
+       prs_set_offset( &prs, 0 );
+               
+       /* stream the printer security descriptor */
+       
+       if ( info2->secdesc_buf &&
+            info2->secdesc_buf->sd &&
+            info2->secdesc_buf->sd_size )  
+       {
+               if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sd, &prs, 0 ) ) {
+                       offset = prs_offset( &prs );
+                       regval_ctr_addvalue( values, "Security", REG_BINARY, prs_data_p(&prs), offset );
+               }
        }
+
+       prs_mem_free( &prs );
+
+       return;         
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int key_printers_fetch_values( const char *key, REGVAL_CTR *values )
+{
+       int             num_values;
+       char            *printers_key;
+       char            *printername, *printerdatakey;
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       NT_PRINTER_DATA *p_data;
+       int             i, key_index;
        
-       /* we finally get to enumerate the drivers */
+       printers_key = strip_printers_prefix( key );    
        
-       keystr = new_path;
-       reg_split_path( keystr, &base, &new_path );
+       /* top level key values stored in the registry has no values */
        
-       if ( !new_path ) {
-               num_drivers = get_ntdrivers( &drivers, environments[env_index], atoi(base) );
-               for ( i=0; i<num_drivers; i++ )
-                       regsubkey_ctr_addkey( subkeys, drivers[i] );
-                       
-               num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
+       if ( !printers_key ) {
+               /* normalize to the 'HKLM\SOFTWARE\...\Print\Printers' key */
+               return regdb_fetch_values( KEY_WINNT_PRINTERS, values );
+       }
+       
+       /* lookup the printer object */
+       
+       if (!reg_split_path( printers_key, &printername, &printerdatakey )) {
+               return -1;
+       }
+       
+       if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) )
+               goto done;
+               
+       if ( !printerdatakey ) {
+               fill_in_printer_values( printer->info_2, values );
                goto done;
        }
+               
+       /* iterate over all printer data keys and fill the regval container */
+       
+       p_data = printer->info_2->data;
+       if ( (key_index = lookup_printerkey( p_data, printerdatakey )) == -1  ) {
+               /* failure....should never happen if the client has a valid open handle first */
+               DEBUG(10,("key_printers_fetch_values: Unknown keyname [%s]\n", printerdatakey));
+               if ( printer )
+                       free_a_printer( &printer, 2 );
+               return -1;
+       }
        
+       num_values = regval_ctr_numvals( p_data->keys[key_index].values );      
+       for ( i=0; i<num_values; i++ )
+               regval_ctr_copyvalue( values, regval_ctr_specific_value(p_data->keys[key_index].values, i) );
+                       
+
 done:
-       SAFE_FREE( key2 );
+       if ( printer )
+               free_a_printer( &printer, 2 );
                
-       return num_subkeys;
+       return regval_ctr_numvals( values );
 }
 
-/***********************************************************************
- simple function to prune a pathname down to the basename of a file 
- **********************************************************************/
-static char* dos_basename ( char *path )
+/**********************************************************************
+ *********************************************************************/
+
+#define REG_IDX_ATTRIBUTES             1
+#define REG_IDX_PRIORITY               2
+#define REG_IDX_DEFAULT_PRIORITY       3
+#define REG_IDX_CHANGEID               4
+#define REG_IDX_STATUS                 5
+#define REG_IDX_STARTTIME              6
+#define REG_IDX_NAME                   7
+#define REG_IDX_LOCATION               8
+#define REG_IDX_DESCRIPTION            9
+#define REG_IDX_PARAMETERS             10
+#define REG_IDX_PORT                   12
+#define REG_IDX_SHARENAME              13
+#define REG_IDX_DRIVER                 14
+#define REG_IDX_SEP_FILE               15
+#define REG_IDX_PRINTPROC              16
+#define REG_IDX_DATATYPE               17
+#define REG_IDX_DEVMODE                        18
+#define REG_IDX_SECDESC                        19
+#define REG_IDX_UNTILTIME              20
+
+struct {
+       const char *name;
+       int index;      
+} printer_values_map[] = {
+       { "Attributes",         REG_IDX_ATTRIBUTES },
+       { "Priority",           REG_IDX_PRIORITY },
+       { "Default Priority",   REG_IDX_DEFAULT_PRIORITY },
+       { "ChangeID",           REG_IDX_CHANGEID },
+       { "Status",             REG_IDX_STATUS },
+       { "StartTime",          REG_IDX_STARTTIME },
+       { "UntilTime",          REG_IDX_UNTILTIME },
+       { "Name",               REG_IDX_NAME },
+       { "Location",           REG_IDX_LOCATION },
+       { "Description",        REG_IDX_DESCRIPTION },
+       { "Parameters",         REG_IDX_PARAMETERS },
+       { "Port",               REG_IDX_PORT },
+       { "Share Name",         REG_IDX_SHARENAME },
+       { "Printer Driver",     REG_IDX_DRIVER },
+       { "Separator File",     REG_IDX_SEP_FILE },
+       { "Print Processor",    REG_IDX_PRINTPROC },
+       { "Datatype",           REG_IDX_DATATYPE },
+       { "Default Devmode",    REG_IDX_DEVMODE },
+       { "Security",           REG_IDX_SECDESC },
+       { NULL, -1 }
+};
+
+
+static int find_valuename_index( const char *valuename )
 {
-       char *p;
+       int i;
        
-       p = strrchr( path, '\\' );
-       if ( p )
-               p++;
-       else
-               p = path;
-               
-       return p;
+       for ( i=0; printer_values_map[i].name; i++ ) {
+               if ( strequal( valuename, printer_values_map[i].name ) )
+                       return printer_values_map[i].index;
+       }
+       
+       return -1;
 }
 
 /**********************************************************************
- handle enumeration of values below 
- KEY_PRINTING\Environments\<arch>\<version>\<drivername>
  *********************************************************************/
-static int print_subpath_values_environments( char *key, REGVAL_CTR *val )
+
+static void convert_values_to_printer_info_2( NT_PRINTER_INFO_LEVEL_2 *printer2, REGVAL_CTR *values )
 {
-       char            *keystr;
-       char            *key2 = NULL;
-       char            *base, *new_path;
-       fstring         env;
-       fstring         driver;
-       int             version;
-       NT_PRINTER_DRIVER_INFO_LEVEL    driver_ctr;
-       NT_PRINTER_DRIVER_INFO_LEVEL_3  *info3;
-       WERROR          w_result;
-       char            *buffer = NULL;
-       char            *buffer2 = NULL;
-       int             buffer_size = 0;
-       int             i, length;
-       char            *filename;
+       int num_values = regval_ctr_numvals( values );
+       uint32 value_index;
+       REGISTRY_VALUE *val;
+       int i;
        
-       DEBUG(8,("print_subpath_values_environments: Enter key => [%s]\n", key ? key : "NULL"));
+       for ( i=0; i<num_values; i++ ) {
+               val = regval_ctr_specific_value( values, i );
+               value_index = find_valuename_index( regval_name( val ) );
+               
+               switch( value_index ) {
+                       case REG_IDX_ATTRIBUTES:
+                               printer2->attributes = (uint32)(*regval_data_p(val));
+                               break;
+                       case REG_IDX_PRIORITY:
+                               printer2->priority = (uint32)(*regval_data_p(val));
+                               break;
+                       case REG_IDX_DEFAULT_PRIORITY:
+                               printer2->default_priority = (uint32)(*regval_data_p(val));
+                               break;
+                       case REG_IDX_CHANGEID:
+                               printer2->changeid = (uint32)(*regval_data_p(val));
+                               break;
+                       case REG_IDX_STARTTIME:
+                               printer2->starttime = (uint32)(*regval_data_p(val));
+                               break;
+                       case REG_IDX_UNTILTIME:
+                               printer2->untiltime = (uint32)(*regval_data_p(val));
+                               break;
+                       case REG_IDX_NAME:
+                               rpcstr_pull( printer2->printername, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_LOCATION:
+                               rpcstr_pull( printer2->location, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_DESCRIPTION:
+                               rpcstr_pull( printer2->comment, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_PARAMETERS:
+                               rpcstr_pull( printer2->parameters, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_PORT:
+                               rpcstr_pull( printer2->portname, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_SHARENAME:
+                               rpcstr_pull( printer2->sharename, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_DRIVER:
+                               rpcstr_pull( printer2->drivername, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_SEP_FILE:
+                               rpcstr_pull( printer2->sepfile, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_PRINTPROC:
+                               rpcstr_pull( printer2->printprocessor, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_DATATYPE:
+                               rpcstr_pull( printer2->datatype, regval_data_p(val), sizeof(fstring), regval_size(val), 0 );
+                               break;
+                       case REG_IDX_DEVMODE:
+                               break;
+                       case REG_IDX_SECDESC:
+                               break;          
+                       default:
+                               /* unsupported value...throw away */
+                               DEBUG(8,("convert_values_to_printer_info_2: Unsupported registry value [%s]\n", 
+                                       regval_name( val ) ));
+               }
+       }
        
-       if ( !key )
-               return 0;
+       return;
+}      
+
+/**********************************************************************
+ *********************************************************************/
+
+static bool key_printers_store_values( const char *key, REGVAL_CTR *values )
+{
+       char *printers_key;
+       char *printername, *keyname;
+       NT_PRINTER_INFO_LEVEL   *printer = NULL;
+       WERROR result;
+       
+       printers_key = strip_printers_prefix( key );
+       
+       /* values in the top level key get stored in the registry */
+
+       if ( !printers_key ) {
+               /* normalize on the 'HKLM\SOFTWARE\....\Print\Printers' key */
+               return regdb_store_values( KEY_WINNT_PRINTERS, values );
+       }
+       
+       if (!reg_split_path( printers_key, &printername, &keyname )) {
+               return False;
+       }
+
+       if ( !W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, printername) ) )
+               return False;
+
+       /* deal with setting values directly under the printername */
+
+       if ( !keyname ) {
+               convert_values_to_printer_info_2( printer->info_2, values );
+       }
+       else {
+               int num_values = regval_ctr_numvals( values );
+               int i;
+               REGISTRY_VALUE *val;
+               
+               delete_printer_key( printer->info_2->data, keyname );
                
-       /* 
-        * The only key below KEY_PRINTING\Environments that 
-        * posseses values is each specific printer driver 
-        * First get the arch, version, & driver name
-        */
+               /* deal with any subkeys */
+               for ( i=0; i<num_values; i++ ) {
+                       val = regval_ctr_specific_value( values, i );
+                       result = set_printer_dataex( printer, keyname, 
+                               regval_name( val ),
+                               regval_type( val ),
+                               regval_data_p( val ),
+                               regval_size( val ) );
+                       if ( !W_ERROR_IS_OK(result) ) {
+                               DEBUG(0,("key_printers_store_values: failed to set printer data [%s]!\n",
+                                       keyname));
+                               free_a_printer( &printer, 2 );
+                               return False;
+                       }
+               }
+       }
+
+       result = mod_a_printer( printer, 2 );
+
+       free_a_printer( &printer, 2 );
+
+       return W_ERROR_IS_OK(result);
+}
+
+/*********************************************************************
+ *********************************************************************
+ ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/ENVIRONMENTS"
+ *********************************************************************
+ *********************************************************************/
+
+static int key_driver_fetch_keys( const char *key, REGSUBKEY_CTR *subkeys )
+{
+       const char *environments[] = {
+               "Windows 4.0",
+               "Windows NT x86",
+               "Windows NT R4000",
+               "Windows NT Alpha_AXP",
+               "Windows NT PowerPC",
+               "Windows IA64",
+               "Windows x64",
+               NULL };
+       fstring *drivers = NULL;
+       int i, env_index, num_drivers;
+       char *keystr, *base, *subkeypath;
+       pstring key2;
+       int num_subkeys = -1;
+       int version;
+
+       DEBUG(10,("key_driver_fetch_keys key=>[%s]\n", key ? key : "NULL" ));
+       
+       keystr = reg_remaining_path( key + strlen(KEY_ENVIRONMENTS) );  
+       
+       /* list all possible architectures */
+       
+       if ( !keystr ) {
+               for ( num_subkeys=0; environments[num_subkeys]; num_subkeys++ ) 
+                       regsubkey_ctr_addkey( subkeys,  environments[num_subkeys] );
+
+               return num_subkeys;
+       }
+       
+       /* we are dealing with a subkey of "Environments */
+       
+       pstrcpy( key2, keystr );
+       keystr = key2;
+       if (!reg_split_path( keystr, &base, &subkeypath )) {
+               return -1;
+       }
+       
+       /* sanity check */
+       
+       for ( env_index=0; environments[env_index]; env_index++ ) {
+               if ( strequal( environments[env_index], base ) )
+                       break;
+       }
+       if ( !environments[env_index] )
+               return -1;
+       
+       /* ...\Print\Environements\...\ */
+       
+       if ( !subkeypath ) {
+               regsubkey_ctr_addkey( subkeys, "Drivers" );
+               regsubkey_ctr_addkey( subkeys, "Print Processors" );
+                               
+               return 2;
+       }
+       
+       /* more of the key path to process */
+       
+       keystr = subkeypath;
+       if (!reg_split_path( keystr, &base, &subkeypath )) {
+               return -1;
+       }
+               
+       /* ...\Print\Environements\...\Drivers\ */
+       
+       if ( !subkeypath ) {
+               if ( strequal(base, "Drivers") ) {
+                       switch ( env_index ) {
+                               case 0: /* Win9x */
+                                       regsubkey_ctr_addkey( subkeys, "Version-0" );
+                                       break;
+                               default: /* Windows NT based systems */
+                                       regsubkey_ctr_addkey( subkeys, "Version-2" );
+                                       regsubkey_ctr_addkey( subkeys, "Version-3" );
+                                       break;                  
+                       }
+               
+                       return regsubkey_ctr_numkeys( subkeys );
+               } else if ( strequal(base, "Print Processors") ) {
+                       if ( env_index == 1 || env_index == 5 || env_index == 6 )
+                               regsubkey_ctr_addkey( subkeys, "winprint" );
+                               
+                       return regsubkey_ctr_numkeys( subkeys );
+               } else
+                       return -1;      /* bad path */
+       }
+       
+       /* we finally get to enumerate the drivers */
+       
+       /* only one possible subkey below PrintProc key */
+
+       if ( strequal(base, "Print Processors") ) {
+               keystr = subkeypath;
+               if (!reg_split_path( keystr, &base, &subkeypath )) {
+                       return -1;
+               }
+
+               /* no subkeys below this point */
+
+               if ( subkeypath )
+                       return -1;
+
+               /* only allow one keyname here -- 'winprint' */
+
+               return strequal( base, "winprint" ) ? 0 : -1;
+       }
+       
+       /* only dealing with drivers from here on out */
+
+       keystr = subkeypath;
+       if (!reg_split_path( keystr, &base, &subkeypath )) {
+               return -1;
+       }
+
+       version = atoi(&base[strlen(base)-1]);
+                       
+       switch (env_index) {
+       case 0:
+               if ( version != 0 )
+                       return -1;
+               break;
+       default:
+               if ( version != 2 && version != 3 )
+                       return -1;
+               break;
+       }
+
        
-       /* env */
+       if ( !subkeypath ) {
+               num_drivers = get_ntdrivers( &drivers, environments[env_index], version );
+               for ( i=0; i<num_drivers; i++ )
+                       regsubkey_ctr_addkey( subkeys, drivers[i] );
+                       
+               return regsubkey_ctr_numkeys( subkeys );        
+       }       
        
-       key2 = strdup( key );
-       keystr = key2;
-       reg_split_path( keystr, &base, &new_path );
-       if ( !base || !new_path )
-               return 0;
-       fstrcpy( env, base );
+       /* if anything else left, just say if has no subkeys */
        
-       /* version */
+       DEBUG(1,("key_driver_fetch_keys unhandled key [%s] (subkey == %s\n", 
+               key, subkeypath ));
        
-       keystr = new_path;
-       reg_split_path( keystr, &base, &new_path );
-       if ( !base || !new_path )
-               return 0;
-       version = atoi( base );
+       return 0;
+}
 
-       /* printer driver name */
-       
-       keystr = new_path;
-       reg_split_path( keystr, &base, &new_path );
-       /* new_path should be NULL here since this must be the last key */
-       if ( !base || new_path )
-               return 0;
-       fstrcpy( driver, base );
 
-       w_result = get_a_printer_driver( &driver_ctr, 3, driver, env, version );
+/**********************************************************************
+ *********************************************************************/
 
-       if ( !W_ERROR_IS_OK(w_result) )
-               return -1;
-               
-       /* build the values out of the driver information */
-       info3 = driver_ctr.info_3;
+static void fill_in_driver_values( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3, REGVAL_CTR *values )
+{
+       char *buffer = NULL;
+       int buffer_size = 0;
+       int i, length;
+       char *filename;
+       UNISTR2 data;
        
        filename = dos_basename( info3->driverpath );
-       regval_ctr_addvalue( val, "Driver",             REG_SZ,       filename, strlen(filename)+1 );
+       init_unistr2( &data, filename, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Driver", REG_SZ, (char*)data.buffer, 
+               data.uni_str_len*sizeof(uint16) );
+       
        filename = dos_basename( info3->configfile );
-       regval_ctr_addvalue( val, "Configuration File", REG_SZ,       filename, strlen(filename)+1 );
+       init_unistr2( &data, filename, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Configuration File", REG_SZ, (char*)data.buffer, 
+               data.uni_str_len*sizeof(uint16) );
+       
        filename = dos_basename( info3->datafile );
-       regval_ctr_addvalue( val, "Data File",          REG_SZ,       filename, strlen(filename)+1 );
+       init_unistr2( &data, filename, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Data File", REG_SZ, (char*)data.buffer, 
+               data.uni_str_len*sizeof(uint16) );
+       
        filename = dos_basename( info3->helpfile );
-       regval_ctr_addvalue( val, "Help File",          REG_SZ,       filename, strlen(filename)+1 );
-               
-       regval_ctr_addvalue( val, "Data Type",          REG_SZ,       info3->defaultdatatype, strlen(info3->defaultdatatype)+1 );
+       init_unistr2( &data, filename, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Help File", REG_SZ, (char*)data.buffer, 
+               data.uni_str_len*sizeof(uint16) );
        
-       regval_ctr_addvalue( val, "Version",            REG_DWORD,    (char*)&info3->cversion, sizeof(info3->cversion) );
+       init_unistr2( &data, info3->defaultdatatype, UNI_STR_TERMINATE);
+       regval_ctr_addvalue( values, "Data Type", REG_SZ, (char*)data.buffer, 
+               data.uni_str_len*sizeof(uint16) );
        
-       if ( info3->dependentfiles )
-       {
+       regval_ctr_addvalue( values, "Version", REG_DWORD, (char*)&info3->cversion, 
+               sizeof(info3->cversion) );
+       
+       if ( info3->dependentfiles ) {
                /* place the list of dependent files in a single 
                   character buffer, separating each file name by
                   a NULL */
                   
-               for ( i=0; strcmp(info3->dependentfiles[i], ""); i++ )
-               {
+               for ( i=0; strcmp(info3->dependentfiles[i], ""); i++ ) {
                        /* strip the path to only the file's base name */
                
                        filename = dos_basename( info3->dependentfiles[i] );
                        
                        length = strlen(filename);
                
-                       buffer2 = Realloc( buffer, buffer_size + length + 1 );
-                       if ( !buffer2 )
+                       buffer = (char *)SMB_REALLOC( buffer, buffer_size + (length + 1)*sizeof(uint16) );
+                       if ( !buffer ) {
                                break;
-                       buffer = buffer2;
-               
-                       memcpy( buffer+buffer_size, filename, length+1 );
+                       }
+                       
+                       init_unistr2( &data, filename, UNI_STR_TERMINATE);
+                       memcpy( buffer+buffer_size, (char*)data.buffer, data.uni_str_len*sizeof(uint16) );
                
-                       buffer_size += length + 1;
+                       buffer_size += (length + 1)*sizeof(uint16);
                }
                
                /* terminated by double NULL.  Add the final one here */
                
-               buffer2 = Realloc( buffer, buffer_size + 1 );
-               if ( !buffer2 ) {
-                       SAFE_FREE( buffer );
+               buffer = (char *)SMB_REALLOC( buffer, buffer_size + 2 );
+               if ( !buffer ) {
                        buffer_size = 0;
-               }
-               else {
-                       buffer = buffer2;
+               } else {
+                       buffer[buffer_size++] = '\0';
                        buffer[buffer_size++] = '\0';
                }
        }
        
-       regval_ctr_addvalue( val, "Dependent Files",    REG_MULTI_SZ, buffer, buffer_size );
-       
-       free_a_printer_driver( driver_ctr, 3 );
-       SAFE_FREE( key2 );
-       SAFE_FREE( buffer );
+       regval_ctr_addvalue( values, "Dependent Files",    REG_MULTI_SZ, buffer, buffer_size );
                
-       DEBUG(8,("print_subpath_values_environments: Exit\n"));
+       SAFE_FREE( buffer );
        
-       return regval_ctr_numvals( val );
+       return;
 }
 
-
 /**********************************************************************
- handle enumeration of subkeys below KEY_PRINTING\Forms
- Really just a stub function, but left here in case it needs to
- be expanded later on
  *********************************************************************/
-static int print_subpath_forms( char *key, REGSUBKEY_CTR *subkeys )
-{
-       DEBUG(10,("print_subpath_forms: key=>[%s]\n", key ? key : "NULL" ));
-       
-       /* there are no subkeys */
-       
-       if ( key )
-               return -1;
-       
-       return 0;
-}
 
-/**********************************************************************
- handle enumeration of values below KEY_PRINTING\Forms
- *********************************************************************/
-static int print_subpath_values_forms( char *key, REGVAL_CTR *val )
+static int driver_arch_fetch_values( char *key, REGVAL_CTR *values )
 {
-       int             num_values = 0;
-       uint32          data[8];
-       int             form_index = 1;
-       
-       DEBUG(10,("print_values_forms: key=>[%s]\n", key ? key : "NULL" ));
+       char            *keystr, *base, *subkeypath;
+       fstring         arch_environment;
+       fstring         driver;
+       int             version;
+       NT_PRINTER_DRIVER_INFO_LEVEL    driver_ctr;
+       WERROR          w_result;
+
+       if (!reg_split_path( key, &base, &subkeypath )) {
+               return -1;
+       }
        
-       /* handle ..\Forms\ */
+       /* no values in 'Environments\Drivers\Windows NT x86' */
        
-       if ( !key )
-       {
-               nt_forms_struct *forms_list = NULL;
-               nt_forms_struct *form = NULL;
-               int i;
-               
-               if ( (num_values = get_ntforms( &forms_list )) == 0 ) 
-                       return 0;
+       if ( !subkeypath ) 
+               return 0;
                
-               DEBUG(10,("print_subpath_values_forms: [%d] user defined forms returned\n",
-                       num_values));
+       /* We have the Architecture string and some subkey name:
+          Currently we only support
+          * Drivers
+          * Print Processors
+          Anything else is an error.
+          */
 
-               /* handle user defined forms */
-                               
-               for ( i=0; i<num_values; i++ )
-               {
-                       form = &forms_list[i];
-                       
-                       data[0] = form->width;
-                       data[1] = form->length;
-                       data[2] = form->left;
-                       data[3] = form->top;
-                       data[4] = form->right;
-                       data[5] = form->bottom;
-                       data[6] = form_index++;
-                       data[7] = form->flag;
-                       
-                       regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
-               
-               }
-               
-               SAFE_FREE( forms_list );
-               forms_list = NULL;
-               
-               /* handle built-on forms */
-               
-               if ( (num_values = get_builtin_ntforms( &forms_list )) == 0 ) 
-                       return 0;
-               
-               DEBUG(10,("print_subpath_values_forms: [%d] built-in forms returned\n",
-                       num_values));
-                       
-               for ( i=0; i<num_values; i++ )
-               {
-                       form = &forms_list[i];
-                       
-                       data[0] = form->width;
-                       data[1] = form->length;
-                       data[2] = form->left;
-                       data[3] = form->top;
-                       data[4] = form->right;
-                       data[5] = form->bottom;
-                       data[6] = form_index++;
-                       data[7] = form->flag;
-                                       
-                       regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
-               }
-               
-               SAFE_FREE( forms_list );
-       }
+       fstrcpy( arch_environment, base );
        
-       return num_values;
-}
+       keystr = subkeypath;
+       if (!reg_split_path( keystr, &base, &subkeypath )) {
+               return -1;
+       }
 
-/**********************************************************************
- handle enumeration of subkeys below KEY_PRINTING\Printers
- *********************************************************************/
-static int print_subpath_printers( char *key, REGSUBKEY_CTR *subkeys )
-{
-       int n_services = lp_numservices();      
-       int snum;
-       fstring sname;
-       int num_subkeys = 0;
-       char *keystr, *key2 = NULL;
-       char *base, *new_path;
-       NT_PRINTER_INFO_LEVEL *printer = NULL;
-       
+       if ( strequal(base, "Print Processors") )
+               return 0;
+
+       /* only Drivers key can be left */
+               
+       if ( !strequal(base, "Drivers") )
+               return -1;
+                       
+       if ( !subkeypath )
+               return 0;
        
-       DEBUG(10,("print_subpath_printers: key=>[%s]\n", key ? key : "NULL" ));
+       /* We know that we have Architechure\Drivers with some subkey name
+          The subkey name has to be Version-XX */
        
-       if ( !key )
-       {
-               /* enumerate all printers */
-               
-               for (snum=0; snum<n_services; snum++) {
-                       if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
-                               continue;
-                               
-                       fstrcpy( sname, lp_servicename(snum) );
-                               
-                       regsubkey_ctr_addkey( subkeys, sname );
-               }
-               
-               num_subkeys = regsubkey_ctr_numkeys( subkeys );
-               goto done;
+       keystr = subkeypath;
+       if (!reg_split_path( keystr, &base, &subkeypath )) {
+               return -1;
        }
 
-       /* get information for a specific printer */
+       if ( !subkeypath )
+               return 0;
+               
+       version = atoi(&base[strlen(base)-1]);
+
+       /* BEGIN PRINTER DRIVER NAME BLOCK */
        
-       key2 = strdup( key );
-       keystr = key2;
-       reg_split_path( keystr, &base, &new_path );
+       keystr = subkeypath;
+       if (!reg_split_path( keystr, &base, &subkeypath )) {
+               return -1;
+       }
        
+       /* don't go any deeper for now */
        
-       if ( !new_path ) {
-               /* sanity check on the printer name */
-               if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, base) ) )
-                       goto done;
-               
-               free_a_printer( &printer, 2 );
+       fstrcpy( driver, base );
+       
+       w_result = get_a_printer_driver( &driver_ctr, 3, driver, arch_environment, version );
+
+       if ( !W_ERROR_IS_OK(w_result) )
+               return -1;
                
-               regsubkey_ctr_addkey( subkeys, "PrinterDriverData" );
-       }
+       fill_in_driver_values( driver_ctr.info_3, values ); 
        
-       /* no other subkeys below here */
+       free_a_printer_driver( driver_ctr, 3 );
 
-done:  
-       SAFE_FREE( key2 );
-       return num_subkeys;
+       /* END PRINTER DRIVER NAME BLOCK */
+
+                                               
+       DEBUG(8,("key_driver_fetch_values: Exit\n"));
+       
+       return regval_ctr_numvals( values );
 }
 
 /**********************************************************************
- handle enumeration of values below KEY_PRINTING\Printers
  *********************************************************************/
-static int print_subpath_values_printers( char *key, REGVAL_CTR *val )
+
+static int key_driver_fetch_values( const char *key, REGVAL_CTR *values )
 {
-       int             num_values = 0;
-       char            *keystr, *key2 = NULL;
-       char            *base, *new_path;
-       NT_PRINTER_INFO_LEVEL   *printer = NULL;
-       NT_PRINTER_INFO_LEVEL_2 *info2;
-       DEVICEMODE      *devmode;
-       prs_struct      prs;
-       uint32          offset;
-       int             snum;
-       int             i;
-       fstring         valuename;
-       uint8           *data;
-       uint32          type, data_len;
-       fstring         printername;
-       
-       /* 
-        * There are tw cases to deal with here
-        * (1) enumeration of printer_info_2 values
-        * (2) enumeration of the PrinterDriverData subney
-        */
-        
-       if ( !key ) {
-               /* top level key has no values */
-               goto done;
-       }
-       
-       key2 = strdup( key );
-       keystr = key2;
-       reg_split_path( keystr, &base, &new_path );
-       
-       fstrcpy( printername, base );
+       char *keystr;
+       pstring subkey;
        
-       if ( !new_path ) 
-       {
-               /* we are dealing with the printer itself */
-
-               if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, printername) ) )
-                       goto done;
-
-               info2 = printer->info_2;
-               
-
-               regval_ctr_addvalue( val, "Attributes",       REG_DWORD, (char*)&info2->attributes,       sizeof(info2->attributes) );
-               regval_ctr_addvalue( val, "Priority",         REG_DWORD, (char*)&info2->priority,         sizeof(info2->attributes) );
-               regval_ctr_addvalue( val, "ChangeID",         REG_DWORD, (char*)&info2->changeid,         sizeof(info2->changeid) );
-               regval_ctr_addvalue( val, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) );
-               regval_ctr_addvalue( val, "Status",           REG_DWORD, (char*)&info2->status,           sizeof(info2->status) );
-               regval_ctr_addvalue( val, "StartTime",        REG_DWORD, (char*)&info2->starttime,        sizeof(info2->starttime) );
-               regval_ctr_addvalue( val, "UntilTime",        REG_DWORD, (char*)&info2->untiltime,        sizeof(info2->untiltime) );
-               regval_ctr_addvalue( val, "cjobs",            REG_DWORD, (char*)&info2->cjobs,            sizeof(info2->cjobs) );
-               regval_ctr_addvalue( val, "AveragePPM",       REG_DWORD, (char*)&info2->averageppm,       sizeof(info2->averageppm) );
-               
-               regval_ctr_addvalue( val, "Name",             REG_SZ, info2->printername,     sizeof(info2->printername)+1 );
-               regval_ctr_addvalue( val, "Location",         REG_SZ, info2->location,        sizeof(info2->location)+1 );
-               regval_ctr_addvalue( val, "Comment",          REG_SZ, info2->comment,         sizeof(info2->comment)+1 );
-               regval_ctr_addvalue( val, "Parameters",       REG_SZ, info2->parameters,      sizeof(info2->parameters)+1 );
-               regval_ctr_addvalue( val, "Port",             REG_SZ, info2->portname,        sizeof(info2->portname)+1 );
-               regval_ctr_addvalue( val, "Server",           REG_SZ, info2->servername,      sizeof(info2->servername)+1 );
-               regval_ctr_addvalue( val, "Share",            REG_SZ, info2->sharename,       sizeof(info2->sharename)+1 );
-               regval_ctr_addvalue( val, "Driver",           REG_SZ, info2->drivername,      sizeof(info2->drivername)+1 );
-               regval_ctr_addvalue( val, "Separator File",   REG_SZ, info2->sepfile,         sizeof(info2->sepfile)+1 );
-               regval_ctr_addvalue( val, "Print Processor",  REG_SZ, info2->printprocessor,  sizeof(info2->printprocessor)+1 );
-               
-               
-               /* use a prs_struct for converting the devmode and security 
-                  descriptor to REG_BIARY */
-               
-               prs_init( &prs, MAX_PDU_FRAG_LEN, regval_ctr_getctx(val), MARSHALL);
-
-               /* stream the device mode */
-               
-               snum = lp_servicenumber(info2->sharename);
-               if ( (devmode = construct_dev_mode( snum )) != NULL )
-               {                       
-                       if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) {
-                       
-                               offset = prs_offset( &prs );
-                               
-                               regval_ctr_addvalue( val, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset );
-                       }
-                       
-                       
-               }
-               
-               prs_mem_clear( &prs );
-               prs_set_offset( &prs, 0 );
-               
-               if ( info2->secdesc_buf && info2->secdesc_buf->len ) 
-               {
-                       if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sec, &prs, 0 ) ) {
-                       
-                               offset = prs_offset( &prs );
-                       
-                               regval_ctr_addvalue( val, "Security", REG_BINARY, prs_data_p(&prs), offset );
-                       }
-               }
+       DEBUG(8,("key_driver_fetch_values: Enter key => [%s]\n", key ? key : "NULL"));
 
-                               
-               prs_mem_free( &prs );
-               free_a_printer( &printer, 2 );  
-               
-               num_values = regval_ctr_numvals( val ); 
-               goto done;
-               
-       }
+       /* no values in the Environments key */
        
+       if ( !(keystr = reg_remaining_path( key + strlen(KEY_ENVIRONMENTS) )) )
+               return 0;
        
-       keystr = new_path;
-       reg_split_path( keystr, &base, &new_path );
+       pstrcpy( subkey, keystr);
        
-       /* here should be no more path components here */
+       /* pass off to handle subkeys */
        
-       if ( new_path || strcmp(base, "PrinterDriverData") )
-               goto done;
-               
-       /* now enumerate the PrinterDriverData key */
-       if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, printername) ) )
-               goto done;
+       return driver_arch_fetch_values( subkey, values );
+}
 
-       info2 = printer->info_2;
-               
+/*********************************************************************
+ *********************************************************************
+ ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT"
+ *********************************************************************
+ *********************************************************************/
+
+static int key_print_fetch_keys( const char *key, REGSUBKEY_CTR *subkeys )
+{      
+       int key_len = strlen(key);
        
-       /* iterate over all printer data and fill the regval container */
+       /* no keys below 'Print' handled here */
        
-       for ( i=0; get_specific_param_by_index(*printer, 2, i, valuename, &data, &type, &data_len); i++ )
-       {
-               regval_ctr_addvalue( val, valuename, type, data, data_len );
-       }
-               
-       free_a_printer( &printer, 2 );
+       if ( (key_len != strlen(KEY_CONTROL_PRINT)) && (key_len != strlen(KEY_WINNT_PRINT)) )
+               return -1;
 
-       num_values = regval_ctr_numvals( val );
-       
-done:
-       SAFE_FREE( key2 ); 
+       regsubkey_ctr_addkey( subkeys, "Environments" );
+       regsubkey_ctr_addkey( subkeys, "Monitors" );
+       regsubkey_ctr_addkey( subkeys, "Forms" );
+       regsubkey_ctr_addkey( subkeys, "Printers" );
        
-       return num_values;
+       return regsubkey_ctr_numkeys( subkeys );
 }
 
 /**********************************************************************
- Routine to handle enumeration of subkeys and values 
- below KEY_PRINTING (depending on whether or not subkeys/val are 
- valid pointers. 
+ *********************************************************************
+ ** Structure to hold dispatch table of ops for various printer keys.
+ ** Make sure to always store deeper keys along the same path first so 
+ ** we ge a more specific match.
+ *********************************************************************
  *********************************************************************/
-static int handle_printing_subpath( char *key, REGSUBKEY_CTR *subkeys, REGVAL_CTR *val )
+
+static struct reg_dyn_tree print_registry[] = {
+/* just pass the monitor onto the registry tdb */
+{ KEY_MONITORS,
+       &regdb_fetch_keys, 
+       &regdb_store_keys,
+       &regdb_fetch_values,
+       &regdb_store_values },
+{ KEY_FORMS, 
+       &key_forms_fetch_keys, 
+       NULL, 
+       &key_forms_fetch_values,
+       NULL },
+{ KEY_CONTROL_PRINTERS, 
+       &key_printers_fetch_keys,
+       &key_printers_store_keys,
+       &key_printers_fetch_values,
+       &key_printers_store_values },
+{ KEY_ENVIRONMENTS,
+       &key_driver_fetch_keys,
+       NULL,
+       &key_driver_fetch_values,
+       NULL },
+{ KEY_CONTROL_PRINT,
+       &key_print_fetch_keys,
+       NULL,
+       NULL,
+       NULL },
+{ KEY_WINNT_PRINTERS,
+       &key_printers_fetch_keys,
+       &key_printers_store_keys,
+       &key_printers_fetch_values,
+       &key_printers_store_values },
+{ KEY_PORTS,
+       &regdb_fetch_keys, 
+       &regdb_store_keys,
+       &regdb_fetch_values,
+       &regdb_store_values },
+       
+{ NULL, NULL, NULL, NULL, NULL }
+};
+
+
+/**********************************************************************
+ *********************************************************************
+ ** Main reg_printing interface functions
+ *********************************************************************
+ *********************************************************************/
+
+/***********************************************************************
+ Lookup a key in the print_registry table, returning its index.
+ -1 on failure
+ **********************************************************************/
+
+static int match_registry_path( const char *key )
 {
-       int result = 0;
-       char *p, *base;
        int i;
+       pstring path;
        
-       DEBUG(10,("handle_printing_subpath: key=>[%s]\n", key ));
-       
-       /* 
-        * break off the first part of the path 
-        * topmost base **must** be one of the strings 
-        * in top_level_keys[]
-        */
-       
-       reg_split_path( key, &base, &p);
-               
-       for ( i=0; i<MAX_TOP_LEVEL_KEYS; i++ ) {
-               if ( StrCaseCmp( top_level_keys[i], base ) == 0 )
-                       break;
-       }
-       
-       DEBUG(10,("handle_printing_subpath: base=>[%s], i==[%d]\n", base, i));  
-               
-       if ( !(i < MAX_TOP_LEVEL_KEYS) )
+       if ( !key )
                return -1;
-                                               
-       /* Call routine to handle each top level key */
-       switch ( i )
-       {
-               case KEY_INDEX_ENVIR:
-                       if ( subkeys )
-                               print_subpath_environments( p, subkeys );
-                       if ( val )
-                               print_subpath_values_environments( p, val );
-                       break;
-               
-               case KEY_INDEX_FORMS:
-                       if ( subkeys )
-                               print_subpath_forms( p, subkeys );
-                       if ( val )
-                               print_subpath_values_forms( p, val );
-                       break;
-                       
-               case KEY_INDEX_PRINTER:
-                       if ( subkeys )
-                               print_subpath_printers( p, subkeys );
-                       if ( val )
-                               print_subpath_values_printers( p, val );
-                       break;
+
+       pstrcpy( path, key );
+       normalize_reg_path( path );
        
-               /* default case for top level key that has no handler */
-               
-               default:
-                       break;
+       for ( i=0; print_registry[i].path; i++ ) {
+               if ( strncmp( path, print_registry[i].path, strlen(print_registry[i].path) ) == 0 )
+                       return i;
        }
        
-       
-       
-       return result;
-
+       return -1;
 }
-/**********************************************************************
- Enumerate registry subkey names given a registry path.  
- Caller is responsible for freeing memory to **subkeys
- *********************************************************************/
-int printing_subkey_info( char *key, REGSUBKEY_CTR *subkey_ctr )
+
+/***********************************************************************
+ **********************************************************************/
+
+static int regprint_fetch_reg_keys( const char *key, REGSUBKEY_CTR *subkeys )
 {
-       char            *path;
-       BOOL            top_level = False;
-       int             num_subkeys = 0;
+       int i = match_registry_path( key );
        
-       DEBUG(10,("printing_subkey_info: key=>[%s]\n", key));
-       
-       path = trim_reg_path( key );
-       
-       /* check to see if we are dealing with the top level key */
-       
-       if ( !path )
-               top_level = True;
+       if ( i == -1 )
+               return -1;
                
-       if ( top_level ) {
-               for ( num_subkeys=0; num_subkeys<MAX_TOP_LEVEL_KEYS; num_subkeys++ )
-                       regsubkey_ctr_addkey( subkey_ctr, top_level_keys[num_subkeys] );
-       }
-       else
-               num_subkeys = handle_printing_subpath( path, subkey_ctr, NULL );
-       
-       SAFE_FREE( path );
-       
-       return num_subkeys;
+       if ( !print_registry[i].fetch_subkeys )
+               return -1;
+               
+       return print_registry[i].fetch_subkeys( key, subkeys );
 }
 
 /**********************************************************************
- Enumerate registry values given a registry path.  
- Caller is responsible for freeing memory 
  *********************************************************************/
 
-int printing_value_info( char *key, REGVAL_CTR *val )
+static bool regprint_store_reg_keys( const char *key, REGSUBKEY_CTR *subkeys )
 {
-       char            *path;
-       BOOL            top_level = False;
-       int             num_values = 0;
-       
-       DEBUG(10,("printing_value_info: key=>[%s]\n", key));
+       int i = match_registry_path( key );
        
-       path = trim_reg_path( key );
+       if ( i == -1 )
+               return False;
        
-       /* check to see if we are dealing with the top level key */
-       
-       if ( !path )
-               top_level = True;
-       
-       /* fill in values from the getprinterdata_printer_server() */
-       if ( top_level )
-               num_values = 0;
-       else
-               num_values = handle_printing_subpath( path, NULL, val );
+       if ( !print_registry[i].store_subkeys )
+               return False;
                
-       
-       return num_values;
+       return print_registry[i].store_subkeys( key, subkeys );
 }
 
 /**********************************************************************
- Stub function which always returns failure since we don't want
- people storing printing information directly via regostry calls
- (for now at least)
  *********************************************************************/
 
-BOOL printing_store_subkey( char *key, REGSUBKEY_CTR *subkeys )
+static int regprint_fetch_reg_values( const char *key, REGVAL_CTR *values )
 {
-       return False;
+       int i = match_registry_path( key );
+       
+       if ( i == -1 )
+               return -1;
+       
+       /* return 0 values by default since we know the key had 
+          to exist because the client opened a handle */
+          
+       if ( !print_registry[i].fetch_values )
+               return 0;
+               
+       return print_registry[i].fetch_values( key, values );
 }
 
 /**********************************************************************
- Stub function which always returns failure since we don't want
- people storing printing information directly via regostry calls
- (for now at least)
  *********************************************************************/
 
-BOOL printing_store_value( char *key, REGVAL_CTR *val )
+static bool regprint_store_reg_values( const char *key, REGVAL_CTR *values )
 {
-       return False;
+       int i = match_registry_path( key );
+       
+       if ( i == -1 )
+               return False;
+       
+       if ( !print_registry[i].store_values )
+               return False;
+               
+       return print_registry[i].store_values( key, values );
 }
 
 /* 
@@ -803,10 +1237,11 @@ BOOL printing_store_value( char *key, REGVAL_CTR *val )
  */
  
 REGISTRY_OPS printing_ops = {
-       printing_subkey_info,
-       printing_value_info,
-       printing_store_subkey,
-       printing_store_value
+       regprint_fetch_reg_keys,
+       regprint_fetch_reg_values,
+       regprint_store_reg_keys,
+       regprint_store_reg_values,
+       NULL, NULL, NULL
 };