Code

Backport from trunk
[gosa.git] / gosa-plugins / goto / admin / systems / ppd / class_ppdManager.inc
1 <?php
3 class ppdManager
4 {
5     var $path= "";
6     var $cachedList= array();
7     var $timestamps = array();
9     var $useGzip  = true;
11     function ppdManager($path)
12     {
13         global $config;
14         if($config->boolValueIsTrue("core","ppdGzip")){
15             $this->useGzip  = false;
16         }
18         if(is_dir($path)){
19             $this->path= $path;
20         }elseif(is_link($path) && (is_dir(readlink($path)))) {
21             $this->path= $path;
22         } else {
23             msg_dialog::display(_("PPD manager error"), sprintf(_("The specified path '%s' does not exist."),$path), ERROR_DIALOG);
24             return (false);
25         }
26     }
29     function findPPD($path)
30     {
31         $list= array();
32         $currentDir= getcwd();
34         $dh = opendir($path);
35         while(false !== ($file = readdir($dh))){
37             /* Skip well known files */
38             if( $file == '.' || $file == '..'){
39                 continue;
40             }
42             /* Recurse through all "common" directories */
43             $check_path= $path.'/'.$file;
44             if(is_dir($check_path) || (is_link($check_path) && (is_dir(readlink($check_path))))){
45                 $list= array_merge($list, $this->findPPD($check_path));
46                 continue;
47             }
49             /* Check for PPD extension */
50             if (preg_match('/\.ppd(.gz)?$/i', $file)){
51                 $list[]= $path.'/'.$file;
52             }
53         }
55         closedir($dh);
56         chdir ($currentDir);
57         return ($list);
58     }
61     function updateAttribute($file, $section, $attribute, $value)
62     {
63         $fsection= false;
64         $fattribute= false;
65         $section= preg_replace('/^\*/', '', $section);
66         $attribute= preg_replace('/^\*/', '', $attribute);
68         if($this->useGzip){
69             $rp= @gzopen($file, "r");
70             $wp= @gzopen("$file.tmp", "w");
71         }else{
72             $rp= @gzopen($file, "r");
73             $wp= @fopen("$file.tmp", "w");
74         }
76         if($this->useGzip){     
77             while (!gzeof($rp)){
78                 $lines[]= gzgets($rp, 1024);
79             }
80         }else{
81             while (!feof($rp)){
82                 $lines[]= fgets($rp, 1024);
83             }
84         }
86         $ret = "";
87         $done =false;
88         foreach($lines as $nr => $line){
90             if (preg_match("/\*OpenGroup:*\s+\**$section\/*/", $line)){
91                 $fsection= true;
92                 $ret .=$line; 
93                 continue;
94             }
96             /* Change model name .. */
97             if ((preg_match("/^\*".$attribute.":*\s+/",$line)) && ($attribute == "ModelName")){
98                 $line= "*$attribute: \"$value\"\n";
99                 $done =true;
100             }
102             if (($fsection) && ($section != "NO_SECTION")){
103                 if (preg_match("/^\*CloseGroup:*\s+\**$section\/*/", $line)){
104                     $fsection= false;
105                     $ret .=$line; 
106                     continue;
107                 }
110                 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
111                     $fattribute= true;
112                     $ret .= $line; 
113                     continue;
114                 }
116                 if ($fattribute){
117                     if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
118                         $fattribute= false;
119                         $ret .= $line; 
120                         continue;
121                     }
123                     if (preg_match("/^\*Default$attribute:*\s+/", $line)){
124                         $line= "*Default$attribute: $value\n";
125                         $done =true;
126                     }
127                 }
128             }else{
129                 if (preg_match("/^\*OpenUI:*\s+\**$attribute\/*/", $line)){
130                     $fattribute= true;
131                     $ret .= $line; 
132                     continue;
133                 }
135                 if ($fattribute){
136                     if (preg_match("/^\*CloseUI:*\s+\**$attribute\/*/", $line)){
137                         $fattribute= false;
138                         $ret .= $line; 
139                         continue;
140                     }
142                     if (preg_match("/^\*Default$attribute:*\s+/", $line)){
143                         $line= "*Default$attribute: $value\n";
144                         $done =true;
145                     }
146                 }
147             }
148             $ret .=$line; 
149         }
151         if($this->useGzip){
152             gzwrite($wp,$ret);
153             gzclose($wp);
154             gzclose($rp);
155         }else{
156             fwrite($wp,$ret);
157             fclose($wp);
158             gzclose($rp);
159         }
161         copy("$file.tmp", "$file");
162         unlink("$file.tmp");
163     }
166     function saveProperties($ppdFile, $properties)
167     {
168         if(!is_readable($ppdFile)){
169             msg_dialog::display(_("PPD manager error"), sprintf(_("Specified PPD file '%s' cannot be opened for reading."),$ppdFile), ERROR_DIALOG);
170         }elseif(!is_writeable(preg_replace("#(^.*/).*$#","\\1",$ppdFile.".tmp"))){
171             msg_dialog::display(_("PPD manager error"), sprintf(_("The temporary file '%s' cannot be opened for writing."),$ppdFile.".tmp"), ERROR_DIALOG);
172         }else{
173             if(is_array($properties)){
174                 foreach ($properties as $name => $section){
175                     foreach ($section as $attribute => $value){
176                         if (is_array($value)){
177                             $this->updateAttribute($ppdFile, $name, $attribute, $value['_default']);
178                         }
179                     }
180                 }
181             }
182         }
183     }
185     function loadProperties($ppdFile)
186     {
187         $group= "";
188         $option= "";
189         $properties= array();
191         // Check for empty files
192         if(!filesize($ppdFile)) {
193             trigger_error(_('Parsing PPD file failed - file is empty!'));
194             return;
195         }
197         $fh= gzopen ($ppdFile, 'r');
198         while (!gzeof($fh) && $fh){
200             /* Read line */
201             $line= gzgets($fh, 256);
202             if (strlen($line) >= 256){
203                 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored!'), E_USER_WARNING);
204             }
206             /* Trigger for option groups */
207             if (preg_match('/^\*OpenGroup:/i', $line)){
209                 /* Sanity checks */
210                 if ($group != ""){
211                     trigger_error(_('Nested groups are not supported!'), E_USER_WARNING);
212                     continue;
213                 }
214                 if (in_array_strict($group, $properties)){
215                     trigger_error(_('Group name not unique!'), E_USER_WARNING);
216                     continue;
217                 }
219                 // TODO: Symbol values are not supported yet!
220                 if (preg_match('/\^/', $line)){
221                     trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
222                 }
223                 $complete= preg_replace('@^\*OpenGroup:\s+(.*)$@i', '\1', $line);
224                 $complete= trim($complete, '"');
225                 if (preg_match('@/@', $complete)){
226                     $group= trim(preg_replace('@^\*OpenGroup:\s+"?([^/]+)/.*$@i', '\1', $line));
227                     $name = preg_replace('@^\*OpenGroup:\s+"?[^/]+/([^/]+).*$@i', '\1', $line);
228                 } else {
229                     $group= $complete;
230                     $name = $complete;
231                 }
232                 $properties[$group]= array('_name' => $name);
233                 continue;
234             }
235             if (preg_match("/^\*CloseGroup:\s+\"?$group\"?/i", $line)){
236                 $group= "";
237                 continue;
238             }
240             /* Trigger for options */
241             if (preg_match('/^\*OpenUI\s+/i', $line)){
243                 /* Sanity check */
244                 if ($option != ""){
245                     trigger_error(_('Nested options are not supported!'), E_USER_WARNING);
246                     continue;
247                 }
249                 // TODO: Symbol values are not supported yet!
250                 if (preg_match('/\^/', $line)){
251                     trigger_error(_('Symbol values are not supported yet!'), E_USER_WARNING);
252                 }
253                 $complete= preg_replace('@^\*OpenUI\s+(.*)$@i', '\1', $line);
254                 $complete= trim($complete, '"');
255                 if (preg_match('@/@', $complete)){
256                     $option= trim(preg_replace('@^\*OpenUI\s+([^/]+)/.*$@i', '\1', $line));
257                     $name = trim(preg_replace('@^\*OpenUI\s+[^/]+/([^/]+).*$@i', '\1', $line));
258                 } else {
259                     $option= trim($complete);
260                     $name  = trim($complete);
261                 }
263                 /* Extract option type */
264                 $type= trim(preg_replace('/^[^:]+:\s+/', '', $line));
265                 $name= preg_replace('/:.*$/', '', $name);
266                 $option= preg_replace('/:.*$/', '', $option);
268                 // TODO: PickMany is not supported yet!
269                 if (preg_match('/PickMany/i', $type)){
270                     trigger_error(_('PickMany is not supported yet!'), E_USER_WARNING);
271                 }
272                 if(empty($group)){
273                     $properties["NO_SECTION"][$option]= array('_name' => $name, '_type' => $type);
274                 }else{
275                     $properties[$group][$option]= array('_name' => $name, '_type' => $type);
276                 }
277                 continue;
278             }
279             /* Show interest for option parsing */
280             if ($option != ""){
282                 $eoption= preg_replace('@\*@', '', $option);
284                 /* Close section? */
285                 if (preg_match("@^\*CloseUI:\s+\*$eoption@i", $line)){
286                     $option= "";
287                     continue;
288                 }
290                 /* Default value? */
291                 if (preg_match("@^\*Default$eoption:@", $line)){
292                     $c= preg_replace("@^\*Default$eoption:\s+@", "", $line);
293                     if(empty($group)){
294                         $properties["NO_SECTION"][$option]['_default']= trim(trim($c, '"'));
295                     }else{
296                         $properties[$group][$option]['_default']= trim(trim($c, '"'));
297                     }
298                     continue;
299                 }
301                 /* Possible value? */
302                 if (preg_match("@^\*$eoption\s+@", $line)){
304                     // Detect what comes first, the  '/' or the ':'. 
305                     // We may have entries like this: 
306                     //  *Resolution 300dpi: "<</HWResolution [300 300] >> setpagedevice"
307                     // and
308                     //  *Resolution 300dpi/300: "<</HWResolution [300 300] >> setpagedevice"
309                     $name = $value = preg_replace("@^\*$eoption\s+([^:]+).*$@", "$1", $line);
310                     if(preg_match("/\//", $name)){
311                         list($name, $value) = preg_split("/\//", $name);
312                     }
313                     if(empty($group)){
314                         $properties["NO_SECTION"][$option][$name]= $value;
315                     }else{
316                         $properties[$group][$option][$name]= $value;
317                     }
318                     continue;
319                 }
320             }
321         }
323         gzclose ($fh);
324         return ($properties);
325     }
327     function loadDescription($ppdFile)
328     {
329         // Check for empty files
330         if(!filesize($ppdFile)) {
331             trigger_error(_('Parsing PPD file failed - file is empty!'));
332             return;
333         }
335         $ppdDesc = array();
337         /* Only parse complete PPD file again, if it was changed */
338         $modified = filemtime($ppdFile);
339         if(isset($this->cachedList[$ppdFile]) && isset($this->timestamps[$ppdFile]) && $modified == $this->timestamps[$ppdFile]){
340             return($this->cachedList[$ppdFile]);
341         }
343         /* Remember modified timestamp, to speed up next request */
344         $this->timestamps[$ppdFile] = filemtime($ppdFile);
345         $fh= gzopen ($ppdFile, 'r');
347         while (!gzeof($fh) && $fh){
349             $line= gzgets($fh, 256);
350             if (strlen($line) >= 256){
351                 trigger_error(_('Parsing PPD file %s failed - line too long. Trailing characters have been ignored'), E_USER_WARNING);
352             }
354             /* Extract interesting informations */
355             if (preg_match('/^\*Manufacturer:/i', $line)){
356                 $ppdDesc['manufacturer'] = trim(preg_replace('/^\*Manufacturer:\s+"?([^"]+)"?.*$/i', '\1',    $line));
357             }
358             if (preg_match('/^\*ModelName:/i', $line)){
359                 $ppdDesc['model'] = trim(preg_replace('/^\*ModelName:\s+"?([^"]+)"?.*$/i', '\1', $line));
360             }
361             if (preg_match('/^\*ShortNickName:/i', $line)) { 
362                 $ppdDesc['nickname'] = trim(preg_replace('/^\*ShortNickName:\s+"?([^"]+)"?.*$/i', '\1', $line)); 
363             } 
365             /* Got everything we need? Skip rest for speed reasons... */
366             if (isset($ppdDesc['manufacturer'])  && isset($ppdDesc['model']) && isset($ppdDesc['nickname'])){
367                 break;
368             }
369         }
371         gzclose ($fh);
373         /* If model contains manufacturer strip it */
374         $ppdDesc['model'] = str_replace($ppdDesc['manufacturer']." ", '', $ppdDesc['model']);
376         /* Write out a notice that the PPD file seems to be broken if we can't
377            extract any usefull informations */
378         if ($ppdDesc['manufacturer'] == '' || $ppdDesc['model'] == ''){
379             trigger_error(sprintf(_('Parsing PPD file %s failed - no information found.'), $ppdFile), E_USER_WARNING);
380         }
382         $ppdDesc['name'] = $ppdDesc['manufacturer'] . " - " . $ppdDesc['model'];
383         return $ppdDesc;
384     }
387     function getPrinterList($reload= false)
388     {
389         /* Load list of PPD files */
390         if (count($this->cachedList) == 0 || $reload){
391             $list= $this->findPPD($this->path);
393             /* Load descriptive informations to build final printer list */
394             $new = array();
395             foreach ($list as $ppdFile){
396                 $new[$ppdFile] = $this->loadDescription($ppdFile); 
397             }
398             $this->cachedList= $new ;
400         }
402         return ($this->cachedList);
403     }
406 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
407 ?>