delineation between smb and msrpc more marked. smbd now constructs
[tprouty/samba.git] / source / printing / nt_printing.c
1 #include "includes.h"
2 #include "nterr.h"
3
4 extern int DEBUGLEVEL;
5
6 /****************************************************************************
7 parse a form line.
8 ****************************************************************************/
9 static BOOL parse_form_entry(char *line, nt_forms_struct *buf)
10 {
11 #define NAMETOK   0
12 #define FLAGTOK   1
13 #define WIDTHTOK  2
14 #define LENGTHTOK 3
15 #define LEFTTOK   4
16 #define TOPTOK    5
17 #define RIGHTTOK  6
18 #define BOTTOMTOK 7
19 #define MAXTOK 8
20         char *tok[MAXTOK];
21         int count = 0;
22
23         tok[count] = strtok(line,":");
24         
25         /* strip the comment lines */
26         if (tok[0][0]=='#') return (False);     
27         count++;
28         
29         while ( ((tok[count] = strtok(NULL,":")) != NULL ) && count<MAXTOK-1)
30         {
31                 count++;
32         }
33
34         DEBUG(6,("Found [%d] tokens\n", count));
35
36         StrnCpy(buf->name,tok[NAMETOK],sizeof(buf->name)-1);
37         buf->flag=atoi(tok[FLAGTOK]);
38         buf->width=atoi(tok[WIDTHTOK]);
39         buf->length=atoi(tok[LENGTHTOK]);
40         buf->left=atoi(tok[LEFTTOK]);
41         buf->top=atoi(tok[TOPTOK]);
42         buf->right=atoi(tok[RIGHTTOK]);
43         buf->bottom=atoi(tok[BOTTOMTOK]);
44         
45         return(True);
46 }  
47   
48 /****************************************************************************
49 get a form struct list
50 ****************************************************************************/
51 int get_ntforms(nt_forms_struct **list)
52 {
53         FILE *f;
54         pstring line;
55         char *lp_forms = lp_nt_forms();
56         int total=0;
57         int grandtotal=0;
58         *line=0;
59
60         f = sys_fopen(lp_forms,"r");
61         if (!f)
62         {
63                 return(0);
64         }
65
66         while ( fgets(line, sizeof(pstring), f) )
67         {
68                 DEBUG(5,("%s\n",line));
69                 
70                 *list = Realloc(*list, sizeof(nt_forms_struct)*(total+1));
71                 if (! *list)
72                 {
73                         total = 0;
74                         break;
75                 }
76                 bzero( (char *)&(*list)[total], sizeof(nt_forms_struct) );
77                 if ( parse_form_entry(line, &(*list)[total] ) )
78                 {
79                         total++;
80                 }
81                 grandtotal++;
82         }    
83         fclose(f);
84
85         DEBUG(4,("%d info lines on %d\n",total, grandtotal));
86
87         return(total);
88 }
89
90 /****************************************************************************
91 write a form struct list
92 ****************************************************************************/
93 int write_ntforms(nt_forms_struct **list, int number)
94 {
95        FILE *f;
96        pstring line;
97        char *file = lp_nt_forms();
98        int total=0;
99        int i;
100
101        *line=0;
102
103        DEBUG(6,("write_ntforms\n"));
104
105        if((f = sys_fopen(file, "w")) == NULL)
106        {
107                DEBUG(1, ("cannot create forms file [%s]\n", file));
108                return(0);
109        }
110
111        for (i=0; i<number;i++)
112        {
113
114                fprintf(f,"%s:%d:%d:%d:%d:%d:%d:%d\n", (*list)[i].name,
115                        (*list)[i].flag, (*list)[i].width, (*list)[i].length,
116                        (*list)[i].left, (*list)[i].top, (*list)[i].right, (*list)[i].bottom);
117
118                DEBUGADD(7,("adding entry [%s]\n", (*list)[i].name));
119        }
120
121        fclose(f);
122        DEBUGADD(6,("closing file\n"));
123        return(total);
124 }
125
126 /****************************************************************************
127 add a form struct at the end of the list
128 ****************************************************************************/
129 void add_a_form(nt_forms_struct **list, FORM form, int *count)
130 {
131         int n=0;
132         BOOL update;
133         fstring form_name;
134
135         /* 
136          * NT tries to add forms even when 
137          * they are already in the base
138          * only update the values if already present
139          */
140
141         update=False;
142         
143         unistr2_to_ascii(form_name, &(form.name), sizeof(form_name)-1);
144         for (n=0; n<*count && update==False; n++)
145         {
146                 if (!strncmp((*list)[n].name, form_name, strlen(form_name)))
147                 {
148                         DEBUG(3, ("NT workaround, [%s] already exists\n", form_name));
149                         update=True;
150                 }
151         }
152
153         if (update==False)
154         {
155                 *list=Realloc(*list, (n+1)*sizeof(nt_forms_struct));
156                 unistr2_to_ascii((*list)[n].name, &(form.name), sizeof((*list)[n].name)-1);
157                 (*count)++;
158         }
159         
160         (*list)[n].flag=form.flags;
161         (*list)[n].width=form.size_x;
162         (*list)[n].length=form.size_y;
163         (*list)[n].left=form.left;
164         (*list)[n].top=form.top;
165         (*list)[n].right=form.right;
166         (*list)[n].bottom=form.bottom;
167 }
168
169 /****************************************************************************
170 update a form struct 
171 ****************************************************************************/
172 void update_a_form(nt_forms_struct **list, FORM form, int count)
173 {
174         int n=0;
175         fstring form_name;
176         unistr2_to_ascii(form_name, &(form.name), sizeof(form_name)-1);
177
178         DEBUG(6, ("[%s]\n", form_name));
179         for (n=0; n<count; n++)
180         {
181                 DEBUGADD(6, ("n [%d]:[%s]\n", n, (*list)[n].name));
182                 if (!strncmp((*list)[n].name, form_name, strlen(form_name)))
183                         break;
184         }
185
186         if (n==count) return;
187
188         (*list)[n].flag=form.flags;
189         (*list)[n].width=form.size_x;
190         (*list)[n].length=form.size_y;
191         (*list)[n].left=form.left;
192         (*list)[n].top=form.top;
193         (*list)[n].right=form.right;
194         (*list)[n].bottom=form.bottom;
195 }
196  
197 /****************************************************************************
198 get the nt drivers list
199
200 open the directory and look-up the matching names
201 ****************************************************************************/
202 int get_ntdrivers(fstring **list, char *architecture)
203 {
204         DIR *dirp;
205         char *dpname;
206         fstring name_match;
207         fstring short_archi;
208         fstring driver_name;
209         int match_len;
210         int total=0;
211
212         DEBUG(5,("Getting the driver list from directory: [%s]\n", lp_nt_drivers_file()));
213         
214         *list=NULL;
215         dirp = opendir(lp_nt_drivers_file());
216
217         if (dirp == NULL)
218         {
219                 DEBUG(0,("Error opening driver directory [%s]\n",lp_nt_drivers_file())); 
220                 return(-1);
221         }
222         
223         get_short_archi(short_archi, architecture);
224         slprintf(name_match, sizeof(name_match)-1, "NTdriver_%s_", short_archi);
225         match_len=strlen(name_match);
226         
227         while ((dpname = readdirname(dirp)) != NULL)
228         {
229                 if (strncmp(dpname, name_match, match_len)==0)
230                 {
231                         DEBUGADD(7,("Found: [%s]\n", dpname));
232                         
233                         StrCpy(driver_name, dpname+match_len);
234                         all_string_sub(driver_name, "#", "/");                  
235                         *list = Realloc(*list, sizeof(fstring)*(total+1));
236                         StrnCpy((*list)[total], driver_name, strlen(driver_name));
237                         DEBUGADD(6,("Added: [%s]\n", driver_name));             
238                         total++;
239                 }
240         }
241
242         closedir(dirp);
243         return(total);
244 }
245
246 /****************************************************************************
247 function to do the mapping between the long architecture name and
248 the short one.
249 ****************************************************************************/
250 void get_short_archi(char *short_archi, char *long_archi)
251 {
252         struct table {
253                 char *long_archi;
254                 char *short_archi;
255         };
256         
257         struct table archi_table[]=
258         {
259                 {"Windows 4.0",          ""       },
260                 {"Windows NT x86",       "W32X86" },
261                 {"Windows NT R4000",     ""       },
262                 {"Windows NT Alpha_AXP", ""       },
263                 {"Windows NT PowerPC",   ""       },
264                 {NULL,                   ""       }
265         };
266         
267         int i=-1;
268
269         DEBUG(7,("Getting architecture dependant directory\n"));
270         do {
271                 i++;
272         } while ( (archi_table[i].long_archi!=NULL ) && strncmp(long_archi, archi_table[i].long_archi, strlen(long_archi)) );
273
274         if (archi_table[i].long_archi==NULL)
275         {
276                 DEBUGADD(7,("Unknown architecture [%s] !\n", long_archi));
277         }
278         StrnCpy (short_archi, archi_table[i].short_archi, strlen(archi_table[i].short_archi));
279
280         DEBUGADD(8,("index: [%d]\n", i));
281         DEBUGADD(8,("long architecture: [%s]\n", long_archi));
282         DEBUGADD(8,("short architecture: [%s]\n", short_archi));
283 }
284
285 /****************************************************************************
286 ****************************************************************************/
287 static uint32 add_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 *driver)
288 {
289         FILE *f;
290         pstring file;
291         fstring architecture;
292         fstring driver_name;
293         char **dependentfiles;
294
295         /* create a file in the dir lp_nt_driver_file */
296         /* with the full printer DRIVER name */
297         /* eg: "/usr/local/samba/lib/NTdriver_HP LaserJet 6MP" */
298         /* each name is really defining an *unique* printer model */
299         /* I don't want to mangle the name to find it back when enumerating */
300
301         /* il faut substituer les / par 1 autre caractere d'abord */
302         /* dans le nom de l'imprimante par un # ???*/
303
304         StrnCpy(driver_name, driver->name, sizeof(driver_name)-1);
305
306         all_string_sub(driver_name, "/", "#");
307
308         get_short_archi(architecture, driver->environment);
309                 
310         slprintf(file, sizeof(file)-1, "%s/NTdriver_%s_%s",
311                  lp_nt_drivers_file(), architecture, driver_name);
312                 
313         if((f = sys_fopen(file, "w")) == NULL)
314         {
315                 DEBUG(1, ("cannot create driver file [%s]\n", file));
316                 return(2);
317         }
318
319         /*
320          * cversion must be 2.
321          * when adding a printer ON the SERVER
322          * rpcAddPrinterDriver defines it to zero
323          * which is wrong !!!
324          *
325          * JFM, 4/14/99
326          */
327         driver->cversion=2;
328         
329         fprintf(f, "version:         %d\n", driver->cversion);
330         fprintf(f, "name:            %s\n", driver->name);
331         fprintf(f, "environment:     %s\n", driver->environment);
332         fprintf(f, "driverpath:      %s\n", driver->driverpath);
333         fprintf(f, "datafile:        %s\n", driver->datafile);
334         fprintf(f, "configfile:      %s\n", driver->configfile);
335         fprintf(f, "helpfile:        %s\n", driver->helpfile);
336         fprintf(f, "monitorname:     %s\n", driver->monitorname);
337         fprintf(f, "defaultdatatype: %s\n", driver->defaultdatatype);
338
339         /* and the dependants files */
340         
341         dependentfiles=driver->dependentfiles;
342         
343         while ( **dependentfiles != '\0' )
344         {
345                 fprintf(f, "dependentfile:   %s\n", *dependentfiles);
346                 dependentfiles++;
347         }
348         
349         fclose(f);      
350         return(0);
351 }
352
353 /****************************************************************************
354 ****************************************************************************/
355 static uint32 get_a_printer_driver_3(NT_PRINTER_DRIVER_INFO_LEVEL_3 **info_ptr, fstring in_prt, fstring in_arch)
356 {
357         FILE *f;
358         pstring file;
359         fstring driver_name;
360         fstring architecture;
361         NT_PRINTER_DRIVER_INFO_LEVEL_3 *info;
362         char *line;
363         fstring p;
364         char *v;
365         int i=0;
366         char **dependentfiles=NULL;
367         
368         /*
369          * replace all the / by # in the driver name
370          * get the short architecture name
371          * construct the driver file name
372          */
373         StrnCpy(driver_name, in_prt, sizeof(driver_name)-1);
374         all_string_sub(driver_name, "/", "#");
375
376         get_short_archi(architecture, in_arch);
377                 
378         slprintf(file, sizeof(file)-1, "%s/NTdriver_%s_%s",
379                  lp_nt_drivers_file(), architecture, driver_name);
380                         
381         if((f = sys_fopen(file, "r")) == NULL)
382         {
383                 DEBUG(2, ("cannot open printer driver file [%s]\n", file));
384                 return(2);
385         }
386
387         /* the file exists, allocate some memory */
388         info=(NT_PRINTER_DRIVER_INFO_LEVEL_3 *)malloc(sizeof(NT_PRINTER_DRIVER_INFO_LEVEL_3));
389         ZERO_STRUCTP(info);
390         
391         /* allocate a 4Kbytes buffer for parsing lines */
392         line=(char *)malloc(4096*sizeof(char));
393         
394         while ( fgets(line, 4095, f) )
395         {
396
397                 v=strncpyn(p, line, sizeof(p), ':');
398                 if (v==NULL)
399                 {
400                         DEBUG(1, ("malformed printer entry (no :)\n"));
401                         continue;
402                 }
403                 
404                 v++;
405                 
406                 trim_string(v, " ", NULL);
407                 trim_string(v, NULL, " ");
408                 trim_string(v, NULL, "\n");
409                 /* don't check if v==NULL as an empty arg is valid */
410                 
411                 if (!strncmp(p, "version", strlen("version")))
412                         info->cversion=atoi(v);
413
414                 if (!strncmp(p, "name", strlen("name")))
415                         StrnCpy(info->name, v, strlen(v));
416
417                 if (!strncmp(p, "environment", strlen("environment")))
418                         StrnCpy(info->environment, v, strlen(v));
419
420                 if (!strncmp(p, "driverpath", strlen("driverpath")))
421                         StrnCpy(info->driverpath, v, strlen(v));
422
423                 if (!strncmp(p, "datafile", strlen("datafile")))
424                         StrnCpy(info->datafile, v, strlen(v));
425
426                 if (!strncmp(p, "configfile", strlen("configfile")))
427                         StrnCpy(info->configfile, v, strlen(v));
428
429                 if (!strncmp(p, "helpfile", strlen("helpfile")))
430                         StrnCpy(info->helpfile, v, strlen(v));
431
432                 if (!strncmp(p, "monitorname", strlen("monitorname")))
433                         StrnCpy(info->monitorname, v, strlen(v));
434
435                 if (!strncmp(p, "defaultdatatype", strlen("defaultdatatype")))
436                         StrnCpy(info->defaultdatatype, v, strlen(v));
437
438                 if (!strncmp(p, "dependentfile", strlen("dependentfile")))
439                 {
440                         dependentfiles=(char **)Realloc(dependentfiles, sizeof(char *)*(i+1));
441                         
442                         dependentfiles[i]=(char *)malloc( sizeof(char)* (strlen(v)+1) );
443                         
444                         StrnCpy(dependentfiles[i], v, strlen(v) );
445                         i++;
446                 }
447
448         }
449         
450         free(line);
451         
452         fclose(f);
453         
454         dependentfiles=(char **)Realloc(dependentfiles, sizeof(char *)*(i+1));
455         dependentfiles[i]=(char *)malloc( sizeof(char) );
456         *dependentfiles[i]='\0';
457         
458         info->dependentfiles=dependentfiles;
459         
460         *info_ptr=info;
461         
462         return (0);     
463 }
464
465 /****************************************************************************
466 debugging function, dump at level 6 the struct in the logs
467 ****************************************************************************/
468 static uint32 dump_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
469 {
470         uint32 success;
471         NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
472         char **dependentfiles;  
473         
474         DEBUG(6,("Dumping printer driver at level [%d]\n", level));
475         
476         switch (level)
477         {
478                 case 3: 
479                 {
480                         if (driver.info_3 == NULL)
481                         {
482                                 DEBUGADD(3,("NULL pointer, memory not alloced ?\n"));
483                                 success=5;
484                         }
485                         else
486                         {
487                                 info3=driver.info_3;
488                         
489                                 DEBUGADD(6,("version:[%d]\n",         info3->cversion));
490                                 DEBUGADD(6,("name:[%s]\n",            info3->name));
491                                 DEBUGADD(6,("environment:[%s]\n",     info3->environment));
492                                 DEBUGADD(6,("driverpath:[%s]\n",      info3->driverpath));
493                                 DEBUGADD(6,("datafile:[%s]\n",        info3->datafile));
494                                 DEBUGADD(6,("configfile:[%s]\n",      info3->configfile));
495                                 DEBUGADD(6,("helpfile:[%s]\n",        info3->helpfile));
496                                 DEBUGADD(6,("monitorname:[%s]\n",     info3->monitorname));
497                                 DEBUGADD(6,("defaultdatatype:[%s]\n", info3->defaultdatatype));
498                                 
499                                 dependentfiles=info3->dependentfiles;
500         
501                                 while ( **dependentfiles != '\0' )
502                                 {
503                                         DEBUGADD(6,("dependentfile:[%s]\n", *dependentfiles));
504                                         dependentfiles++;
505                                 }
506                                 success=0;
507                         }
508                         break;
509                 }
510                 default:
511                         DEBUGADD(1,("Level not implemented\n"));
512                         success=1;
513                         break;
514         }
515         
516         return (success);
517 }
518
519 /****************************************************************************
520 ****************************************************************************/
521 static void add_a_devicemode(NT_DEVICEMODE *nt_devmode, FILE *f)
522 {
523         int i;
524         
525         fprintf(f, "formname: %s\n",      nt_devmode->formname);
526         fprintf(f, "specversion: %d\n",   nt_devmode->specversion);
527         fprintf(f, "driverversion: %d\n", nt_devmode->driverversion);
528         fprintf(f, "size: %d\n",          nt_devmode->size);
529         fprintf(f, "driverextra: %d\n",   nt_devmode->driverextra);
530         fprintf(f, "fields: %d\n",        nt_devmode->fields);
531         fprintf(f, "orientation: %d\n",   nt_devmode->orientation);
532         fprintf(f, "papersize: %d\n",     nt_devmode->papersize);
533         fprintf(f, "paperlength: %d\n",   nt_devmode->paperlength);
534         fprintf(f, "paperwidth: %d\n",    nt_devmode->paperwidth);
535         fprintf(f, "scale: %d\n",         nt_devmode->scale);
536         fprintf(f, "copies: %d\n",        nt_devmode->copies);
537         fprintf(f, "defaultsource: %d\n", nt_devmode->defaultsource);
538         fprintf(f, "printquality: %d\n",  nt_devmode->printquality);
539         fprintf(f, "color: %d\n",         nt_devmode->color);
540         fprintf(f, "duplex: %d\n",        nt_devmode->duplex);
541         fprintf(f, "yresolution: %d\n",   nt_devmode->yresolution);
542         fprintf(f, "ttoption: %d\n",      nt_devmode->ttoption);
543         fprintf(f, "collate: %d\n",       nt_devmode->collate);
544         fprintf(f, "icmmethod: %d\n",     nt_devmode->icmmethod);
545         fprintf(f, "icmintent: %d\n",     nt_devmode->icmintent);
546         fprintf(f, "mediatype: %d\n",     nt_devmode->mediatype);
547         fprintf(f, "dithertype: %d\n",    nt_devmode->dithertype);
548         
549         if (nt_devmode->private != NULL)
550         {
551                 fprintf(f, "private: ");                
552                 for (i=0; i<nt_devmode->driverextra; i++)
553                         fprintf(f, "%02X", nt_devmode->private[i]);
554                 fprintf(f, "\n");       
555         }
556 }
557
558 /****************************************************************************
559 ****************************************************************************/
560 static void save_specifics(NT_PRINTER_PARAM *param, FILE *f)
561 {
562         int i;
563         
564         while (param != NULL)
565         {
566                 fprintf(f, "specific: %s#%d#%d#", param->value, param->type, param->data_len);
567                 
568                 for (i=0; i<param->data_len; i++)
569                         fprintf(f, "%02X", param->data[i]);
570                 
571                 fprintf(f, "\n");
572         
573                 param=param->next;      
574         }
575 }
576
577 /****************************************************************************
578 ****************************************************************************/
579 static uint32 add_a_printer_2(NT_PRINTER_INFO_LEVEL_2 *info)
580 {
581         FILE *f;
582         pstring file;
583         fstring printer_name;
584         NT_DEVICEMODE *nt_devmode;
585         
586         /*
587          * JFM: one day I'll forget.
588          * below that's info->portname because that's the SAMBA sharename
589          * and I made NT 'thinks' it's the portname
590          * the info->sharename is the thing you can name when you add a printer
591          * that's the short-name when you create shared printer for 95/98
592          * So I've made a limitation in SAMBA: you can only have 1 printer model
593          * behind a SAMBA share.
594          */
595
596
597         StrnCpy(printer_name, info->portname, sizeof(printer_name)-1);
598                 
599         slprintf(file, sizeof(file)-1, "%s/NTprinter_%s",
600                  lp_nt_drivers_file(), printer_name);
601
602         /* create a file in the dir lp_nt_driver_file */
603         /* with the full printer name */
604         /* eg: "/usr/local/samba/lib/NTprinter_HP LaserJet 6MP" */
605         /* each name is really defining an *unique* printer model */
606         /* I don't want to mangle the name to find it back when enumerating */
607         
608         if((f = sys_fopen(file, "w")) == NULL)
609         {
610                 DEBUG(1, ("cannot create printer file [%s]\n", file));
611                 return(2);
612         }
613
614         fprintf(f, "attributes: %d\n", info->attributes);
615         fprintf(f, "priority: %d\n", info->priority);
616         fprintf(f, "default_priority: %d\n", info->default_priority);
617         fprintf(f, "starttime: %d\n", info->starttime);
618         fprintf(f, "untiltime: %d\n", info->untiltime);
619         fprintf(f, "status: %d\n", info->status);
620         fprintf(f, "cjobs: %d\n", info->cjobs);
621         fprintf(f, "averageppm: %d\n", info->averageppm);
622
623         /* 
624          * in addprinter: no servername and the printer is the name
625          * in setprinter: servername is \\server
626          *                and printer is \\server\\printer
627          *
628          * Samba manages only local printers.
629          * we currently don't support things like path=\\other_server\printer
630          */
631
632         if (info->servername[0]!='\0')
633         {
634                 trim_string(info->printername, info->servername, NULL);
635                 trim_string(info->printername, "\\", NULL);
636                 info->servername[0]='\0';
637         }
638
639         fprintf(f, "servername: %s\n", info->servername);
640         fprintf(f, "printername: %s\n", info->printername);
641         fprintf(f, "sharename: %s\n", info->sharename);
642         fprintf(f, "portname: %s\n", info->portname);
643         fprintf(f, "drivername: %s\n", info->drivername);
644         fprintf(f, "comment: %s\n", info->comment);
645         fprintf(f, "location: %s\n", info->location);
646         fprintf(f, "sepfile: %s\n", info->sepfile);
647         fprintf(f, "printprocessor: %s\n", info->printprocessor);
648         fprintf(f, "datatype: %s\n", info->datatype);
649         fprintf(f, "parameters: %s\n", info->parameters);
650
651         /* store the devmode and the private part if it exist */
652         nt_devmode=info->devmode;
653         if (nt_devmode!=NULL)
654         {
655                 add_a_devicemode(nt_devmode, f);
656         }
657         
658         /* and store the specific parameters */
659         if (info->specific != NULL)
660         {
661                 save_specifics(info->specific, f);
662         }
663         
664         fclose(f);
665         
666         return (0);     
667 }
668
669 /****************************************************************************
670 fill a NT_PRINTER_PARAM from a text file
671
672 used when reading from disk.
673 ****************************************************************************/
674 static void dissect_and_fill_a_param(NT_PRINTER_PARAM *param, char *v)
675 {
676         char *tok[5];
677         int count = 0;
678
679         DEBUG(5,("dissect_and_fill_a_param\n"));        
680                 
681         tok[count] = strtok(v,"#");
682         count++;
683         
684         while ( ((tok[count] = strtok(NULL,"#")) != NULL ) && count<4)
685         {
686                 count++;
687         }
688
689         StrnCpy(param->value, tok[0], sizeof(param->value)-1);
690         param->type=atoi(tok[1]);
691         param->data_len=atoi(tok[2]);
692         param->data=(uint8 *)malloc(param->data_len * sizeof(uint8));                   
693         strhex_to_str(param->data, 2*(param->data_len), tok[3]);                
694         param->next=NULL;       
695
696         DEBUGADD(5,("value:[%s], len:[%d]\n", param->value, param->data_len));
697 }
698
699 /****************************************************************************
700 fill a NT_PRINTER_PARAM from a text file
701
702 used when reading from disk.
703 ****************************************************************************/
704 void dump_a_param(NT_PRINTER_PARAM *param)
705 {
706         DEBUG(5,("dump_a_param\n"));
707         DEBUGADD(6,("value [%s]\n", param->value));
708         DEBUGADD(6,("type [%d]\n", param->type));
709         DEBUGADD(6,("data len [%d]\n", param->data_len));
710 }
711
712 /****************************************************************************
713 ****************************************************************************/
714 BOOL add_a_specific_param(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_PARAM *param)
715 {
716         NT_PRINTER_PARAM *current;
717         
718         DEBUG(8,("add_a_specific_param\n"));    
719
720         param->next=NULL;
721         
722         if (info_2->specific == NULL)
723         {
724                 info_2->specific=param;
725         }
726         else
727         {
728                 current=info_2->specific;               
729                 while (current->next != NULL) {
730                         current=current->next;
731                 }               
732                 current->next=param;
733         }
734         return (True);
735 }
736
737 /****************************************************************************
738 ****************************************************************************/
739 BOOL unlink_specific_param_if_exist(NT_PRINTER_INFO_LEVEL_2 *info_2, NT_PRINTER_PARAM *param)
740 {
741         NT_PRINTER_PARAM *current;
742         NT_PRINTER_PARAM *previous;
743         
744         current=info_2->specific;
745         previous=current;
746         
747         if (current==NULL) return (False);
748         
749         if ( !strcmp(current->value, param->value) && 
750             (strlen(current->value)==strlen(param->value)) )
751         {
752                 DEBUG(9,("deleting first value\n"));
753                 info_2->specific=current->next;
754                 free(current);
755                 DEBUG(9,("deleted first value\n"));
756                 return (True);
757         }
758
759         current=previous->next;
760                 
761         while ( current!=NULL )
762         {
763                 if (!strcmp(current->value, param->value) &&
764                     strlen(current->value)==strlen(param->value) )
765                 {
766                         DEBUG(9,("deleting current value\n"));
767                         previous->next=current->next;
768                         free(current);
769                         DEBUG(9,("deleted current value\n"));
770                         return(True);
771                 }
772                 
773                 previous=previous->next;
774                 current=current->next;
775         }
776         return (False);
777 }
778
779 /****************************************************************************
780 ****************************************************************************/
781 static uint32 get_a_printer_2(NT_PRINTER_INFO_LEVEL_2 **info_ptr, fstring sharename)
782 {
783         FILE *f;
784         pstring file;
785         fstring printer_name;
786         NT_PRINTER_INFO_LEVEL_2 *info;
787         NT_DEVICEMODE *nt_devmode;
788         NT_PRINTER_PARAM *param;
789         char *line;
790         fstring p;
791         char *v;
792                 
793         /*
794          * the sharename argument is the SAMBA sharename
795          */
796         StrnCpy(printer_name, sharename, sizeof(printer_name)-1);
797                 
798         slprintf(file, sizeof(file)-1, "%s/NTprinter_%s",
799                  lp_nt_drivers_file(), printer_name);
800         
801         if((f = sys_fopen(file, "r")) == NULL)
802         {
803                 DEBUG(2, ("cannot open printer file [%s]\n", file));
804                 return(2);
805         }
806
807         /* the file exists, allocate some memory */
808         info=(NT_PRINTER_INFO_LEVEL_2 *)malloc(sizeof(NT_PRINTER_INFO_LEVEL_2));
809         ZERO_STRUCTP(info);
810
811         nt_devmode=(NT_DEVICEMODE *)malloc(sizeof(NT_DEVICEMODE));
812         ZERO_STRUCTP(nt_devmode);
813         init_devicemode(nt_devmode);
814         
815         info->devmode=nt_devmode;
816
817         line=(char *)malloc(4096*sizeof(char));
818         
819         while ( fgets(line, 4095, f) )
820         {
821
822                 v=strncpyn(p, line, sizeof(p), ':');
823                 if (v==NULL)
824                 {
825                         DEBUG(1, ("malformed printer entry (no `:')\n"));
826                         DEBUGADD(2, ("line [%s]\n", line));             
827                         continue;
828                 }
829                 
830                 v++;
831                 
832                 trim_string(v, " ", NULL);
833                 trim_string(v, NULL, " ");
834                 trim_string(v, NULL, "\n");
835                 
836                 /* don't check if v==NULL as an empty arg is valid */
837                 
838                 DEBUGADD(15, ("[%s]:[%s]\n", p, v));
839
840                 /*
841                  * The PRINTER_INFO_2 fields
842                  */
843                 
844                 if (!strncmp(p, "attributes", strlen("attributes")))
845                         info->attributes=atoi(v);
846
847                 if (!strncmp(p, "priority", strlen("priority")))
848                         info->priority=atoi(v);
849
850                 if (!strncmp(p, "default_priority", strlen("default_priority")))
851                         info->default_priority=atoi(v);
852
853                 if (!strncmp(p, "starttime", strlen("starttime")))
854                         info->starttime=atoi(v);
855
856                 if (!strncmp(p, "untiltime", strlen("untiltime")))
857                         info->untiltime=atoi(v);
858
859                 if (!strncmp(p, "status", strlen("status")))
860                         info->status=atoi(v);
861
862                 if (!strncmp(p, "cjobs", strlen("cjobs")))
863                         info->cjobs=atoi(v);
864
865                 if (!strncmp(p, "averageppm", strlen("averageppm")))
866                         info->averageppm=atoi(v);
867                 
868                 if (!strncmp(p, "servername", strlen("servername")))
869                         StrnCpy(info->servername, v, strlen(v));
870
871                 if (!strncmp(p, "printername", strlen("printername")))
872                         StrnCpy(info->printername, v, strlen(v));
873
874                 if (!strncmp(p, "sharename", strlen("sharename")))
875                         StrnCpy(info->sharename, v, strlen(v));
876
877                 if (!strncmp(p, "portname", strlen("portname")))
878                         StrnCpy(info->portname, v, strlen(v));
879
880                 if (!strncmp(p, "drivername", strlen("drivername")))
881                         StrnCpy(info->drivername, v, strlen(v));
882
883                 if (!strncmp(p, "comment", strlen("comment")))
884                         StrnCpy(info->comment, v, strlen(v));
885
886                 if (!strncmp(p, "location", strlen("location")))
887                         StrnCpy(info->location, v, strlen(v));
888
889                 if (!strncmp(p, "sepfile", strlen("sepfile")))
890                         StrnCpy(info->sepfile, v, strlen(v));
891
892                 if (!strncmp(p, "printprocessor", strlen("printprocessor")))
893                         StrnCpy(info->printprocessor, v, strlen(v));
894
895                 if (!strncmp(p, "datatype", strlen("datatype")))
896                         StrnCpy(info->datatype, v, strlen(v));
897
898                 if (!strncmp(p, "parameters", strlen("parameters")))
899                         StrnCpy(info->parameters, v, strlen(v));
900
901                 /*
902                  * The DEVICEMODE fields
903                  */
904
905                 if (!strncmp(p, "formname", strlen("formname")))
906                         StrnCpy(nt_devmode->formname, v, strlen(v));
907                         
908                 if (!strncmp(p, "specversion", strlen("specversion")))
909                         nt_devmode->specversion=atoi(v);
910
911                 if (!strncmp(p, "driverversion", strlen("driverversion")))
912                         nt_devmode->driverversion=atoi(v);
913
914                 if (!strncmp(p, "size", strlen("size")))
915                         nt_devmode->size=atoi(v);
916
917                 if (!strncmp(p, "driverextra", strlen("driverextra")))
918                         nt_devmode->driverextra=atoi(v);
919
920                 if (!strncmp(p, "fields", strlen("fields")))
921                         nt_devmode->fields=atoi(v);
922
923                 if (!strncmp(p, "orientation", strlen("orientation")))
924                         nt_devmode->orientation=atoi(v);
925
926                 if (!strncmp(p, "papersize", strlen("papersize")))
927                         nt_devmode->papersize=atoi(v);
928
929                 if (!strncmp(p, "paperlength", strlen("paperlength")))
930                         nt_devmode->paperlength=atoi(v);
931
932                 if (!strncmp(p, "paperwidth", strlen("paperwidth")))
933                         nt_devmode->paperwidth=atoi(v);
934
935                 if (!strncmp(p, "scale", strlen("scale")))
936                         nt_devmode->scale=atoi(v);
937
938                 if (!strncmp(p, "copies", strlen("copies")))
939                         nt_devmode->copies=atoi(v);
940
941                 if (!strncmp(p, "defaultsource", strlen("defaultsource")))
942                         nt_devmode->defaultsource=atoi(v);
943
944                 if (!strncmp(p, "printquality", strlen("printquality")))
945                         nt_devmode->printquality=atoi(v);
946
947                 if (!strncmp(p, "color", strlen("color")))
948                         nt_devmode->color=atoi(v);
949
950                 if (!strncmp(p, "duplex", strlen("duplex")))
951                         nt_devmode->duplex=atoi(v);
952
953                 if (!strncmp(p, "yresolution", strlen("yresolution")))
954                         nt_devmode->yresolution=atoi(v);
955
956                 if (!strncmp(p, "ttoption", strlen("ttoption")))
957                         nt_devmode->ttoption=atoi(v);
958
959                 if (!strncmp(p, "collate", strlen("collate")))
960                         nt_devmode->collate=atoi(v);
961
962                 if (!strncmp(p, "icmmethod", strlen("icmmethod")))
963                         nt_devmode->icmmethod=atoi(v);
964
965                 if (!strncmp(p, "icmintent", strlen("icmintent")))
966                         nt_devmode->icmintent=atoi(v);
967
968                 if (!strncmp(p, "mediatype", strlen("mediatype")))
969                         nt_devmode->mediatype=atoi(v);
970
971                 if (!strncmp(p, "dithertype", strlen("dithertype")))
972                         nt_devmode->dithertype=atoi(v);
973                         
974                 if (!strncmp(p, "private", strlen("private")))
975                 {
976                         nt_devmode->private=(uint8 *)malloc(nt_devmode->driverextra*sizeof(uint8));
977                         strhex_to_str(nt_devmode->private, 2*nt_devmode->driverextra, v);
978                 }
979                 
980                 /* the specific */
981                 
982                 if (!strncmp(p, "specific", strlen("specific")))
983                 {
984                         param=(NT_PRINTER_PARAM *)malloc(sizeof(NT_PRINTER_PARAM));
985                         ZERO_STRUCTP(param);
986                         
987                         dissect_and_fill_a_param(param, v);
988                         
989                         dump_a_param(param);
990                         
991                         add_a_specific_param(info, param);
992                 }
993                 
994         }
995         fclose(f);
996         free(line);
997         
998         *info_ptr=info;
999         
1000         return (0);     
1001 }
1002
1003 /****************************************************************************
1004 debugging function, dump at level 6 the struct in the logs
1005 ****************************************************************************/
1006 static uint32 dump_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
1007 {
1008         uint32 success;
1009         NT_PRINTER_INFO_LEVEL_2 *info2;
1010         
1011         DEBUG(6,("Dumping printer at level [%d]\n", level));
1012         
1013         switch (level)
1014         {
1015                 case 2: 
1016                 {
1017                         if (printer.info_2 == NULL)
1018                         {
1019                                 DEBUGADD(3,("NULL pointer, memory not alloced ?\n"));
1020                                 success=5;
1021                         }
1022                         else
1023                         {
1024                                 info2=printer.info_2;
1025                         
1026                                 DEBUGADD(6,("attributes:[%d]\n",       info2->attributes));
1027                                 DEBUGADD(6,("priority:[%d]\n",         info2->priority));
1028                                 DEBUGADD(6,("default_priority:[%d]\n", info2->default_priority));
1029                                 DEBUGADD(6,("starttime:[%d]\n",        info2->starttime));
1030                                 DEBUGADD(6,("untiltime:[%d]\n",        info2->untiltime));
1031                                 DEBUGADD(6,("status:[%d]\n",           info2->status));
1032                                 DEBUGADD(6,("cjobs:[%d]\n",            info2->cjobs));
1033                                 DEBUGADD(6,("averageppm:[%d]\n",       info2->averageppm));
1034
1035                                 DEBUGADD(6,("servername:[%s]\n",       info2->servername));
1036                                 DEBUGADD(6,("printername:[%s]\n",      info2->printername));
1037                                 DEBUGADD(6,("sharename:[%s]\n",        info2->sharename));
1038                                 DEBUGADD(6,("portname:[%s]\n",         info2->portname));
1039                                 DEBUGADD(6,("drivername:[%s]\n",       info2->drivername));
1040                                 DEBUGADD(6,("comment:[%s]\n",          info2->comment));
1041                                 DEBUGADD(6,("location:[%s]\n",         info2->location));
1042                                 DEBUGADD(6,("sepfile:[%s]\n",          info2->sepfile));
1043                                 DEBUGADD(6,("printprocessor:[%s]\n",   info2->printprocessor));
1044                                 DEBUGADD(6,("datatype:[%s]\n",         info2->datatype));
1045                                 DEBUGADD(6,("parameters:[%s]\n",       info2->parameters));
1046                                 success=0;
1047                         }
1048                         break;
1049                 }
1050                 default:
1051                         DEBUGADD(1,("Level not implemented\n"));
1052                         success=1;
1053                         break;
1054         }
1055         
1056         return (success);
1057 }
1058
1059 /*
1060  * The function below are the high level ones.
1061  * only those ones must be called from the spoolss code.
1062  * JFM.
1063  */
1064
1065
1066 /****************************************************************************
1067 ****************************************************************************/
1068 uint32 add_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
1069 {
1070         uint32 success;
1071         
1072         dump_a_printer(printer, level); 
1073         
1074         switch (level)
1075         {
1076                 case 2: 
1077                 {
1078                         success=add_a_printer_2(printer.info_2);
1079                         break;
1080                 }
1081                 default:
1082                         success=1;
1083                         break;
1084         }
1085         
1086         return (success);
1087 }
1088
1089 /****************************************************************************
1090 ****************************************************************************/
1091 uint32 get_a_printer(NT_PRINTER_INFO_LEVEL *printer, uint32 level, fstring sharename)
1092 {
1093         uint32 success;
1094         
1095         switch (level)
1096         {
1097                 case 2: 
1098                 {
1099                         printer->info_2=NULL;
1100                         success=get_a_printer_2(&(printer->info_2), sharename);
1101                         break;
1102                 }
1103                 default:
1104                         success=1;
1105                         break;
1106         }
1107         
1108         dump_a_printer(*printer, level);
1109         return (success);
1110 }
1111
1112 /****************************************************************************
1113 ****************************************************************************/
1114 uint32 free_a_printer(NT_PRINTER_INFO_LEVEL printer, uint32 level)
1115 {
1116         uint32 success;
1117         DEBUG(4,("freeing a printer at level [%d]\n", level));
1118         
1119         switch (level)
1120         {
1121                 case 2: 
1122                 {
1123                         if (printer.info_2 != NULL)
1124                         {
1125                                 if ((printer.info_2)->devmode != NULL)
1126                                 {
1127                                         DEBUG(6,("deleting DEVMODE\n"));
1128                                         if ((printer.info_2)->devmode->private !=NULL )
1129                                                 free((printer.info_2)->devmode->private);
1130                                         free((printer.info_2)->devmode);
1131                                 }
1132                                 
1133                                 if ((printer.info_2)->specific != NULL)
1134                                 {
1135                                         NT_PRINTER_PARAM *param;
1136                                         NT_PRINTER_PARAM *next_param;
1137         
1138                                         param=(printer.info_2)->specific;
1139                                         
1140                                         while ( param != NULL)
1141                                         {
1142                                                 next_param=param->next;
1143                                                 DEBUG(6,("deleting param [%s]\n", param->value));
1144                                                 free(param->data);
1145                                                 free(param);
1146                                                 param=next_param;
1147                                         }
1148                                 }       
1149                                 
1150                                 free(printer.info_2);
1151                                 success=0;
1152                         }
1153                         else
1154                         {
1155                                 success=4;
1156                         }
1157                         break;
1158                 }
1159                 default:
1160                         success=1;
1161                         break;
1162         }
1163         return (success);
1164 }
1165
1166 /****************************************************************************
1167 ****************************************************************************/
1168 uint32 add_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
1169 {
1170         uint32 success;
1171         DEBUG(4,("adding a printer at level [%d]\n", level));
1172         dump_a_printer_driver(driver, level);
1173         
1174         switch (level)
1175         {
1176                 case 3: 
1177                 {
1178                         success=add_a_printer_driver_3(driver.info_3);
1179                         break;
1180                 }
1181                 default:
1182                         success=1;
1183                         break;
1184         }
1185         
1186         return (success);
1187 }
1188 /****************************************************************************
1189 ****************************************************************************/
1190 uint32 get_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL *driver, uint32 level, 
1191                             fstring printername, fstring architecture)
1192 {
1193         uint32 success;
1194         
1195         switch (level)
1196         {
1197                 case 3: 
1198                 {
1199                         success=get_a_printer_driver_3(&(driver->info_3), 
1200                                                        printername,
1201                                                        architecture);
1202                         break;
1203                 }
1204                 default:
1205                         success=1;
1206                         break;
1207         }
1208         
1209         dump_a_printer_driver(*driver, level);
1210         return (success);
1211 }
1212
1213 /****************************************************************************
1214 ****************************************************************************/
1215 uint32 free_a_printer_driver(NT_PRINTER_DRIVER_INFO_LEVEL driver, uint32 level)
1216 {
1217         uint32 success;
1218         NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3;
1219         char **dependentfiles;
1220         
1221         switch (level)
1222         {
1223                 case 3: 
1224                 {
1225                         if (driver.info_3 != NULL)
1226                         {
1227                                 info3=driver.info_3;
1228                                 dependentfiles=info3->dependentfiles;
1229         
1230                                 while ( **dependentfiles != '\0' )
1231                                 {
1232                                         free (*dependentfiles);
1233                                         dependentfiles++;
1234                                 }
1235                                 
1236                                 /* the last one (1 char !) */
1237                                 free (*dependentfiles);
1238                                 
1239                                 dependentfiles=info3->dependentfiles;
1240                                 free (dependentfiles);
1241                                 
1242                                 free(info3);
1243                                 success=0;
1244                         }
1245                         else
1246                         {
1247                                 success=4;
1248                         }
1249                         break;
1250                 }
1251                 default:
1252                         success=1;
1253                         break;
1254         }
1255         return (success);
1256 }
1257
1258 /****************************************************************************
1259 ****************************************************************************/
1260 BOOL get_specific_param_by_index(NT_PRINTER_INFO_LEVEL printer, uint32 level, uint32 param_index,
1261                                  fstring value, uint8 **data, uint32 *type, uint32 *len)
1262 {
1263         /* right now that's enough ! */ 
1264         NT_PRINTER_PARAM *param;
1265         int i=0;
1266         
1267         param=printer.info_2->specific;
1268         
1269         while (param != NULL && i < param_index)
1270         {
1271                 param=param->next;
1272                 i++;
1273         }
1274         
1275         if (param != NULL)
1276         {
1277                 /* exited because it exist */
1278                 *type=param->type;              
1279                 StrnCpy(value, param->value, sizeof(value)-1);
1280                 *data=(uint8 *)malloc(param->data_len*sizeof(uint8));
1281                 memcpy(*data, param->data, param->data_len);
1282                 *len=param->data_len;
1283                 return (True);
1284         }
1285         return (False);
1286 }
1287
1288 /****************************************************************************
1289 ****************************************************************************/
1290 BOOL get_specific_param(NT_PRINTER_INFO_LEVEL printer, uint32 level, 
1291                         fstring value, uint8 **data, uint32 *type, uint32 *len)
1292 {
1293         /* right now that's enough ! */ 
1294         NT_PRINTER_PARAM *param;
1295         
1296         DEBUG(5, ("get_specific_param\n"));
1297         
1298         param=printer.info_2->specific;
1299                 
1300         while (param != NULL)
1301         {
1302                 if ( !strcmp(value, param->value) 
1303                     && strlen(value)==strlen(param->value))
1304                         break;
1305                         
1306                 param=param->next;
1307         }
1308         
1309         DEBUG(6, ("found one param\n"));
1310         if (param != NULL)
1311         {
1312                 /* exited because it exist */
1313                 *type=param->type;      
1314                 
1315                 *data=(uint8 *)malloc(param->data_len*sizeof(uint8));
1316                 memcpy(*data, param->data, param->data_len);
1317                 *len=param->data_len;
1318
1319                 DEBUG(6, ("exit of get_specific_param:true\n"));
1320                 return (True);
1321         }
1322         DEBUG(6, ("exit of get_specific_param:false\n"));
1323         return (False);
1324 }
1325
1326 /****************************************************************************
1327 ****************************************************************************/
1328 void init_devicemode(NT_DEVICEMODE *nt_devmode)
1329 {
1330 /*
1331  * should I init this ones ???
1332         nt_devmode->devicename
1333 */
1334         StrCpy(nt_devmode->formname, "A4");
1335
1336         nt_devmode->specversion      = 0x0401;
1337         nt_devmode->driverversion    = 0x0400;
1338         nt_devmode->size             = 0x00DC;
1339         nt_devmode->driverextra      = 0x0000;
1340         nt_devmode->fields           = FORMNAME | TTOPTION | PRINTQUALITY | 
1341                                        DEFAULTSOURCE | COPIES | SCALE | 
1342                                        PAPERSIZE | ORIENTATION;
1343         nt_devmode->orientation      = 1;
1344         nt_devmode->papersize        = PAPER_A4;
1345         nt_devmode->paperlength      = 0;
1346         nt_devmode->paperwidth       = 0;
1347         nt_devmode->scale            = 0x64;
1348         nt_devmode->copies           = 01;
1349         nt_devmode->defaultsource    = BIN_FORMSOURCE;
1350         nt_devmode->printquality     = 0x0258;
1351         nt_devmode->color            = COLOR_MONOCHROME;
1352         nt_devmode->duplex           = DUP_SIMPLEX;
1353         nt_devmode->yresolution      = 0;
1354         nt_devmode->ttoption         = TT_SUBDEV;
1355         nt_devmode->collate          = COLLATE_FALSE;
1356         nt_devmode->icmmethod        = 0;
1357         nt_devmode->icmintent        = 0;
1358         nt_devmode->mediatype        = 0;
1359         nt_devmode->dithertype       = 0;
1360
1361         /* non utilis├ęs par un driver d'imprimante */
1362         nt_devmode->logpixels        = 0;
1363         nt_devmode->bitsperpel       = 0;
1364         nt_devmode->pelswidth        = 0;
1365         nt_devmode->pelsheight       = 0;
1366         nt_devmode->displayflags     = 0;
1367         nt_devmode->displayfrequency = 0;
1368         nt_devmode->reserved1        = 0;
1369         nt_devmode->reserved2        = 0;
1370         nt_devmode->panningwidth     = 0;
1371         nt_devmode->panningheight    = 0;
1372         
1373         nt_devmode->private=NULL;
1374 }
1375
1376 /* error code:
1377         0: everything OK
1378         1: level not implemented
1379         2: file doesn't exist
1380         3: can't allocate memory
1381         4: can't free memory
1382         5: non existant struct
1383 */
1384
1385 /*
1386         A printer and a printer driver are 2 different things.
1387         NT manages them separatelly, Samba does the same.
1388         Why ? Simply because it's easier and it makes sense !
1389         
1390         Now explanation: You have 3 printers behind your samba server,
1391         2 of them are the same make and model (laser A and B). But laser B 
1392         has an 3000 sheet feeder and laser A doesn't such an option.
1393         Your third printer is an old dot-matrix model for the accounting :-).
1394         
1395         If the /usr/local/samba/lib directory (default dir), you will have
1396         5 files to describe all of this.
1397         
1398         3 files for the printers (1 by printer):
1399                 NTprinter_laser A
1400                 NTprinter_laser B
1401                 NTprinter_accounting
1402         2 files for the drivers (1 for the laser and 1 for the dot matrix)
1403                 NTdriver_printer model X
1404                 NTdriver_printer model Y
1405
1406 jfm: I should use this comment for the text file to explain 
1407         same thing for the forms BTW.
1408         Je devrais mettre mes commentaires en francais, ca serait mieux :-)
1409
1410 */
1411
1412