Fairly large change to printing code.
[samba.git] / source3 / registry / reg_printing.c
1 /* 
2  *  Unix SMB/CIFS implementation.
3  *  RPC Pipe client / server routines
4  *  Copyright (C) Gerald Carter                     2002.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *  
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *  
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 /* Implementation of registry virtual views for printing information */
22
23 #include "includes.h"
24
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_RPC_SRV
27
28 #define MAX_TOP_LEVEL_KEYS      3
29
30 /* some symbolic indexes into the top_level_keys */
31
32 #define KEY_INDEX_ENVIR         0
33 #define KEY_INDEX_FORMS         1
34 #define KEY_INDEX_PRINTER       2
35
36 static char *top_level_keys[MAX_TOP_LEVEL_KEYS] = { 
37         "Environments", 
38         "Forms",
39         "Printers" 
40 };
41
42
43 /**********************************************************************
44  It is safe to assume that every registry path passed into on of 
45  the exported functions here begins with KEY_PRINTING else
46  these functions would have never been called.  This is a small utility
47  function to strip the beginning of the path and make a copy that the 
48  caller can modify.  Note that the caller is responsible for releasing
49  the memory allocated here.
50  **********************************************************************/
51
52 static char* trim_reg_path( char *path )
53 {
54         char *p;
55         uint16 key_len = strlen(KEY_PRINTING);
56         
57         /* 
58          * sanity check...this really should never be True.
59          * It is only here to prevent us from accessing outside
60          * the path buffer in the extreme case.
61          */
62         
63         if ( strlen(path) < key_len ) {
64                 DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path));
65                 DEBUG(0,("trim_reg_path: KEY_PRINTING => [%s]!\n", KEY_PRINTING));
66                 return NULL;
67         }
68         
69         
70         p = path + strlen( KEY_PRINTING );
71         
72         if ( *p == '\\' )
73                 p++;
74         
75         if ( *p )
76                 return strdup(p);
77         else
78                 return NULL;
79 }
80
81 /**********************************************************************
82  handle enumeration of subkeys below KEY_PRINTING\Environments
83  *********************************************************************/
84  
85 static int print_subpath_environments( char *key, REGSUBKEY_CTR *subkeys )
86 {
87         char *environments[] = {
88                 "Windows 4.0",
89                 "Windows NT x86",
90                 "Windows NT R4000",
91                 "Windows NT Alpha_AXP",
92                 "Windows NT PowerPC",
93                 NULL };
94         fstring *drivers = NULL;
95         int i, env_index, num_drivers;
96         BOOL valid_env = False;
97         char *base, *new_path;
98         char *keystr;
99         char *key2 = NULL;
100         int num_subkeys = -1;
101
102         DEBUG(10,("print_subpath_environments: key=>[%s]\n", key ? key : "NULL" ));
103         
104         /* listed architectures of installed drivers */
105         
106         if ( !key ) 
107         {
108                 /* Windows 9x drivers */
109                 
110                 if ( get_ntdrivers( &drivers, environments[0], 0 ) )
111                         regsubkey_ctr_addkey( subkeys,  environments[0] );
112                 SAFE_FREE( drivers );
113                                         
114                 /* Windows NT/2k intel drivers */
115                 
116                 if ( get_ntdrivers( &drivers, environments[1], 2 ) 
117                         || get_ntdrivers( &drivers, environments[1], 3 ) )
118                 {
119                         regsubkey_ctr_addkey( subkeys,  environments[1] );
120                 }
121                 SAFE_FREE( drivers );
122                 
123                 /* Windows NT 4.0; non-intel drivers */
124                 for ( i=2; environments[i]; i++ ) {
125                         if ( get_ntdrivers( &drivers, environments[i], 2 ) )
126                                 regsubkey_ctr_addkey( subkeys,  environments[i] );
127                 
128                 }
129                 SAFE_FREE( drivers );
130
131                 num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
132                 goto done;
133         }
134         
135         /* we are dealing with a subkey of "Environments */
136         
137         key2 = strdup( key );
138         keystr = key2;
139         reg_split_path( keystr, &base, &new_path );
140         
141         /* sanity check */
142         
143         for ( env_index=0; environments[env_index]; env_index++ ) {
144                 if ( StrCaseCmp( environments[env_index], base ) == 0 ) {
145                         valid_env = True;
146                         break;
147                 }
148         }
149                 
150         if ( !valid_env )
151                 return -1;
152
153         /* enumerate driver versions; environment is environments[env_index] */
154         
155         if ( !new_path ) {
156                 switch ( env_index ) {
157                         case 0: /* Win9x */
158                                 if ( get_ntdrivers( &drivers, environments[0], 0 ) ) {
159                                         regsubkey_ctr_addkey( subkeys, "0" );
160                                         SAFE_FREE( drivers );
161                                 }
162                                 break;
163                         case 1: /* Windows NT/2k - intel */
164                                 if ( get_ntdrivers( &drivers, environments[1], 2 ) ) {
165                                         regsubkey_ctr_addkey( subkeys, "2" );
166                                         SAFE_FREE( drivers );
167                                 }
168                                 if ( get_ntdrivers( &drivers, environments[1], 3 ) ) {
169                                         regsubkey_ctr_addkey( subkeys, "3" );
170                                         SAFE_FREE( drivers );
171                                 }
172                                 break;
173                         default: /* Windows NT - nonintel */
174                                 if ( get_ntdrivers( &drivers, environments[env_index], 2 ) ) {
175                                         regsubkey_ctr_addkey( subkeys, "2" );
176                                         SAFE_FREE( drivers );
177                                 }
178                         
179                 }
180                 
181                 num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
182                 goto done;
183         }
184         
185         /* we finally get to enumerate the drivers */
186         
187         keystr = new_path;
188         reg_split_path( keystr, &base, &new_path );
189         
190         if ( !new_path ) {
191                 num_drivers = get_ntdrivers( &drivers, environments[env_index], atoi(base) );
192                 for ( i=0; i<num_drivers; i++ )
193                         regsubkey_ctr_addkey( subkeys, drivers[i] );
194                         
195                 num_subkeys = regsubkey_ctr_numkeys( subkeys ); 
196                 goto done;
197         }
198         
199 done:
200         SAFE_FREE( key2 );
201                 
202         return num_subkeys;
203 }
204
205 /***********************************************************************
206  simple function to prune a pathname down to the basename of a file 
207  **********************************************************************/
208  
209 static char* dos_basename ( char *path )
210 {
211         char *p;
212         
213         p = strrchr( path, '\\' );
214         if ( p )
215                 p++;
216         else
217                 p = path;
218                 
219         return p;
220 }
221
222 /**********************************************************************
223  handle enumeration of values below 
224  KEY_PRINTING\Environments\<arch>\<version>\<drivername>
225  *********************************************************************/
226  
227 static int print_subpath_values_environments( char *key, REGVAL_CTR *val )
228 {
229         char            *keystr;
230         char            *key2 = NULL;
231         char            *base, *new_path;
232         fstring         env;
233         fstring         driver;
234         int             version;
235         NT_PRINTER_DRIVER_INFO_LEVEL    driver_ctr;
236         NT_PRINTER_DRIVER_INFO_LEVEL_3  *info3;
237         WERROR          w_result;
238         char            *buffer = NULL;
239         char            *buffer2 = NULL;
240         int             buffer_size = 0;
241         int             i, length;
242         char            *filename;
243         
244         DEBUG(8,("print_subpath_values_environments: Enter key => [%s]\n", key ? key : "NULL"));
245         
246         if ( !key )
247                 return 0;
248                 
249         /* 
250          * The only key below KEY_PRINTING\Environments that 
251          * posseses values is each specific printer driver 
252          * First get the arch, version, & driver name
253          */
254         
255         /* env */
256         
257         key2 = strdup( key );
258         keystr = key2;
259         reg_split_path( keystr, &base, &new_path );
260         if ( !base || !new_path )
261                 return 0;
262         fstrcpy( env, base );
263         
264         /* version */
265         
266         keystr = new_path;
267         reg_split_path( keystr, &base, &new_path );
268         if ( !base || !new_path )
269                 return 0;
270         version = atoi( base );
271
272         /* printer driver name */
273         
274         keystr = new_path;
275         reg_split_path( keystr, &base, &new_path );
276         /* new_path should be NULL here since this must be the last key */
277         if ( !base || new_path )
278                 return 0;
279         fstrcpy( driver, base );
280
281         w_result = get_a_printer_driver( &driver_ctr, 3, driver, env, version );
282
283         if ( !W_ERROR_IS_OK(w_result) )
284                 return -1;
285                 
286         /* build the values out of the driver information */
287         info3 = driver_ctr.info_3;
288         
289         filename = dos_basename( info3->driverpath );
290         regval_ctr_addvalue( val, "Driver",             REG_SZ,       filename, strlen(filename)+1 );
291         filename = dos_basename( info3->configfile );
292         regval_ctr_addvalue( val, "Configuration File", REG_SZ,       filename, strlen(filename)+1 );
293         filename = dos_basename( info3->datafile );
294         regval_ctr_addvalue( val, "Data File",          REG_SZ,       filename, strlen(filename)+1 );
295         filename = dos_basename( info3->helpfile );
296         regval_ctr_addvalue( val, "Help File",          REG_SZ,       filename, strlen(filename)+1 );
297                 
298         regval_ctr_addvalue( val, "Data Type",          REG_SZ,       info3->defaultdatatype, strlen(info3->defaultdatatype)+1 );
299         
300         regval_ctr_addvalue( val, "Version",            REG_DWORD,    (char*)&info3->cversion, sizeof(info3->cversion) );
301         
302         if ( info3->dependentfiles )
303         {
304                 /* place the list of dependent files in a single 
305                    character buffer, separating each file name by
306                    a NULL */
307                    
308                 for ( i=0; strcmp(info3->dependentfiles[i], ""); i++ )
309                 {
310                         /* strip the path to only the file's base name */
311                 
312                         filename = dos_basename( info3->dependentfiles[i] );
313                         
314                         length = strlen(filename);
315                 
316                         buffer2 = Realloc( buffer, buffer_size + length + 1 );
317                         if ( !buffer2 )
318                                 break;
319                         buffer = buffer2;
320                 
321                         memcpy( buffer+buffer_size, filename, length+1 );
322                 
323                         buffer_size += length + 1;
324                 }
325                 
326                 /* terminated by double NULL.  Add the final one here */
327                 
328                 buffer2 = Realloc( buffer, buffer_size + 1 );
329                 if ( !buffer2 ) {
330                         SAFE_FREE( buffer );
331                         buffer_size = 0;
332                 }
333                 else {
334                         buffer = buffer2;
335                         buffer[buffer_size++] = '\0';
336                 }
337         }
338         
339         regval_ctr_addvalue( val, "Dependent Files",    REG_MULTI_SZ, buffer, buffer_size );
340         
341         free_a_printer_driver( driver_ctr, 3 );
342         SAFE_FREE( key2 );
343         SAFE_FREE( buffer );
344                 
345         DEBUG(8,("print_subpath_values_environments: Exit\n"));
346         
347         return regval_ctr_numvals( val );
348 }
349
350
351 /**********************************************************************
352  handle enumeration of subkeys below KEY_PRINTING\Forms
353  Really just a stub function, but left here in case it needs to
354  be expanded later on
355  *********************************************************************/
356  
357 static int print_subpath_forms( char *key, REGSUBKEY_CTR *subkeys )
358 {
359         DEBUG(10,("print_subpath_forms: key=>[%s]\n", key ? key : "NULL" ));
360         
361         /* there are no subkeys */
362         
363         if ( key )
364                 return -1;
365         
366         return 0;
367 }
368
369 /**********************************************************************
370  handle enumeration of values below KEY_PRINTING\Forms
371  *********************************************************************/
372  
373 static int print_subpath_values_forms( char *key, REGVAL_CTR *val )
374 {
375         int             num_values = 0;
376         uint32          data[8];
377         int             form_index = 1;
378         
379         DEBUG(10,("print_values_forms: key=>[%s]\n", key ? key : "NULL" ));
380         
381         /* handle ..\Forms\ */
382         
383         if ( !key )
384         {
385                 nt_forms_struct *forms_list = NULL;
386                 nt_forms_struct *form = NULL;
387                 int i;
388                 
389                 if ( (num_values = get_ntforms( &forms_list )) == 0 ) 
390                         return 0;
391                 
392                 DEBUG(10,("print_subpath_values_forms: [%d] user defined forms returned\n",
393                         num_values));
394
395                 /* handle user defined forms */
396                                 
397                 for ( i=0; i<num_values; i++ )
398                 {
399                         form = &forms_list[i];
400                         
401                         data[0] = form->width;
402                         data[1] = form->length;
403                         data[2] = form->left;
404                         data[3] = form->top;
405                         data[4] = form->right;
406                         data[5] = form->bottom;
407                         data[6] = form_index++;
408                         data[7] = form->flag;
409                         
410                         regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
411                 
412                 }
413                 
414                 SAFE_FREE( forms_list );
415                 forms_list = NULL;
416                 
417                 /* handle built-on forms */
418                 
419                 if ( (num_values = get_builtin_ntforms( &forms_list )) == 0 ) 
420                         return 0;
421                 
422                 DEBUG(10,("print_subpath_values_forms: [%d] built-in forms returned\n",
423                         num_values));
424                         
425                 for ( i=0; i<num_values; i++ )
426                 {
427                         form = &forms_list[i];
428                         
429                         data[0] = form->width;
430                         data[1] = form->length;
431                         data[2] = form->left;
432                         data[3] = form->top;
433                         data[4] = form->right;
434                         data[5] = form->bottom;
435                         data[6] = form_index++;
436                         data[7] = form->flag;
437                                         
438                         regval_ctr_addvalue( val, form->name, REG_BINARY, (char*)data, sizeof(data) );
439                 }
440                 
441                 SAFE_FREE( forms_list );
442         }
443         
444         return num_values;
445 }
446
447 /**********************************************************************
448  handle enumeration of subkeys below KEY_PRINTING\Printers
449  *********************************************************************/
450  
451 static int print_subpath_printers( char *key, REGSUBKEY_CTR *subkeys )
452 {
453         int n_services = lp_numservices();      
454         int snum;
455         fstring sname;
456         int num_subkeys = 0;
457         char *keystr, *key2 = NULL;
458         char *base, *new_path;
459         NT_PRINTER_INFO_LEVEL *printer = NULL;
460         
461         
462         DEBUG(10,("print_subpath_printers: key=>[%s]\n", key ? key : "NULL" ));
463         
464         if ( !key )
465         {
466                 /* enumerate all printers */
467                 
468                 for (snum=0; snum<n_services; snum++) {
469                         if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) )
470                                 continue;
471                                 
472                         fstrcpy( sname, lp_servicename(snum) );
473                                 
474                         regsubkey_ctr_addkey( subkeys, sname );
475                 }
476                 
477                 num_subkeys = regsubkey_ctr_numkeys( subkeys );
478                 goto done;
479         }
480
481         /* get information for a specific printer */
482         
483         key2 = strdup( key );
484         keystr = key2;
485         reg_split_path( keystr, &base, &new_path );
486         
487         
488         if ( !new_path ) {
489                 /* sanity check on the printer name */
490                 if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, base) ) )
491                         goto done;
492                 
493                 free_a_printer( &printer, 2 );
494                 
495                 regsubkey_ctr_addkey( subkeys, SPOOL_PRINTERDATA_KEY );
496         }
497         
498         /* no other subkeys below here */
499
500 done:   
501         SAFE_FREE( key2 );
502         return num_subkeys;
503 }
504
505 /**********************************************************************
506  handle enumeration of values below KEY_PRINTING\Printers
507  *********************************************************************/
508  
509 static int print_subpath_values_printers( char *key, REGVAL_CTR *val )
510 {
511         int             num_values = 0;
512         char            *keystr, *key2 = NULL;
513         char            *base, *new_path;
514         NT_PRINTER_INFO_LEVEL   *printer = NULL;
515         NT_PRINTER_INFO_LEVEL_2 *info2;
516         DEVICEMODE      *devmode;
517         prs_struct      prs;
518         uint32          offset;
519         int             snum;
520         int             i;
521         fstring         valuename;
522         uint8           *data;
523         uint32          type, data_len;
524         fstring         printername;
525         
526         /* 
527          * There are tw cases to deal with here
528          * (1) enumeration of printer_info_2 values
529          * (2) enumeration of the PrinterDriverData subney
530          */
531          
532         if ( !key ) {
533                 /* top level key has no values */
534                 goto done;
535         }
536         
537         key2 = strdup( key );
538         keystr = key2;
539         reg_split_path( keystr, &base, &new_path );
540         
541         fstrcpy( printername, base );
542         
543         if ( !new_path ) 
544         {
545                 /* we are dealing with the printer itself */
546
547                 if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, printername) ) )
548                         goto done;
549
550                 info2 = printer->info_2;
551                 
552
553                 regval_ctr_addvalue( val, "Attributes",       REG_DWORD, (char*)&info2->attributes,       sizeof(info2->attributes) );
554                 regval_ctr_addvalue( val, "Priority",         REG_DWORD, (char*)&info2->priority,         sizeof(info2->attributes) );
555                 regval_ctr_addvalue( val, "ChangeID",         REG_DWORD, (char*)&info2->changeid,         sizeof(info2->changeid) );
556                 regval_ctr_addvalue( val, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) );
557                 regval_ctr_addvalue( val, "Status",           REG_DWORD, (char*)&info2->status,           sizeof(info2->status) );
558                 regval_ctr_addvalue( val, "StartTime",        REG_DWORD, (char*)&info2->starttime,        sizeof(info2->starttime) );
559                 regval_ctr_addvalue( val, "UntilTime",        REG_DWORD, (char*)&info2->untiltime,        sizeof(info2->untiltime) );
560                 regval_ctr_addvalue( val, "cjobs",            REG_DWORD, (char*)&info2->cjobs,            sizeof(info2->cjobs) );
561                 regval_ctr_addvalue( val, "AveragePPM",       REG_DWORD, (char*)&info2->averageppm,       sizeof(info2->averageppm) );
562                 
563                 regval_ctr_addvalue( val, "Name",             REG_SZ, info2->printername,     sizeof(info2->printername)+1 );
564                 regval_ctr_addvalue( val, "Location",         REG_SZ, info2->location,        sizeof(info2->location)+1 );
565                 regval_ctr_addvalue( val, "Comment",          REG_SZ, info2->comment,         sizeof(info2->comment)+1 );
566                 regval_ctr_addvalue( val, "Parameters",       REG_SZ, info2->parameters,      sizeof(info2->parameters)+1 );
567                 regval_ctr_addvalue( val, "Port",             REG_SZ, info2->portname,        sizeof(info2->portname)+1 );
568                 regval_ctr_addvalue( val, "Server",           REG_SZ, info2->servername,      sizeof(info2->servername)+1 );
569                 regval_ctr_addvalue( val, "Share",            REG_SZ, info2->sharename,       sizeof(info2->sharename)+1 );
570                 regval_ctr_addvalue( val, "Driver",           REG_SZ, info2->drivername,      sizeof(info2->drivername)+1 );
571                 regval_ctr_addvalue( val, "Separator File",   REG_SZ, info2->sepfile,         sizeof(info2->sepfile)+1 );
572                 regval_ctr_addvalue( val, "Print Processor",  REG_SZ, "winprint",             sizeof("winprint")+1 );
573                 
574                 
575                 /* use a prs_struct for converting the devmode and security 
576                    descriptor to REG_BIARY */
577                 
578                 prs_init( &prs, MAX_PDU_FRAG_LEN, regval_ctr_getctx(val), MARSHALL);
579
580                 /* stream the device mode */
581                 
582                 snum = lp_servicenumber(info2->sharename);
583                 if ( (devmode = construct_dev_mode( snum )) != NULL )
584                 {                       
585                         if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) {
586                         
587                                 offset = prs_offset( &prs );
588                                 
589                                 regval_ctr_addvalue( val, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset );
590                         }
591                         
592                         
593                 }
594                 
595                 prs_mem_clear( &prs );
596                 prs_set_offset( &prs, 0 );
597                 
598                 if ( info2->secdesc_buf && info2->secdesc_buf->len ) 
599                 {
600                         if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sec, &prs, 0 ) ) {
601                         
602                                 offset = prs_offset( &prs );
603                         
604                                 regval_ctr_addvalue( val, "Security", REG_BINARY, prs_data_p(&prs), offset );
605                         }
606                 }
607
608                                 
609                 prs_mem_free( &prs );
610                 free_a_printer( &printer, 2 );  
611                 
612                 num_values = regval_ctr_numvals( val ); 
613                 goto done;
614                 
615         }
616         
617         
618         keystr = new_path;
619         reg_split_path( keystr, &base, &new_path );
620         
621         /* here should be no more path components here */
622         
623         if ( new_path || strcmp(base, SPOOL_PRINTERDATA_KEY) )
624                 goto done;
625                 
626         /* now enumerate the PrinterDriverData key */
627         if ( !W_ERROR_IS_OK( get_a_printer(&printer, 2, printername) ) )
628                 goto done;
629
630         info2 = printer->info_2;
631                 
632         
633         /* iterate over all printer data and fill the regval container */
634         
635 #if 0   /* JERRY */
636         for ( i=0; get_specific_param_by_index(*printer, 2, i, valuename, &data, &type, &data_len); i++ )
637         {
638                 regval_ctr_addvalue( val, valuename, type, data, data_len );
639         }
640 #endif
641                 
642         free_a_printer( &printer, 2 );
643
644         num_values = regval_ctr_numvals( val );
645         
646 done:
647         SAFE_FREE( key2 ); 
648         
649         return num_values;
650 }
651
652 /**********************************************************************
653  Routine to handle enumeration of subkeys and values 
654  below KEY_PRINTING (depending on whether or not subkeys/val are 
655  valid pointers. 
656  *********************************************************************/
657  
658 static int handle_printing_subpath( char *key, REGSUBKEY_CTR *subkeys, REGVAL_CTR *val )
659 {
660         int result = 0;
661         char *p, *base;
662         int i;
663         
664         DEBUG(10,("handle_printing_subpath: key=>[%s]\n", key ));
665         
666         /* 
667          * break off the first part of the path 
668          * topmost base **must** be one of the strings 
669          * in top_level_keys[]
670          */
671         
672         reg_split_path( key, &base, &p);
673                 
674         for ( i=0; i<MAX_TOP_LEVEL_KEYS; i++ ) {
675                 if ( StrCaseCmp( top_level_keys[i], base ) == 0 )
676                         break;
677         }
678         
679         DEBUG(10,("handle_printing_subpath: base=>[%s], i==[%d]\n", base, i));  
680                 
681         if ( !(i < MAX_TOP_LEVEL_KEYS) )
682                 return -1;
683                                                 
684         /* Call routine to handle each top level key */
685         switch ( i )
686         {
687                 case KEY_INDEX_ENVIR:
688                         if ( subkeys )
689                                 print_subpath_environments( p, subkeys );
690                         if ( val )
691                                 print_subpath_values_environments( p, val );
692                         break;
693                 
694                 case KEY_INDEX_FORMS:
695                         if ( subkeys )
696                                 print_subpath_forms( p, subkeys );
697                         if ( val )
698                                 print_subpath_values_forms( p, val );
699                         break;
700                         
701                 case KEY_INDEX_PRINTER:
702                         if ( subkeys )
703                                 print_subpath_printers( p, subkeys );
704                         if ( val )
705                                 print_subpath_values_printers( p, val );
706                         break;
707         
708                 /* default case for top level key that has no handler */
709                 
710                 default:
711                         break;
712         }
713         
714         
715         
716         return result;
717
718 }
719 /**********************************************************************
720  Enumerate registry subkey names given a registry path.  
721  Caller is responsible for freeing memory to **subkeys
722  *********************************************************************/
723  
724 int printing_subkey_info( char *key, REGSUBKEY_CTR *subkey_ctr )
725 {
726         char            *path;
727         BOOL            top_level = False;
728         int             num_subkeys = 0;
729         
730         DEBUG(10,("printing_subkey_info: key=>[%s]\n", key));
731         
732         path = trim_reg_path( key );
733         
734         /* check to see if we are dealing with the top level key */
735         
736         if ( !path )
737                 top_level = True;
738                 
739         if ( top_level ) {
740                 for ( num_subkeys=0; num_subkeys<MAX_TOP_LEVEL_KEYS; num_subkeys++ )
741                         regsubkey_ctr_addkey( subkey_ctr, top_level_keys[num_subkeys] );
742         }
743         else
744                 num_subkeys = handle_printing_subpath( path, subkey_ctr, NULL );
745         
746         SAFE_FREE( path );
747         
748         return num_subkeys;
749 }
750
751 /**********************************************************************
752  Enumerate registry values given a registry path.  
753  Caller is responsible for freeing memory 
754  *********************************************************************/
755
756 int printing_value_info( char *key, REGVAL_CTR *val )
757 {
758         char            *path;
759         BOOL            top_level = False;
760         int             num_values = 0;
761         
762         DEBUG(10,("printing_value_info: key=>[%s]\n", key));
763         
764         path = trim_reg_path( key );
765         
766         /* check to see if we are dealing with the top level key */
767         
768         if ( !path )
769                 top_level = True;
770         
771         /* fill in values from the getprinterdata_printer_server() */
772         if ( top_level )
773                 num_values = 0;
774         else
775                 num_values = handle_printing_subpath( path, NULL, val );
776                 
777         
778         return num_values;
779 }
780
781 /**********************************************************************
782  Stub function which always returns failure since we don't want
783  people storing printing information directly via regostry calls
784  (for now at least)
785  *********************************************************************/
786
787 BOOL printing_store_subkey( char *key, REGSUBKEY_CTR *subkeys )
788 {
789         return False;
790 }
791
792 /**********************************************************************
793  Stub function which always returns failure since we don't want
794  people storing printing information directly via regostry calls
795  (for now at least)
796  *********************************************************************/
797
798 BOOL printing_store_value( char *key, REGVAL_CTR *val )
799 {
800         return False;
801 }
802
803 /* 
804  * Table of function pointers for accessing printing data
805  */
806  
807 REGISTRY_OPS printing_ops = {
808         printing_subkey_info,
809         printing_value_info,
810         printing_store_subkey,
811         printing_store_value
812 };
813
814