Code

Applied modifications for fixed PHP versions
[gosa.git] / include / functions_setup.inc
1 <?php
3 function check_schema_version($description, $version)
4 {
5   $desc= preg_replace("/^.* DESC\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $description);
7   return preg_match("/\(v$version\)/", $desc);
8 }
11 function view_schema_check($table)
12 {
13   $message="<table summary=\"\" class=\"check\">";
15   foreach ($table as $key => $values){
16     $msg = $values['msg'];
17     $message.= "<tr><td class=\"check\">$msg";
19     if($values['status']) {
20       $message.="</td><td style='text-align:center' >
21         <img src=images/true.png alt='true' /></td></tr>";
22     } else {
23       $message.="</td><td style='text-align:center' >
24         <img src=images/button_cancel.png alt='false' /></td></tr>";
25     }
26   }
27   $message.="</table>";
29   return $message;
30 }
33 function is_schema_readable($server, $admin, $password)
34 {
35   $ds= ldap_connect ($server);
36   if (!$ds) {
37     return (false);
38   }
39   ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
40   $r= ldap_bind ($ds, $admin, $password);
42   /* Get base to look for schema */
43   $sr  = @ldap_read ($ds, NULL, "objectClass=*", array("subschemaSubentry"));
44   $attr= @ldap_get_entries($ds,$sr);
45   if (!isset($attr[0]['subschemasubentry'][0])){
46     return (false);
47   }
49   $nb= $attr[0]['subschemasubentry'][0];
50   $objectclasses= array();
51   $sr= ldap_read ($ds, $nb, "objectClass=*", array("objectclasses"));
52   $attrs= ldap_get_entries($ds,$sr);
53   if (!isset($attrs[0])){
54     return (false);
55   }
56   return(true);
57
59 function schema_check($server, $admin, $password, $aff=0,$CalledByIndexPhP=false)
60 {
61   global $config;
63   $messages= array();
64   $required_classes= array(
65       "gosaObject"            => array("version" => "2.4"),
66       "gosaAccount"           => array("version" => "2.4"),
67       "gosaLockEntry"         => array("version" => "2.4"),
68       "gosaCacheEntry"        => array("version" => "2.4"),
69       "gosaDepartment"        => array("version" => "2.4"),
71       "goFaxAccount"          => array("version" => "1.0.4", "class" => "gofaxAccount","file" => "gofax.schema"),
72       "goFaxSBlock"           => array("version" => "1.0.4", "class" => "gofaxAccount","file" => "gofax.schema"),
73       "goFaxRBlock"           => array("version" => "1.0.4", "class" => "gofaxAccount","file" => "gofax.schema"),
75       "gosaUserTemplate"      => array("version" => "2.4", "class" => "posixAccount","file" => "nis.schema"),
76       "gosaMailAccount"       => array("version" => "2.4", "class" => "mailAccount","file" => "gosa+samba3.schema"),
77       "gosaProxyAccount"      => array("version" => "2.4", "class" => "proxyAccount","file" => "gosa+samba3.schema"),
78       "gosaApplication"       => array("version" => "2.4", "class" => "appgroup","file" => "gosa.schema"),
79       "gosaApplicationGroup"  => array("version" => "2.4", "class" => "appgroup","file" => "gosa.schema"),
81       "GOhard"                => array("version" => "2.4", "class" => "terminals","file" => "goto.schema"),
82       "gotoTerminal"          => array("version" => "2.0", "class" => "terminals","file" => "goto.schema"),
83       "goServer"              => array("version" => "2.4","class" => "server","file" => "goserver.schema"),
84       "goTerminalServer"      => array("version" => "2.4", "class" => "terminals","file" => "goto.schema"),
85       "goShareServer"           => array("version" => "2.4", "class" => "terminals","file" => "goto.schema"),
86       "goNtpServer"           => array("version" => "2.4", "class" => "terminals","file" => "goto.schema"),
87       "goSyslogServer"        => array("version" => "2.4", "class" => "terminals","file" => "goto.schema"),
88       "goLdapServer"          => array("version" => "2.4"),
89       "goCupsServer"          => array("version" => "2.4", "class" => array("posixAccount", "terminals"),),
90       "goImapServer"          => array("version" => "2.4", "class" => array("mailAccount", "mailgroup"),"file" => "gosa+samba3.schema"),
91       "goKrbServer"           => array("version" => "2.4"),
92       "goFaxServer"           => array("version" => "2.4", "class" => "gofaxAccount","file" => "gofax.schema"),
93       );
95   /* Build LDAP connection */
96   $ds= ldap_connect ($server);
97   if (!$ds) {
98     return (array(array("msg" => _("Can't bind to LDAP. No schema check possible!"), "status" => FALSE)));
99   }
100   ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
101   $r= ldap_bind ($ds, $admin, $password);
103   /* Get base to look for schema */
104   $sr  = @ldap_read ($ds, NULL, "objectClass=*", array("subschemaSubentry"));
105   $attr= @ldap_get_entries($ds,$sr);
106   if (!isset($attr[0]['subschemasubentry'][0])){
107     return (array(array("msg" => _("Can't get schema information from server. No schema check possible!"), "status" => FALSE)));
108   }
110   /* Get list of objectclasses */
111   $nb= $attr[0]['subschemasubentry'][0];
112   $objectclasses= array();
113   $sr= ldap_read ($ds, $nb, "objectClass=*", array("objectclasses"));
114   $attrs= ldap_get_entries($ds,$sr);
115   if (!isset($attrs[0])){
116     return (array(array("msg" => _("Can't get schema information from server. No schema check possible!"), "status" => FALSE)));
117   }
118   foreach ($attrs[0]['objectclasses'] as $val){
119     $name= preg_replace("/^.* NAME\s+\(*\s*'([^']+)'\s*\)*.*$/", '\\1', $val);
120     if ($name != $val){
121       $objectclasses[$name]= $val;
122     }
123   }
124   /* Walk through objectclasses and check if they are needed or not */
125   foreach ($required_classes as $key => $value){
126     if (isset($value['class'])){
127       if (!is_array($value['class'])){
128         $classes= array($value['class']);
129       } else {
130         $classes= $value['class'];
131       }
133       /* Check if we are using the class that requires */
134       foreach($classes as $class){
135         if (!isset($objectclasses[$key])){
136           $messages[$key]['msg']= sprintf(_("Optional objectclass '%s' required by plugin '%s' is not present in LDAP setup"), $key, $class);
137           $messages[$key]['status'] = FALSE;
138         } else {
139           if (!check_schema_version($objectclasses[$key], $value['version'])){
140             $messages[$key]['msg']= sprintf(_("Optional objectclass '%s' required by plugin '%s' does not have version %s"), $key, $class, $value['version']);
141             $messages[$key]['needonstartup'] = TRUE;
142             $messages[$key]['status'] =FALSE;
143           }else {
144             if(!isset($affich2[$class])){
145               $affich2[$class]['msg']   = sprintf(_("Support for '%s' enabled"), $class)."<td class=\"check\"> ".$value['file']."</td>";
146               $affich2[$class]['status']= TRUE; 
147             }
148           }
149         }
151       }
152     } else {
153       /* Required class */
154       if (!isset($objectclasses[$key])){
155         $messages[$key]['msg']= sprintf(_("Required objectclass '%s' is not present in LDAP setup"), $key);
156         $messages[$key]['status'] = FALSE;  
157       } else {
158         if (!check_schema_version($objectclasses[$key], $value['version'])){
159           $messages[$key]['msg']= sprintf(_("Required objectclass '%s' does not have version %s"), $key, $value['version']);
160           $messages[$key]['status'] = FALSE;  
161           $messages[$key]['needonstartup'] = TRUE;
162         }
163     
164       }
165     }
166   }
168   /* Check for correct samba parameters */
169   if (!isset($objectclasses['sambaSamAccount'])){
170     $messages['samba3']['msg']= _("SAMBA 3 support disabled, no schema seems to be installed");
171     $affich['samba3']['msg']= $messages['samba3']['msg']."<td class=\"check\">gosa+samba3.schema</td>";
172     $messages['samba3']['status']= FALSE;
173     $affich['samba3']['status']= FALSE;
174   }else{
175     $affich['samba3']['msg']= _("SAMBA 3 support enabled")."<td class=\"check\">gosa+samba3.schema</td>";
176     $affich['samba3']['status']= TRUE;
177   }
179   if (!isset($objectclasses['sambaAccount'])){
180     $messages['samba2']['msg']= _("SAMBA 2 support disabled, no schema seems to be installed");
181     $affich['samba2']['msg']= $messages['samba2']['msg']."<td class=\"check\">samba.schema</td>";
182     $messages['samba2']['status']= FALSE;
183     $affich['samba2']['status']= FALSE;
184   }else{
185     $affich['samba2']['msg']= _("SAMBA 2 support enabled")."<td class=\"check\">samba.schema</td>";
186     $affich['samba2']['status']= TRUE;
187   }
189   /* Check pureftp/dns/ */
190   if (!isset($objectclasses['PureFTPdUser'])){
191     $messages['pureftp']['msg']= _("Support for pureftp disabled, no schema seems to be installed");
192     $affich['pureftp']['msg']= $messages['pureftp']['msg']."<td class=\"check\">pureftpd.schema</td>";
193     $messages['pureftp']['status']= FALSE;
194     $affich['pureftp']['status']= FALSE;
195   }else{
196     $affich['pureftp']['msg']= _("Support for pureftp enabled")."<td class=\"check\">pureftpd.schema</td>";
197     $affich['pureftp']['status']= TRUE;
198   }
200   if (!isset($objectclasses['gosaWebdavAccount'])){
201     $messages['webdav']['msg']= _("Support for WebDAV disabled, no schema seems to be installed");
202     $affich['webdav']['msg']= $messages['webdav']['msg']."<td class=\"check\"></td>";
203     $messages['webdav']['status']= FALSE;
204     $affich['webdav']['status']= FALSE;
205   }else{
206     $affich['webdav']['msg']=_("Support for WebDAV enabled")."<td class=\"check\">gosa+samba3.schema</td>";
207     $affich['webdav']['status']= TRUE;
208   }
210   if (!isset($objectclasses['phpgwAccount'])){
211     $messages['phpgroupware']['msg']= _("Support for phpgroupware disabled, no schema seems to be installed");
212     $affich['phpgroupware']['msg']= $messages['phpgroupware']['msg']."<td class=\"check\">phpgwaccount.schema</td>";
213     $messages['phpgroupware']['status']= FALSE;
214     $affich['phpgroupware']['status']= FALSE;
215   }else{
216     $affich['phpgroupware']['msg']= _("Support for phpgroupware enabled")."<td class=\"check\">phpgwaccount.schema</td>";
217     $affich['phpgroupware']['status']= TRUE;
218   }
220   if (!isset($objectclasses['goFonAccount'])){
221     $messages['phoneaccount']['msg']= _("Support for gofon disabled, no schema seems to be installed");
222     $affich['phoneaccount']['msg']= $messages['phoneaccount']['msg']."<td class=\"check\">gofon.schema</td>";
223     $messages['phoneaccount']['status']= FALSE;
224     $affich['phoneaccount']['status']= FALSE;
225   }else{
226     $affich['phoneaccount']['msg']= _("Support for gofon enabled")."<td class=\"check\">gofon.schema</td>";
227     $affich['phoneaccount']['status']= true;
228   }
230   if (!isset($objectclasses['nagiosContact'])){
231     $messages['nagioscontact']['msg']= _("Support for nagios disabled, no schema seems to be installed");
232     $affich['nagioscontact']['msg']= $messages['nagioscontact']['msg']."<td class=\"check\">nagios.schema</td>";
233     $messages['nagioscontact']['status']= FALSE;
234     $affich['nagioscontact']['status']= FALSE;
235   }else{
236     $affich['nagioscontact']['msg']= _("Support for nagios enabled")."<td class=\"check\">nagios.schema</td>";
237     $affich['nagioscontact']['status']= true;
238   }
239   
240   /* Fix for PHP Fehler "Undefined index: ldapconf"
241    * Ablaufverfolgung[1]: Funktion schema_check Datei: /home/hickert/gosa/include/functions_setup.inc (Zeile 230)
242    */
243   if((isset($_SESSION['ldapconf']['mail_methods']))&&(isset($_SESSION['ldapconf']))){
244         if(($_SESSION['ldapconf']['mail_methods'][$_SESSION['ldapconf']['mail']] == "kolab")&&(!$CalledByIndexPhP)){
245           if(!isset($objectclasses['kolabInetOrgPerson']))  {
246             $messages['kolab']['msg']= _("Support for Kolab disabled, no schema seems to be installed, setting mail-method to cyrus");
247             $affich['kolab']['msg']=$messages['kolab']['msg']."<td class=\"check\">kolab2.schema</td>";
248             $tmp= array_flip($_SESSION['ldapconf']['mail_methods']);
249             $_SESSION['ldapconf']['mail']=$tmp['cyrus'];
250             $messages['kolab']['status']= FALSE;
251             $affich['kolab']['status']= FALSE;
252           }else{
253             $affich['kolab']['msg']=_("Support for Kolab enabled")."<td class=\"check\">gofon.schema</td>";
254             $affich['kolab']['status']= TRUE;
255           }
256         }
257   }
258   if($aff==0){
259     return ($messages);
260   } else {
261     return(array_merge($affich,$affich2));
262   }
266 function check(&$faults, $message, $description, $test, $required= TRUE)
268   $msg= "<table summary=\"\" class='check'><tr><td class='check' style='font-size:14px;'>$message</td>
269     <td rowspan=2 style='vertical-align:middle; text-align:center;width:45px;'>";
270   if ($test){
271     $msg.= _("OK")."<br>";
272   } else {
273     if (!$required){
274       $msg.="<font color=red>"._("Ignored")."</font><br>";
275     } else {
276       $msg.="<font color=red>"._("Failed")."</font><br>";
277       $faults++;
278     }
279   }
280   $msg.= "</td></tr><tr><td class='check' style='padding-left:20px;".
281     "background-color:#F0F0F0;'>$description</td></tr></table><br>";
283   return $msg;
286 function perform_php_checks(&$faults)
288   global $check_globals;
290   $faults= 0;
291   $msg= "";
293   $msg.= "<h1>"._("PHP setup inspection")."</h1>";
294   $msg.= check (        $faults, _("Checking for PHP version (>=4.1.0)"),
295       _("PHP must be of version 4.1.0 or above for some functions and known bugs in PHP language."),
296       version_compare(phpversion(), "4.1.0")>=0);
298   $msg.= check (        $faults, _("Checking if register_globals is set to 'off'"),
299       _("register_globals is a PHP mechanism to register all global varibales to be accessible from scripts without changing the scope. This may be a security risk. GOsa will run in both modes."),
300       $check_globals == 0, FALSE);
302   $msg.= check (        $faults, _("Checking for ldap module"),
303       _("This is the main module used by GOsa and therefore really required."),
304       is_callable('ldap_bind'));
306   $msg.= check (  $faults, _("Checking for XML functions"),
307       _("XML functions are required to parse the configuration file."),
308       is_callable('xml_parser_create'));
310   $msg.= check (        $faults, _("Checking for gettext support"),
311       _("Gettext support is required for internationalized GOsa."),
312       is_callable('bindtextdomain'));
314   $msg.= check (        $faults, _("Checking for iconv support"),
315       _("This module is used by GOsa to convert samba munged dial informations and is therefore required."),
316       is_callable('iconv'));
318   $msg.= check (        $faults, _("Checking for mhash module"),
319       _("To use SSHA encryption, you'll need this module. If you are just using crypt or md5 encryption, ignore this message. GOsa will run without it."),
320       is_callable('mhash'), FALSE);
322   $msg.= check (        $faults, _("Checking for imap module"),
323       _("The IMAP module is needed to communicate with the IMAP server. It gets status informations, creates and deletes mail users."),
324       is_callable('imap_open'));
326   $msg.= check (        $faults, _("Checking for getacl in imap"),
327       _("The getacl support is needed for shared folder permissions. The standard IMAP module is not capable of reading acl's. You need a recend PHP version for this feature."),
328       is_callable('imap_getacl'), FALSE);
330   $msg.= check (        $faults, _("Checking for mysql module"),
331       _("MySQL support is needed for reading GOfax reports from databases."),
332       is_callable('mysql_query'), FALSE);
334   $msg.= check (        $faults, _("Checking for cups module"),
335       _("In order to read available printers from IPP protocol instead of printcap files, you've to install the CUPS module."),
336       is_callable('cups_get_dest_list'), FALSE);
338   $msg.= check (        $faults, _("Checking for kadm5 module"),
339       _("Managing users in kerberos requires the kadm5 module which is downloadable via PEAR network."),
340       is_callable('kadm5_init_with_password'), FALSE);
342   $msg.= check (  $faults, _("Checking for snmp Module"),
343       _("Simple Network Management Protocol (SNMP) is required for client monitoring."),
344       is_callable('snmpget'), FALSE);
346   return ($msg);
349 function get_link($function_name) {
350   $result= "<a href='http://de.php.net/manual/en/function.";
352   /* Replace all underscores with hyphens (phpdoc convention) */
353   $function_name= str_replace("_", "-", $function_name);
355   /* Append to base URL */
356   $result.= $function_name.".php'>$function_name</a>";
358   return $result;
361 function perform_additional_function_checks(&$faults) {
362   global $check_globals;
364   $faults= 0;
365   $msg= "";
366   $functions= array();
367   
368   $functions_list= '../include/functions_list.inc';
370   /* Make sure that we can read the file */
371   if(is_readable($functions_list)) {
372     /* Open filehandle */
373     $fh= fopen($functions_list,'rb');
374     if($fh!=null) {
375       $functions= eval(fread($fh,filesize($functions_list)));
376     }
377   }
379   $msg.= "<h1>"._("PHP detailed function inspection")."</h1>";
380   /* Only print message, if function is not callable */
381   foreach($functions as $key => $fn_name) {
382     if(!is_callable($fn_name)) {
383       $msg.= check ($faults, sprintf(_("Checking for function %s"), "<b>".get_link($fn_name)."</b>"),
384         sprintf(_("The function %s is used by GOsa. There is no information if it's optional or required yet."), "<b>".get_link($fn_name)."</b>"),
385         is_callable($fn_name), false);
386     }
387   }
388   return $msg;
391 function perform_additional_checks(&$faults)
393   $ret = NULL;
394   /* Programm check */
395   $msg= "<h1>"._("Checking for some additional programms")."</h1>";
397   /* Image Magick */
398   $query= "LC_ALL=C LANG=C convert -help";
399   $output= shell_exec ($query);
400   if ($output != ""){
401     $lines= split ("\n", $output);
402     $version= preg_replace ("/^Version:.+Magick ([^\s]+).*/", "\\1", $lines[0]);
403     list($major, $minor)= split("\.", $version);
404     $msg.= check (      $faults, _("Checking for ImageMagick (>=5.4.0)"),
405         _("ImageMagick is used to convert user supplied images to fit the suggested size and the unified JPEG format."),
406         ($major > 5 || ($major == 5 && $minor >= 4)));
407   } else {
408     $msg.= check (      $faults, _("Checking imagick module for PHP"),
409         _("Imagick is used to convert user supplied images to fit the suggested size and the unified JPEG format from PHP script."), function_exists('imagick_blob2image'), TRUE);
410   }
412   /* Check for fping */
413   $query= "LC_ALL=C LANG=C fping -v 2>&1";
414   $output= shell_exec ($query);
415   $have_fping= preg_match("/^fping:/", $output);
416   $msg.= check (        $faults, _("Checking for fping utility"),
417       _("The fping utility is only used if you've got a thin client based terminal environment running."),
418       $have_fping, FALSE);
420   /* Check for smb hash generation tool */
421   $query= "mkntpwd 2>&1";
422   $output= shell_exec ($query);
423   $have_mkntpwd= preg_match("/^Usage: mkntpwd /", $output);
424   $alt = 0;
426   if (!$have_mkntpwd){
427     $query= "LC_ALL=C LANG=C perl -MCrypt::SmbHash -e 'ntlmgen \"PASSWD\", \$lm, \$nt; print \"\${lm}:\${nt}\\n\";' &>/dev/null";
428     system ($query, $ret);
429     $alt= ($ret == 0);
430   }
432   $msg.= check (        $faults, _("Checking for a way to generate LM/NT password hashes"),
433       _("In order to use SAMBA 2/3, you've to install some additional packages to generate password hashes."),
434       ($have_mkntpwd || $alt));
436   /* seesio.auto_start should be off, in order to without trouble*/
437   $session_auto_start = ini_get('session.auto_start');
438   $implicit_flush     = ini_get('implicit_flush');
439   $max_execution_time = ini_get('max_execution_time');
440   $memory_limit       = ini_get('memory_limit');
441   $expose_php         = ini_get('expose_php');
442   $magic_quotes_gpc   = ini_get('magic_quotes_gpc');
443   $register_globals   = ini_get('register_globals');
445   /* auto_register */
446   $msg.= check (  $faults, _("php.ini check -> session.auto_register"),
447       _("In Order to use GOsa without any trouble, the session.auto_register option in your php.ini must be set to 'Off'."), (!$session_auto_start['local_value']));
449   /* implicit_flush */
450   $msg.= check (  $faults, _("php.ini check -> implicit_flush"),
451       _("This option influences the Output handling. Turn this Option off, to increase performance."),
452       !$implicit_flush['local_value'],0,false);
454   /* max_execution_time */
455   if($max_execution_time['local_value'] < 30 ){
456     $max_execution_time['local_value']=false;
457   }
458   $msg.= check (  $faults, _("php.ini check -> max_execution_time"),
459       _("The Execution time should be at least 30 seconds, because some actions may consume more time."),
460       $max_execution_time['local_value'],0,false);
462   /* memory_limit */
463   if($memory_limit['local_value'] < 16 ){
464     $memory_limit['local_value']=false;
465   }
466   $msg.= check (  $faults, _("php.ini check -> memory_limit"),
467       _("GOsa needs at least 16MB of memory, less will cause unpredictable errors! Increase it for larger setups."),
468       !$implicit_flush['local_value'],0,false);
470   /* expose_php */
471   $msg.= check (  $faults, _("php.ini check -> expose_php"),
472       _("Increase the server security by setting expose_php to 'off'. PHP won't send any Information about the server you are running in this case."),
473       !$implicit_flush['local_value'],0,false);
475   /* magic_quotes_gpc */
476   $msg.= check (  $faults, _("php.ini check -> magic_quotes_gpc"),
477       _("Increase your server security by setting magic_quotes_gpc to 'on'. PHP will escape all quotes in strings in this case."),
478       $magic_quotes_gpc['local_value'],0,false);
480   return $msg;
484 function parse_contrib_conf()
487   $str                = "";
488   $used_samba_version = 0;
489   $query              = ""; 
490   $fp                 = false;
491   $output             = "";
492   $needridbase_sid    = false;
493   $pwdhash            = "";
494   $replacements       = array();
495   $ldapconf           = $_SESSION['ldapconf']; // The Installation information
496   $classes            = $_SESSION['classes'];  // Class information needed to define which features are enabled
497   $possible_plugins   = array();
499   /* Which samba version do we use? */
500   if(isset($classes['samba3'])){
501     $used_samba_version = 2;
502   } else {
503     $used_samba_version = 3;
504   }
506   /* Look for samba password generation method */
507   if(file_exists("/usr/bin/mkntpasswd")){
508     $pwdhash  = "/usr/bin/mkntpasswd";
509   } elseif (preg_match("/^Usage: mkntpwd /", shell_exec ("mkntpwd 2>&1"))){
510     $pwdhash= "mkntpwd";
511   } else {
512     $pwdhash=('perl -MCrypt::SmbHash -e "ntlmgen \"\$ARGV[0]\", \$lm, \$nt; print \"\${lm}:\${nt}\n\";" $1');
513   }
516   /* Define which variables will be replaced */
517   $replacements['{LOCATIONNAME}']  = $ldapconf['location'];
518   $replacements['{SAMBAVERSION}']  = $used_samba_version;
519   $replacements['{LDAPBASE}']      = $ldapconf['base'];
520   $replacements['{LDAPADMIN}']     = $ldapconf['admin'];
521   $replacements['{UIDBASE}']       = $ldapconf['uidbase'];
522   $replacements['{DNMODE}']        = $ldapconf['peopledn'];
523   $replacements['{LDAPHOST}']      = $ldapconf['uri'];
524   $replacements['{PASSWORD}']      = $ldapconf['password'];
525   $replacements['{CRYPT}']         = $ldapconf['arr_cryptkeys'][$ldapconf['arr_crypts']];
526   $replacements['{SID}']         = "";
527   $replacements['{RIDBASE}']     = "";
528   if($ldapconf['mail'] != "disabled"){
529     $replacements['{MAILMETHOD}']    = $ldapconf['mail_methods'][$ldapconf['mail']];
530   }   
531   $replacements['{SMBHASH}']       = $pwdhash;
532   $replacements['{GOVERNMENTMODE}']= "false"; 
533   $replacements['{kolabAccount}']  = "";
534   $replacements['{servKolab}']     = "";
535   $replacements['{errorlvl}']     = $ldapconf['errorlvl'];
537   /* This array contains all preg_replace syntax to delete all unused plugins
538      THE kEY MUST BE THE CLASSNAME so we can check it with $ldapconf['classes'] */
540   $possible_plugins['fonreport'][]   = "'\n.*<plugin.*fonreport+.*\n.*>.*\n'i";
541   $possible_plugins['phoneaccount'][]= "'\n.*<tab.*phoneAccount.*>.*\n'i";
542   $possible_plugins['logview'][]     = "'\n.*<plugin.*logview+.*\n.*>.*\n'i";
543   $possible_plugins['pureftp'][]     = "'\n.*<tab.*pureftp.*>.*\n'i";
544   $possible_plugins['webdav'][]      = "'\n.*<tab.*webdav.*>.*\n'i";
545   $possible_plugins['phpgroupware'][]= "'\n.*<tab.*phpgroupware.*>.*\n'i";
547   /*Header information
548      Needed to send the generated gosa.conf to the browser */
549   header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
550   header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
551   header("Cache-Control: no-cache");
552   header("Pragma: no-cache");
553   header("Cache-Control: post-check=0, pre-check=0");
554   header("Content-type: text/plain");
556   if (preg_match('/MSIE 5.5/', $_SERVER['HTTP_USER_AGENT']) ||
557       preg_match('/MSIE 6.0/', $_SERVER['HTTP_USER_AGENT'])){
558     header('Content-Disposition: filename="gosa.conf"');
559   } else {
560     header('Content-Disposition: attachment; filename="gosa.conf"');
561   }
563   if(!$fp=fopen(CONFIG_TEMPLATE_DIR."/gosa.conf","r")) {
564     echo "Can't open file ".CONFIG_TEMPLATE_DIR."/gosa.conf";
565   } else {
566     while(!feof($fp)) {
567       $str.= fread($fp,512);
568     }
570     if($ldapconf['mail_methods'][$ldapconf['mail']]=="kolab") {
571       $replacements['{kolabAccount}']  ="<tab class=\"kolabAccount\" />\n     ";
572       $replacements['{servKolab}']     ="<tab class=\"servkolab\" name=\"Kolab\" />";
573     }
575     if($used_samba_version == 2) {
576       /* Do nothing for samba 2... */
577     } else {
578       /* Create LDAP connection, to check if there's a domain
579          object defined in the LDAP schema */
580       $ldap= new LDAP($ldapconf['admin'], $ldapconf['password'], $ldapconf['uri']);
582       /* Try to find a Samba Domain Objekt */
583       $ldap->search("(objectClass=sambaDomain)");
584     
585       /* Something found ??? so we need to define ridbase an SID by ourselfs */
586       if($ldap->count()< 1) {
587         $replacements['{SID}']= "sid=\"123412-11\"";
588         $replacements['{RIDBASE}']= "ridbase=\"1000\"";  
589       } 
590     }
592     /* Data readed, types replaced, samba version detected and checked if
593        we need to add SID and RIDBASE. Check if there is an ivbbEntry in
594        the LDAP tree, in this case we will set the governmentmode to true.
595        Create LDAP connection, to check if theres a domain Objekt definen
596        in the LDAP schema. */
597     if(!isset($ldap)){
598       $ldap= new LDAP($ldapconf['admin'], $ldapconf['password'], $ldapconf['uri']);
599     }
601     /* Try to find a Samba Domain Objekt */
602     $ldap->search("(objectClass=ivbbEntry)");
604     /* Something found ??? so we need to define ridbase an SID by ourselfs */
605     if($ldap->count()> 0) {
606       $replacements['{GOVERNMENTMODE}']= "true";
607     }
609     /* Replace all colleted information with placeholder */
610     foreach($replacements as $key => $val) {
611       $str = preg_replace("/".$key."/",$val,$str);
612     }    
614     if($ldapconf['mail'] == "disabled"){
615       $str = str_replace("mailMethod=\"{MAILMETHOD}\"","",$str);
616     }
619     /* Remove all unused plugins */
620     foreach($possible_plugins as $key=> $plugin) {
621       foreach($plugin as $key=>$val) {
622         if(in_array($plugin,$classes)) {
623           $str = preg_replace($val,"\n",$str);
624         }
625       }
626     }
627   }
629   return ((($str)));
633 /* Show setup_page 1 */
634 function show_setup_page1($withoutput = true)
636   $faults = array();
637   $smarty = get_smarty();  
638   $smarty->assign ("content", get_template_path('setup_introduction.tpl'));
639   $smarty->assign ("tests", perform_php_checks($faults));
640   $smarty->assign ("detailed_tests", perform_additional_function_checks($faults));
642   /* This var is true if anything went wrong */
643   if ($faults){
644     $smarty->assign("mode", "disabled");
645   }
647   /* This line displays the template only if (withoutput is set) */
648   if($withoutput){
649     $smarty->display (get_template_path('headers.tpl'));
650   }
652   if (isset($_SESSION['errors'])){
653     $smarty->assign("errors", $_SESSION['errors']);
654   }
656   if($withoutput){
657     $smarty->display (get_template_path('setup.tpl'));
658   }
660   return (!$faults);
664 /* Show setup_page 2 */
665 function show_setup_page2($withoutput = true)
667   $faults = array();
668   $smarty = get_smarty();
669   $smarty->assign ("content", get_template_path('setup_step2.tpl'));
670   $smarty->assign ("tests", perform_additional_checks($faults));
672   if ($faults) {
673     $smarty->assign("mode", "disabled");
674   }
675   if($withoutput){
676     $smarty->display (get_template_path('headers.tpl'));
677   }
678   if (isset($_SESSION['errors']))  {
679     $smarty->assign("errors", $_SESSION['errors']);
680   }
681   if($withoutput){
682     $smarty->display (get_template_path('setup.tpl'));
683   }
685   return (!$faults);                               
689 function show_setup_page3($withoutput = true)
691   $ds = NULL;
692   $smarty = get_smarty();
694   /* Take the Post oder the Sessioin saved data */
695   if(isset($_POST['uri'])){
696     $uri = $_POST['uri'];
697   } elseif(isset($_SESSION['ldapconf']['uri'])){
698     $uri = $_SESSION['ldapconf']['uri'];
699   }
701   /* If Page called first time, field is empty */
702   if((!isset($uri))||(empty($uri))){
703     $uri = "ldap://localhost:389";
704   }
706   /* if isset $uri save it to session */
707   if(isset($uri)) {
708     $_SESSION['ldapconf']['uri'] = $uri;
709     $smarty->assign ("uri", validate($uri));
710   }
712   /* No error till now */
713   $fault = false;
715   /* If we pushed the Button continue */
716   if(isset($_POST['continue3'])){
717     if(!isset($uri)) {
718       $fault = true;
720       /* Output the Error */
721       if($withoutput) {
722         print_red (_("You've to specify an ldap server before continuing!"));
723         $smarty->assign ("content", get_template_path('setup_step3.tpl'));
724       }
725     }
726   } elseif (!$ds = @ldap_connect (validate($uri))) {
727     $fault =true;
729     /* Output the Error */
730     if($withoutput) {
731       print_red (_("Can't connect to the specified LDAP server! Please make sure that is reachable for GOsa."));
732       $smarty->assign ("uri", validate($uri));
733       $smarty->assign ("content", get_template_path('setup_step3.tpl'));
734     }
735   } else {
736     /* Try to bind the connection */    
737     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
739     /* if we can't bind , print error */
740     if (!$r  =  @ldap_bind ($ds)) {
741       $fault = true;
743       /* Output the Error */
744       if($withoutput) {
745         print_red (_("Can't bind to the specified LDAP server! Please make sure that it is reachable for GOsa."));
746         $smarty->assign ("content", get_template_path('setup_step3.tpl'));
747         $smarty->assign ("uri", validate($uri));
748       }
749     } else {
750       $fault = false;
751     }
752   }
754   $smarty->assign ("content", get_template_path('setup_step3.tpl'));
756   /* Load Header */
757   if($withoutput){
758     $smarty->display (get_template_path('headers.tpl'));
759   }
761   /* Set Errors to Smarty */
762   if (isset($_SESSION['errors'])) {
763     $smarty->assign("errors", $_SESSION['errors']);
764   }
766   /* Print out Template */ 
767   if($withoutput){
768     $smarty->display (get_template_path('setup.tpl'));
769   }
771   return (!$fault);                             
775 function show_setup_page4($withoutput = true)
777   $smarty= get_smarty();      
779         // ?
780   if(!isset($_SESSION['ldapconf']['base'])){
781     $_SESSION['ldapconf']['base']= $base;
782   }
784   if(!isset($_SESSION['ldapconf']['base'])){
785     $_SESSION['ldapconf']['base']= $base;
786   }
787   require_once("class_password-methods.inc");
789   $fault     = false;              
790   $uri       = $_SESSION['ldapconf']['uri'];
791   $ldapconf  = $_SESSION['ldapconf'];
792   $arr_crypts= array();
793   $temp      = "";
794   $checkvars = array("location", "admin", "password", "peopleou", "base",
795       "peopledn", "arr_crypts", "mail", "uidbase","errorlvl");
797   if(!isset($_SESSION['ldapconf']['arr_cryptkeys'])) {
798     require_once("class_password-methods.inc");
799     $tmp= passwordMethod::get_available_methods_if_not_loaded();
800     $_SESSION['ldapconf']['arr_cryptkeys']= $tmp['name'];
801   }
803   if(!isset($_SESSION['ldapconf']['mail_methods'])) {
804     $_SESSION['ldapconf']['mail_methods']=array();
805     $temp = get_available_mail_classes();
806     $_SESSION['ldapconf']['mail_methods']= $temp['name'];
807   }
809   /* If there are some empty vars in ldapconnect -
810      these values also represent out default values */
811   if(!$ds = @ldap_connect (validate($uri))){
812     $fault = true;
813     if($withoutput){
814       print_red (_("Can't connect to the specified LDAP server! Please make sure that is reachable for GOsa."));
815     }
816   } elseif(!@ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3)){
817     $fault = true;
818     if($withoutput){
819       print_red (_("Can't bind to the specified LDAP server! Please make sure that it is reachable for GOsa."));
820     }
821   } elseif(!$r= @ldap_bind ($ds)){
822     $fault = true;
823     if($withoutput){
824       print_red (_("Can't bind to the specified LDAP server! Please make sure that it is reachable for GOsa."));
825     }
826   } else {
827     $sr=   @ldap_search ($ds, NULL, "objectClass=*", array("namingContexts"));
828     $attr= @ldap_get_entries($ds,$sr);
830     if((empty($attr))) {
831       $base= "dc=example,dc=net";
833       if($withoutput){
834         print_red(_("Bind to server successful, but the server seems to be completly empty, please check all information twice"));
835       }
837     } else {
838       $base= $attr[0]['dn'];
839     }
840   }
842   if(!isset($_SESSION['ldapconf']['base'])){
843     $_SESSION['ldapconf']['base']= $base;
844   }
845   if(!isset($_SESSION['ldapconf']['admin'])){
846     $_SESSION['ldapconf']['admin']= "cn=ldapadmin,".$base;
847   }
848   if(!isset($_SESSION['ldapconf']['peopleou'])){
849     $_SESSION['ldapconf']['peopleou']= "ou=people";
850   }
851   if(!isset($_SESSION['ldapconf']['groupou'])){
852     $_SESSION['ldapconf']['groupou']= "ou=groups";
853   }
854   if(!isset($_SESSION['ldapconf']['peopledn'])){
855     $_SESSION['ldapconf']['peopledn']= "cn";
856   }
857   if(!isset($_SESSION['ldapconf']['password'])){
858     $_SESSION['ldapconf']['password']= "";
859   }
860   if(!isset($_SESSION['ldapconf']['location'])){
861     $_SESSION['ldapconf']['location']= "Example";
862   }
863   if(!isset($_SESSION['ldapconf']['uidbase'])){
864     $_SESSION['ldapconf']['uidbase']= "1000";
865   }
866   if(!isset($_SESSION['ldapconf']['mail'])){
867     $_SESSION['ldapconf']['mail']= 0;
868   }
869   $tmp= array_flip($_SESSION['ldapconf']['arr_cryptkeys']);
870   if(!isset($_SESSION['ldapconf']['arr_crypts'])){
871     $_SESSION['ldapconf']['arr_crypts']   = $tmp['md5'];
872   }
874   /* check POST data */
875   if(isset($_POST['check'])) {
877     /* Check if all needed vars are submitted */
878     foreach($checkvars as $key) {
879       if($key == "peopleou"){
880         continue;
881       }
882       if($key == "groupou"){
883         continue;
884       }
886       if((isset($_POST[$key]))&&($_POST[$key]!="")) {
887         $_SESSION['ldapconf'][$key] = $_POST[$key];
888       } else {
889         if($withoutput) {
890           print_red(sprintf(_("You're missing the required attribute '%s' from this formular. Please complete!"), $key));
891         }
892         $fault = true;
893       }
894     }
895   }
897   /* Transfer base */
898   if(isset($_POST['base'])){
899     $_SESSION['ldapconf']['base']= $_POST['base'];
900   }
902   $smarty->assign("arr_cryptkeys",$_SESSION['ldapconf']['arr_cryptkeys']);
903   $smarty->assign("mail_methods", $_SESSION['ldapconf']['mail_methods']);
905   foreach($_SESSION['ldapconf'] as $key => $val) {
906     $smarty->assign($key,$val);
907   }
909   if(isset($_POST['check'])) {
910     $ldap= new LDAP($_SESSION['ldapconf']['admin'],
911         $_SESSION['ldapconf']['password'],
912         $_SESSION['ldapconf']['uri']);
914     $m= schema_check($_SESSION['ldapconf']['uri'],
915         $_SESSION['ldapconf']['admin'],
916         $_SESSION['ldapconf']['password']);
917     $_SESSION['classes']= $m;
919     if(!is_schema_readable($ldapconf['uri'],$ldapconf['admin'],$ldapconf['password'])){
920       if($withoutput){
921         print_red(_("Can't read schema informations, GOsa needs to know your schema setup. Please verify that it is readable for GOsa"));
922       }
923       $fault=true;
924     }
928     if ($ldap->error != "Success") {
929       if($withoutput) {
930         print_red(sprintf(_("Can't log into LDAP server. Reason was: %s."), $ldap->get_error()));
931       }
932       $fault = true;
933     }
934   }
936   /* Set smarty output */
937   $smarty->assign ("content", get_template_path('setup_step4.tpl'));
938   $smarty->assign ("peopledns", array("cn", "uid"));
939   if($withoutput){
940     $smarty->display (get_template_path('headers.tpl'));
941   }
942   if(isset($_SESSION['errors'])) {
943     $smarty->assign("errors", $_SESSION['errors']);
944   }
945   if($withoutput){
946     $smarty->display (get_template_path('setup.tpl'));
947   }
948   return (!$fault);
952 function show_setup_page5($withoutput=true)
954   /* Get ldapconf */
955   $ldapconf= $_SESSION['ldapconf'];
957   /* get smarty */
958   $smarty = get_smarty();
960   if(isset($_SESSION['classes'])){
961     $classes = $_SESSION['classes'];
962   }
964   $info= posix_getgrgid(posix_getgid());
965   $smarty->assign("webgroup", $info['name']);
966   $smarty->assign("path", CONFIG_DIR);
967   $message= "<table summary=\"\" class=\"check\">";
968   $m= schema_check($ldapconf['uri'], $ldapconf['admin'], $ldapconf['password'],1);
970   if($withoutput) {
971     $smarty->assign ("schemas", view_schema_check($m));
972     $smarty->assign ("content", get_template_path('setup_finish.tpl'));
973   }
975   /* Output templates... */
976   if($withoutput){
977     $smarty->display (get_template_path('headers.tpl'));
978   }
979   if (isset($_SESSION['errors'])) {
980     $smarty->assign("errors", $_SESSION['errors']);
981   }
982   if($withoutput){
983     $smarty->display (get_template_path('setup.tpl'));
984   }
986   return(true);
990 function create_user_for_setup($withoutput=true)
992   global $samba;
994   $ldapconf = $_SESSION['ldapconf'];
995   $smarty = get_smarty();
996   
997   $need_to_create_group = false;
998   $need_to_create_user  = false;
1000   $str_there="";
1002   if(isset($_SESSION['classes'])){
1003     $classes= $_SESSION['classes'];
1004   }
1006   /* Everything runns perfect ...
1007      So we do a last test on this page
1008      is there a user with ACLs :all which will be able to adminsitrate GOsa
1009      We check that, if this user or group is missing we ask for creating them */
1010   $ldap= new LDAP($_SESSION['ldapconf']['admin'],    $_SESSION['ldapconf']['password'],   $_SESSION['ldapconf']['uri']);
1012   /* 
1013   Now we are testing for a group, with the rights :all 
1014   */
1015   
1016   $ldap->cd($ldapconf['base']);
1017   $ldap->search("(&(objectClass=gosaObject)(gosaSubtreeACL=:all))");
1019   $group_cnt  = $ldap->count();
1020   $data       = $ldap->fetch();
1022 //  $str_there  = "Searching for Aminitrative users <br><br>";
1024   /* 
1025   We need to create administrative user and group  because theres no group found 
1026   */
1027   if($group_cnt < 1) {
1028     
1029     /* 
1030     Set var to create user 
1031     */
1032 //    $str_there  =   "no group found<br>";
1034     $need_to_create_group = true;
1035     $need_to_create_user  = true;
1038     /* Output error */
1039     if(($withoutput)&&(!isset($_POST['new_admin']))){
1040       print_red(_("You're missing an administrative account for GOsa, you'll not be able to administrate anything!"));
1041     }
1042   } else {
1043     
1044 //    $str_there = "Group found <br>".$data['dn'];    
1046     $need_to_create_group = false;
1047  
1048     $ldap->clearResult();
1049    
1050     /* We found an Administrative Group, is there a user, too */
1051     if(isset($data['memberUid'][0])) {
1052       $str = "uid=".$data['memberUid']['0'];
1053       $ldap->search("(&(objectClass=gosaAccount)(objectClass=person)(".$str."))");
1054       $data2   = $ldap->fetch();
1055   
1056       /* We must create a user */
1057       if (($ldap->count() < 1)||(!isset($data2))) {
1058 //        $str_there.="Missing user";
1059         
1060         $need_to_create_user = true;
1061       
1062         if(($withoutput)&&(!isset($_POST['new_admin']))){
1063           print_red(_("You're missing an administrative account for GOsa, you'll not be able to administrate anything!"));
1064         }
1065       }else {
1066 //        $str_there.="<br>User found <br>".$data2['dn'];
1067         $need_to_create_user = false;
1068       }
1069     } else {
1070       $need_to_create_user=true;
1071       if(($withoutput)&&(!isset($_POST['new_admin']))){
1072           print_red(_("You're missing an administrative account for GOsa, you'll not be able to administrate anything!"));
1073         }   
1074 //      $str_there.="<br>No User found <br>";
1075     }
1076   }
1078   if(!($need_to_create_user&&$need_to_create_group))
1079     return(true);
1081   /* We need to create a new user with group */
1082   if(isset($_POST['new_admin']))
1083   {
1084   
1085     /* Adjust password attributes according to the samba version */
1086     if (isset($classes['samba3'])) {
1087       $samba= "2";
1088       $lmPassword = "lmPassword";
1089       $ntPassword = "ntPassword";
1090     } else {
1091       $samba= "3";
1092       $lmPassword = "sambaLMPassword";
1093       $ntPassword = "sambaNtPassword";
1094     }
1096     /* Nothing submitted */
1097     if(((empty($_POST['admin_name']))||(empty($_POST['admin_pass'])))) {
1098       return(true);
1099     }
1101     if($need_to_create_user) {
1102       /* We have the order to create an Admin */
1103       /* Define the user we are going to create */
1104       $dn= "cn=".$_POST['admin_name'].",".$ldapconf['peopleou'].",".$ldapconf['base'];
1105       $arr['objectClass'][0] ="person";
1106       $arr['objectClass'][1] ="organizationalPerson";
1107       $arr['objectClass'][2] ="inetOrgPerson";
1108       $arr['objectClass'][3] ="gosaAccount";
1109       $arr['uid']            = $_POST['admin_name'];
1110       $arr['cn']             = $_POST['admin_name'];
1111       $arr['sn']             = $_POST['admin_name'];
1112       $arr['givenName']      = "GOsa main administrator";
1113       $arr[$lmPassword]      = "10974C6EFC0AEE1917306D272A9441BB";
1114       $arr[$ntPassword]      = "38F3951141D0F71A039CFA9D1EC06378";
1115       $arr['userPassword']   = crypt_single($_POST['admin_pass'],"md5");
1116     
1118       if(!$ldap->dn_exists($dn)){ 
1119         $ldap->cd($dn); 
1120         $ldap->create_missing_trees($dn);
1121         $ldap->cd($dn);
1122         $ldap->add($arr);
1123         if($ldap->error!="Success"){
1124           print_red($ldap->error);
1125           print_red("Can't create user, and / or Group, possibly this problem  depends on an empty LDAP server. Check your configuration and try again!");
1126         }
1127       }    
1128     }
1130     /* There's already a group for administrator, so we only need to add the user */
1131     if(!$need_to_create_group) {
1132       if(!isset($data['memberUid'])) {
1133         $arrr['memberUid']= $_POST['admin_name'];
1134       } else {
1135         $data['memberUid'][$data['memberUid']['count']]=$_POST['admin_name'];
1136         $arrr['memberUid'] = $data['memberUid'];
1137         unset($arrr['memberUid']['count']);
1138   
1139         $tmp = array_reverse($arrr['memberUid']);    
1140         foreach($tmp as $tt){
1141           $tmp2[]=$tt;
1142         }
1143         $arrr['memberUid']= $tmp2;
1144 //        $str_there="Group found<br>".$data['dn'];
1145       }
1147       $ldap->cd($data['dn']);
1148       $ldap->modify($arrr);
1150     } else {
1151       $dn                    = "cn=administrators,".$ldapconf['groupou'].",".$ldapconf['base'];
1152       $arrr['objectClass'][0]= "gosaObject";
1153       $arrr['objectClass'][1]= "posixGroup";
1154       $arrr['gosaSubtreeACL']= ":all";
1155       $arrr['cn']            = "administrators";
1156       $arrr['gidNumber']     = "999";
1157       $arrr['memberUid']     = $_POST['admin_name'];
1159       $ldap->cd($dn);
1160       $ldap->create_missing_trees($dn);
1161       $ldap->cd($dn);
1163       $ldap->add($arrr);
1164     }
1165     return(true);
1166   } else {
1168     if((!isset($create_user))||(!($create_user))) {
1169       $smarty->assign ("content", get_template_path('setup_useradmin.tpl'));
1170       $smarty->assign("exists",true);
1171     } else {
1172       $smarty->assign ("content", get_template_path('setup_useradmin.tpl'));
1173       $smarty->assign("exists",false);
1174     }
1176   }
1178   /* Smarty output */ 
1179   if($withoutput){
1180     $smarty->display (get_template_path('headers.tpl'));
1181   }
1182   if (isset($_SESSION['errors'])) {
1183     $smarty->assign("errors", $_SESSION['errors']);
1184   }
1185   $smarty->assign("str_there",$str_there);
1186   if($withoutput){
1187     $smarty->display (get_template_path('setup.tpl'));
1188   }
1189   return(false);
1193 /* Returns the classnames auf the mail classes */
1194 function get_available_mail_classes()
1196   $dir = opendir( "../include");
1197   $methods = array();
1198   $suffix = "class_mail-methods-";
1199   $lensuf = strlen($suffix);
1200   $prefix = ".inc";
1201   $lenpre = strlen($prefix);
1203   $i = 0;
1204   while (($file = readdir($dir)) !== false){
1206     if(stristr($file,$suffix)) {
1207       $lenfile = strlen($file);
1208       $methods['name'][$i] = substr($file,$lensuf,($lenfile-$lensuf)-$lenpre);
1209       $methods['file'][$i] = $file;
1210       $methods[$i]['file'] = $file;
1211       $methods[$i]['name'] = substr($file,$lensuf,($lenfile-$lensuf)-$lenpre);
1212       $i++;
1213     }
1215   }
1217   return($methods);
1220 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1221 ?>