Code

Created trunk inside of 2.6-lhm
[gosa.git] / trunk / gosa-plugins / sudo / admin / sudo / class_sudoOption.inc
1 <?php
2 /*
3  * This code is part of GOsa (http://www.gosa-project.org)
4  * Copyright (C) 2003-2008 GONICUS GmbH
5  *
6  * ID: $$Id: class_sudo.inc 9975 2008-03-25 14:09:30Z hickert $$
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
24 /*! \brief  Sudo option class.
25             Allows setting flags/options for a sudo role.
26  */
27 class sudoOption extends plugin
28 {
29   /* Group attributes */
30   protected $sudoOption = array();
31   public $attributes    = array("sudoOption");
32   private $options = array();
33   public $ignore_account = TRUE;
35   /*! \brief  Initializes this class
36         @param  Object $config  The GOsa configuration object.
37         @param  String $dn      The object dn.
38    */
39   function sudoOption(&$config, $dn= NULL)
40   {
41     plugin::plugin ($config, $dn);
43     /****
44       Create a list of known options
45      ****/
46     $options = array();
47     $option['long_otp_prompt']= array('NAME' =>'long_otp_prompt' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
48     $option['ignore_dot']=  array('NAME' =>'ignore_dot' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
49     $option['mail_always']= array('NAME' =>'mail_always' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
50     $option['mail_badpass']=  array('NAME' =>'mail_badpass' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
51     $option['mail_no_user']=  array('NAME' =>'mail_no_user' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
52     $option['mail_no_host']=  array('NAME' =>'mail_no_host' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
53     $option['mail_no_perms']= array('NAME' =>'mail_no_perms' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
54     $option['tty_tickets']= array('NAME' =>'tty_tickets' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
55     $option['authenticate']=  array('NAME' =>'authenticate' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
56     $option['root_sudo']= array('NAME' =>'root_sudo' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
57     $option['log_host']=  array('NAME' =>'log_host' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
58     $option['log_year']=  array('NAME' =>'log_year' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
59     $option['shell_noargs']=  array('NAME' =>'shell_noargs' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
60     $option['set_home']=  array('NAME' =>'set_home' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
61     $option['always_set_home']= array('NAME' =>'always_set_home' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
62     $option['path_info']= array('NAME' =>'path_info' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
63     $option['preserve_groups']= array('NAME' =>'preserve_groups' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
64     $option['fqdn']=  array('NAME' =>'fqdn' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
65     $option['insults']= array('NAME' =>'insults' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
66     $option['requiretty']=  array('NAME' =>'requiretty' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
67     $option['env_editor']=  array('NAME' =>'env_editor' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
68     $option['rootpw']=  array('NAME' =>'rootpw' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
69     $option['runaspw']= array('NAME' =>'runaspw' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
70     $option['targetpw']=  array('NAME' =>'targetpw' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
71     $option['set_logname']= array('NAME' =>'set_logname' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
72     $option['stay_setuid']= array('NAME' =>'stay_setuid' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
73     $option['env_reset']= array('NAME' =>'env_reset' ,   'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'TRUE');
74     $option['use_loginclass']=  array('NAME' =>'use_loginclass' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
75     $option['noexec']=  array('NAME' =>'noexec' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
76     $option['ignore_local_sudoers']=  array('NAME' =>'ignore_local_sudoers' ,  'TYPE' => 'BOOLEAN' ,   'DEFAULT' => 'FALSE');
77     $option['passwd_tries']=  array('NAME' =>'passwd_tries' ,  'TYPE' => 'INTEGER' ,   'DEFAULT' => 3);
78     $option['loglinelen']=  array('NAME' =>'loglinelen' ,  'TYPE' => 'BOOL_INTEGER' ,  'DEFAULT' => 80);
79     $option['timestamp_timeout']= array('NAME' =>'timestamp_timeout' ,   'TYPE' => 'BOOL_INTEGER' ,  'DEFAULT' => 0);
80     $option['passwd_timeout']=  array('NAME' =>'passwd_timeout' ,  'TYPE' => 'BOOL_INTEGER' ,  'DEFAULT' => 15);
81     $option['umask']= array('NAME' =>'umask' ,   'TYPE' => 'BOOL_INTEGER' ,  'DEFAULT' => "0022");
82     $option['mailsub']= array('NAME' =>'mailsub' ,   'TYPE' => 'STRING' ,   'DEFAULT' => '*** SECURITY information for %h ***');
83     $option['badpass_message']= array('NAME' =>'badpass_message' ,   'TYPE' => 'STRING' ,   'DEFAULT' => 'Sorry, try again');
84     $option['timestampdir']=  array('NAME' =>'timestampdir' ,  'TYPE' => 'STRING' ,   'DEFAULT' => '/var/run/sudo');
85     $option['timestampowner']=  array('NAME' =>'timestampowner' ,  'TYPE' => 'STRING' ,   'DEFAULT' => 'root');
86     $option['passprompt']=  array('NAME' =>'passprompt' ,  'TYPE' => 'STRING' ,   'DEFAULT' => '[sudo] password for %p: ');
87     $option['runas_default']= array('NAME' =>'runas_default' ,   'TYPE' => 'STRING' ,   'DEFAULT' => 'root');
88     $option['syslog_goodpri']=  array('NAME' =>'syslog_goodpri' ,  'TYPE' => 'STRING' ,   'DEFAULT' => 'notice');
89     $option['syslog_badpri']= array('NAME' =>'syslog_badpri' ,   'TYPE' => 'STRING' ,   'DEFAULT' => 'alert');
90     $option['editor']=  array('NAME' =>'editor' ,  'TYPE' => 'STRING' ,   'DEFAULT' => '/usr/bin/vi');
91     $option['noexec_file']= array('NAME' =>'noexec_file' ,   'TYPE' => 'STRING' ,   'DEFAULT' => '/usr/lib/sudo/sudo_noexec.so');
92     $option['lecture']= array('NAME' =>'lecture' ,   'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => 'once');
93     $option['lecture_file']=  array('NAME' =>'lecture_file' ,  'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => '');
94     $option['logfile']= array('NAME' =>'logfile' ,   'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => 'syslog');
95     $option['syslog']=  array('NAME' =>'syslog' ,  'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => 'authpriv');
96     $option['mailerpath']=  array('NAME' =>'mailerpath' ,  'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => '');
97     $option['mailerflags']= array('NAME' =>'mailerflags' ,   'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => '-t');
98     $option['mailto']=  array('NAME' =>'mailto' ,  'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => 'root');
99     $option['exempt_group']=  array('NAME' =>'exempt_group' ,  'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => 'root');
100     $option['verifypw']=  array('NAME' =>'verifypw' ,  'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => 'all');
101     $option['listpw']=  array('NAME' =>'listpw' ,  'TYPE' => 'STRING_BOOL' ,   'DEFAULT' => 'any');
102     $option['env_check']= array('NAME' =>'env_check' ,   'TYPE' => 'LISTS' ,   'DEFAULT' => '');
103     $option['env_delete']=  array('NAME' =>'env_delete' ,  'TYPE' => 'LISTS' ,   'DEFAULT' => '');
104     $option['env_keep']=  array('NAME' =>'env_keep' ,  'TYPE' => 'LISTS' ,   'DEFAULT' => '');
105     ksort($option);
106     $this->options = $option;
108     $this->load_options();
109   }
112   private function load_options()
113   {
115     /****
116       Parse given sudoOption attributes 
117      ****/
118     $this->sudoOption = array();
119     if(isset($this->attrs['sudoOption'])){
120       for($i = 0 ; $i < $this->attrs['sudoOption']['count']; $i++){
122         /****
123           Detect attribute name/value/negation
124          ****/
125         $opt = $this->attrs['sudoOption'][$i];
127         /* Get negation */ 
128         $negation = FALSE;
129         if(preg_match("/^!/",$opt)){
130           $negation = TRUE; 
131           $opt = preg_replace("/^!/","",$opt);
132         }
134         /* Get value / name*/
135         $value    = "";
136         if(preg_match("/=/",$opt)){
137           $value  = preg_replace("/^[^=]*+=/","",$opt);
138           $opt    = preg_replace("/=.*$/","",$opt);
139         }
141         /* Special chars are escaped, remove escape char now.
142             \\ => \
143             \: => :
144             \, => ,
145             \= => = 
146          */
147         $value = $this->unescape_command($value);
149         /* Check if the given value is part of our options list.
150            If it is not, add it as type STRING and display a warning.  
151         */
152         if(!isset($this->options[$opt])){
153           $this->options[$opt]=array('NAME'=>$opt,'TYPE'=>'STRING','DEFAULT' => '');
154           msg_dialog::display(_("Unknown option"),
155               sprintf(_("The sudo option '%s' is invalid!"),
156                 $opt),WARNING_DIALOG);
157         }
159         /* Create internal sudoOption object */
160         $option = array();
161         $option['NAME']   = $opt;
162         $option['VALUE']  = $value;
163         $option['NEGATE'] = $negation;
165         /*  Special handling for mixed flag types. 
166             Some attributes like (BOOL_INTEGER) can be TRUE/FALSE and INTEGER.
167             This means, if the value is empty it is BOOL and $negation defines its boolean value.
168          */
169         if(in_array($this->options[$opt]['TYPE'],array("BOOL_INTEGER","STRING_BOOL"))){
170           if(empty($value)){
171             $option['NEGATE'] = FALSE;
172             if($negation){
173               $option['VALUE'] = "FALSE";
174             }else{
175               $option['VALUE'] = "TRUE";
176             }
177           }
178         }
180         /* Special handling for BOOLEAN values */
181         if(in_array($this->options[$opt]['TYPE'],array("BOOLEAN"))){
182           $option['NEGATE'] = FALSE;
183           if($negation){
184             $option['VALUE'] = "FALSE";
185           }else{
186             $option['VALUE'] = "TRUE";
187           }
188         }
190         /* Append values */
191         $this->sudoOption[$opt][] = $option;
192       }
193     }
194   }
198   /*! \brief  Create HTML output for this plugin 
199       @return String  HTML output for this plugin.
200     */
201   function execute()
202   {
203     /* Call parent execute */
204     plugin::execute();
206     /*****
207       Handle Posts 
208      *****/
209     if($this->acl_is_writeable("")){
211       foreach($_POST as $name => $value){
213         if(preg_match("/^negOption_/",$name)){
215           $opt = preg_replace("/^negOption_/","",$name);
216           $opt = preg_replace("/_[^_]*$/","",$opt);
217           $id  = preg_replace("/^.*_([0-9])*$/","\\1",$opt);
218           $opt = preg_replace("/_[0-9]*$/","",$opt);
220           if(isset($this->sudoOption[$opt][$id])){
221             $val = $this->sudoOption[$opt][$id]["VALUE"];
223             /*****
224               Negate STRING_BOOL && BOOL_INTEGER
225              *****/
226             if(in_array($this->options[$opt]['TYPE'],array('STRING_BOOL','BOOL_INTEGER'))){
227               if(in_array($val, array("TRUE","FALSE"))){
228                 if($val == "TRUE"){
229                   $this->sudoOption[$opt][$id]["VALUE"] = "FALSE";
230                 }else{
231                   $this->sudoOption[$opt][$id]["VALUE"] = "TRUE";
232                 }
233               }else{
234                 $this->sudoOption[$opt][$id]['NEGATE'] = !$this->sudoOption[$opt][$id]['NEGATE']; 
235               }
236             }
238             /*****
239               Negate STRING / INTEGER
240              *****/
241             if(in_array($this->options[$opt]['TYPE'],array('STRING','INTEGER','LISTS'))){
242               $this->sudoOption[$opt][$id]['NEGATE'] = !$this->sudoOption[$opt][$id]['NEGATE']; 
243             }
245             /*****
246               Negate BOOLEAN
247              *****/
248             if(in_array($this->options[$opt]['TYPE'],array('BOOLEAN'))){
249               if($val == "TRUE"){
250                 $this->sudoOption[$opt][$id]["VALUE"] = "FALSE";
251               }else{
252                 $this->sudoOption[$opt][$id]["VALUE"] = "TRUE";
253               }
254             }
255           }
256           break;
257         }
259         /*****
260           Remove options
261          *****/
262         if(preg_match("/^delOption/",$name)){
263           $opt = preg_replace("/^delOption_/","",$name);
264           $opt = preg_replace("/_[^_]*$/","",$opt);
265           $id  = preg_replace("/^.*_([0-9])*$/","\\1",$opt);
266           $opt = preg_replace("/_[0-9]*$/","",$opt);
268           if(isset($this->sudoOption[$opt][$id])){
269             unset($this->sudoOption[$opt][$id]);
270           }
271           if(!count($this->sudoOption[$opt])){
272             unset($this->sudoOption[$opt]);
273           }
274           break;
275         }
276       }
277     }
279  
280     $smarty = get_smarty();
281     $smarty->assign("ACL",$this->getacl(""));
282     $smarty->assign("map",  array("STRING" => _("string"), "BOOLEAN" => _("bool"),
283       "INTEGER" => _("integer") , "BOOL_INTEGER" => _("integer")."-"._("bool") ,
284       "STRING_BOOL" => _("string")."-"._("bool"),"LISTS" => _("list")));
285     $smarty->assign("sudoOption",$this->prepare_for_html($this->sudoOption));
286     $smarty->assign("options",$this->options);
287     return($smarty->fetch(get_template_path('options.tpl', TRUE)));
288   }
291   /*! \brief  Prepare options array to be used in HTML.
292       @param  Array   The options array ($this->sudoOption) 
293       @return Array   HTML ready sudoOption. Can now be used in smarty templates
294    */
295   function prepare_for_html($a_options)
296   {
297     foreach($a_options as $name => $options){
298       foreach($options as $key => $option){
299         $a_options[$name][$key]['VALUE'] = htmlentities($option['VALUE']);
300       }
301     }
302     return($a_options);
303   }
306   /*! \brief  Removes this plugin 
307    */
308   function remove_from_parent()
309   {
310   }
313   /*! \brief  Saves all relevant HTML post values for this plugin 
314    */
315   function save_object()
316   {
317     if($this->acl_is_writeable("")){
318       plugin::save_object();
320       if(isset($_POST['add_option']) && isset($_POST['option'])){
321         $opt = get_post("option");
323         /* Append attribute only once, lists are handled below */
324         if(isset($this->options[$opt])){
325           $type = $this->options[$opt]['TYPE'];
326           $val  = $this->options[$opt]['DEFAULT'];
327           $option = array("NAME" => $opt, "VALUE" => $val , "NEGATE" => FALSE);
328           $this->sudoOption[$opt][] = $option;
329         }
330       }
332       foreach($this->sudoOption as $name => $opts){
333         foreach($opts as $id => $opt){
335           /****
336             Get posted value for BOOLEAN
337            ****/
338           if(in_array($this->options[$name]['TYPE'],array("BOOLEAN"))){
339             if(isset($_POST['option_value__'.$name.'_'.$id])){
340               $this->sudoOption[$name][$id]["VALUE"] = get_post('option_value__'.$name.'_'.$id);
341             }
342           }
344           /****
345             Get posted value for STRING / INTEGER
346            ****/
347           if(in_array($this->options[$name]['TYPE'],array("STRING","INTEGER"))){
348             if(isset($_POST['option_value__'.$name.'_'.$id])){
349               $this->sudoOption[$name][$id]["VALUE"] = get_post('option_value__'.$name.'_'.$id);
350             }
351           }
353           /****
354             Get posted value for STRING_BOOL / BOOL_INTEGER
355            ****/
356           if(in_array($this->options[$name]['TYPE'],array("BOOL_INTEGER","STRING_BOOL"))){
357             if(isset($_POST['option_selection__'.$name.'_'.$id])){
358               $sel = get_post('option_selection__'.$name.'_'.$id);
359               $val = "";
360               if(isset($_POST['option_value__'.$name.'_'.$id])){
361                 $val = get_post('option_value__'.$name.'_'.$id);
362               }
364               if($sel == "FALSE" || $sel == "TRUE"){
365                 $this->sudoOption[$name][$id]['VALUE'] = $sel;
366                 $this->sudoOption[$name][$id]['NEGATE'] = FALSE;
367               }else{
368                 $this->sudoOption[$name][$id]['VALUE'] = $val;
369               }
370             }
371           }
373           /****
374             Get posted value for LISTS
375            ****/
376           if(in_array($this->options[$name]['TYPE'],array("LISTS"))){
377             foreach($this->sudoOption[$name] as $entry_key => $entry){
378               if(isset($_POST['list_value__'.$name.'_'.$entry_key])){
379                 $val = get_post('list_value__'.$name.'_'.$entry_key);
380                 $this->sudoOption[$name][$entry_key]["VALUE"] = $val;
381               }
382             } 
383           }
384         }
385       }
386     }
387   }
390   /*! \brief  Save changes to ldap 
391    */
392   function save()
393   {
394     plugin::save(); 
396     $this->attrs['sudoOption'] = array();
397     foreach($this->sudoOption as $name => $opts){
398       foreach($opts as $id => $opt){
400         $type   = $this->options[$name]['TYPE'];
401         $neg    = $opt['NEGATE'];
402         $value  = $opt['VALUE'];
403         $option = "";
405         /* Escape special chars */
406         $value = $this->escape_command($value);
408         /****
409           Save LISTS 
410          ****/
411         if($type=="LISTS"){
412           if($value == ""){
413             $option = $name;
414           }else{
415             $option = $name."=".$value;
416           }
417           if($neg){
418             $option = "!".$option;
419           }
420         }
422         /****
423           Save BOOLEAN
424          ****/
425         if(in_array($type,array("BOOLEAN"))){ 
426           $option = $name;
427           if($value == "FALSE"){
428             $option = "!".$option;
429           }
430         }
432         /****
433           Save STRING / INTEGER
434          ****/
435         if(in_array($type,array("STRING","INTEGER"))){ 
436           if($value != ""){
437             $option = $name."=".$value;
438           }else{
439             $option = $name; 
440           }
441           if($neg){
442             $option = "!".$option;
443           }
444         }
446         /****
447           Save STRING_BOOL / BOOL_INTEGER
448          ****/
449         if(in_array($type,array("STRING_BOOL","BOOL_INTEGER"))){
450           if($value == "FALSE"){
451             $option = "!".$name;
452           }elseif($value == "TRUE"){
453             $option = $name;
454           }else{
455             if($value != ""){
456               $option = $name."=".$value;
457             }else{
458               $option = $name; 
459             }
460             if($neg){
461               $option = "!".$option;
462             }
463           }
464         }
466         $this->attrs['sudoOption'][] = $option;
467       }
468     }
469     $this->cleanup();
470     $ldap = $this->config->get_ldap_link();
471     $ldap->cd($this->dn);
472     $ldap->modify($this->attrs);;
473   }
476   /*! \brief  Checks input validity
477    */
478   function check()
479   {
480     $message = plugin::check();
482     foreach($this->sudoOption as $name => $options){
483       foreach($options as $id => $option){
484         switch($this->options[$name]['TYPE']){
486           /* Check for a valid integer value */
487           case 'INTEGER' : 
488             {
489               if(!preg_match("/^[0-9]*$/",$option['VALUE'])){
490                 $message[] = msgPool::invalid($name,$option['VALUE'],"/[0-9]/");
491               }
492             } break;
493         }
494       }
495     }
496     return ($message);
497   }
500   /*! \brief  This function will be called if an object gets copied.
501               This function adapts attributes from the source object.
502       @param  Array The source object.
503    */
504   function PrepareForCopyPaste($source)
505   {
506     plugin::PrepareForCopyPaste($source);
507     if(isset($source['sudoOption'])){
508       $this->attrs['sudoOption'] = $source['sudoOption'];
509       $this->load_options();
510     }
511   }
514   /*!  \brief   Escape special chars in function parameters.
515        @param   String the string to that must be escaped.
516    */
517   private function escape_command($str)
518   {
519     /* Check if given value is a command (/[a-z]/ ..)
520      */
521     if(preg_match("/^\//",$str)){
522       $cmd = preg_replace("/^([^ ]*).*$/","\\1",$str);
523       $val = preg_replace("/^[^ ]*(.*)$/","\\1",$str);
524       $str = $cmd.addcslashes($val,":.,\\");
525     }
526     return($str);
527   }
530   /*! \brief  Add ACL object
531       @return Returns the ACL object.
532    */
533   static function plInfo()
534   {
535     return (array(  
536           "plShortName" => _("Options"),
537           "plDescription" => _("Sudo options"),
538           "plSelfModify"  => FALSE,
539           "plDepends"     => array(),
540           "plPriority"    => 2,
541           "plSection"     => array("administration"),
542           "plCategory"    => array("sudo"),
543           "plProvidedAcls"    => array()
544         ));
545   }
547   /*!  \brief   Unescape special chars in function parameters.
548        @param   String the string to that must be unescaped.
549    */
550   private function unescape_command($str)
551   {
552     /* Check if given value is a command (/[a-z]/ ..)
553      */
554     if(preg_match("/^\//",$str)){
555       $cmd = preg_replace("/^([^ ]*).*$/","\\1",$str);
556       $val = preg_replace("/^[^ ]*(.*)$/","\\1",$str);
557       $val = preg_replace(array("/\\\\\\\\/","/\\\\,/","/\\\\:/","/\\\\=/"),
558                               array("\\",",",":","="),$val);
559       $str = $cmd.$val;
560     }
561     return($str);
562   }
564 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
565 ?>