1 #!/usr/bin/php5
2 <?php
4 define ("GOSA_HOME", dirname(__FILE__));
5 define ("LOCALE_DIR", GOSA_HOME."/locale");
6 define ("PLUGSTATE_DIR", GOSA_HOME."/state");
8 function print_usage()
9 {
10 ?>
11 update-gosa - class cache updated and plugin manager for GOsa
12 Usage: update-gosa install dsc Install the plugin using the dsc information
13 placed in the plugin source directory.
15 update-gosa remove plugin Remove the plugin named "plugin" from
16 the current configuration.
18 update-gosa lists Lists installed plugins
20 update-gosa rescan-i18n Rebuilds the translations
22 update-gosa rescan-classes Rebuilds the class list
24 update-gosa Shortcut for rescan-classes and rescan-i18n
25 <?php
26 exit (1);
27 }
30 function rmdirRecursive($path, $followLinks=false) {
31 $dir= opendir($path);
32 while($entry= readdir($dir)) {
33 if(is_file($path."/".$entry) || ((!$followLinks) && is_link($path."/".$entry))) {
34 unlink($path."/".$entry);
35 } elseif (is_dir($path."/".$entry) && $entry!='.' && $entry!='..') {
36 rmdirRecursive($path."/".$entry);
37 }
38 }
39 closedir($dir);
40 return rmdir($path);
41 }
44 /* Function to include all class_ files starting at a given directory base */
45 function get_classes($folder= ".")
46 {
47 static $base_dir= "";
48 static $result= array();
50 if ($base_dir == ""){
51 if ($folder == "."){
52 $base_dir= getcwd();
53 } else {
54 $base_dir= $folder;
55 }
56 }
58 $currdir=getcwd();
59 if ($folder){
60 chdir("$folder");
61 }
63 $dh = opendir(".");
64 while(false !== ($file = readdir($dh))){
66 if (preg_match("/.*\.svn.*/", $file) ||
67 preg_match("/.*smarty.*/i",$file) ||
68 preg_match("/.*\.tpl.*/",$file) ||
69 ($file==".") ||($file =="..")){
70 continue;
71 }
73 /* Recurse through all "common" directories */
74 if (is_dir($file)){
75 get_classes($file);
76 continue;
77 }
79 /* Only take care about .inc and .php files... */
80 if (!(preg_match('/\.php$/', $file) || preg_match('/\.inc$/', $file))){
81 continue;
82 }
84 /* Include existing class_ files */
85 $contents= file($file);
86 foreach($contents as $line){
87 $line= chop($line);
88 if (preg_match('/^\s*class\s*\w.*$/', $line)){
89 $class= preg_replace('/^\s*class\s*(\w+).*$/', '\1', $line);
90 $result[$class]= preg_replace("%$base_dir/%", "", "$currdir/$folder/$file");
91 }
92 }
93 }
95 closedir($dh);
96 chdir($currdir);
98 return ($result);
99 }
102 function rescan_classes()
103 {
104 echo "Updating class cache...\n";
105 $class_mapping= get_classes();
106 $filename= GOSA_HOME."/include/class_location.inc";
108 /* Sanity checks */
109 if (!file_exists($filename) || is_writable($filename)) {
111 if (!$handle= fopen($filename, 'w')) {
112 echo "Cannot open file \"$filename\" - aborted\n";
113 exit (1);
114 }
116 } else {
117 echo "File \"$filename\" is not writable - aborted\n";
118 exit (2);
119 }
121 fwrite ($handle, "<?php\n\$class_mapping= array(\n");
122 foreach ($class_mapping as $key => $value){
123 fwrite ($handle, " \"$key\" => \"$value\",\n");
124 }
125 fwrite ($handle, " );\n");
127 fclose($handle);
128 }
131 function rescan_i18n()
132 {
133 echo "Updating internationalization...\n";
134 $languages= array();
135 $size= strlen(LOCALE_DIR);
137 /* Get all available messages.po files, sort them for languages */
138 $dir= new RecursiveDirectoryIterator(LOCALE_DIR);
139 $all= new RecursiveIteratorIterator($dir);
140 foreach ( $all as $element ){
141 if ($element->isFile() && preg_match('/\/LC_MESSAGES\/messages.po$/', $element->getPathname())){
142 $lang= preg_replace('/^.*\/([^\/]+)\/LC_MESSAGES\/.*$/', '\1', $element);
143 if (!isset($languages[$lang])){
144 $languages[$lang]= array();
145 }
146 $languages[$lang][]= substr($element->getPathName(), $size+1);
147 }
148 }
150 /* For each language, merge the target .mo to the compiled directory. */
151 foreach ($languages as $language => $po_files){
152 if (!is_dir(LOCALE_DIR."/compiled/${language}/LC_MESSAGES")){
153 if (!mkdir (LOCALE_DIR."/compiled/${language}/LC_MESSAGES", 0755, TRUE)){
154 echo "Failed to create '".LOCALE_DIR."/compiled/${language}/LC_MESSAGES'- aborted";
155 exit (3);
156 }
157 }
159 /* Cat all these po files into one single file */
160 system ("(cd ".LOCALE_DIR." && msgcat ".implode(" ", $po_files)." > compiled/${language}/LC_MESSAGES/messages.po)", $val);
161 if ($val != 0){
162 echo "Merging of message files failed - aborted";
163 exit (4);
164 }
165 system ("(cd ".LOCALE_DIR."/compiled/${language}/LC_MESSAGES && msgfmt -o messages.mo messages.po && rm messages.po)", $val);
166 if ($val != 0){
167 echo "Compiling of message files failed - aborted";
168 exit (5);
169 }
170 }
171 }
174 function parse_ini($file)
175 {
176 global $description, $provides, $depends, $versions, $conflicts;
178 $res= "";
179 if (file_exists($file)){
180 $tmp= parse_ini_file($file, TRUE);
182 if (isset($tmp['gosa-plugin'])){
183 $plugin= &$tmp['gosa-plugin'];
184 if (isset($plugin['name'])&& isset($plugin['description'])){
185 $res= $plugin['name'];
186 $provides[$res]= $plugin[$res];
187 $description[$res]= $plugin['description'];
188 $versions[$res]= $plugin['version'];
189 if (isset($plugin['depends'])){
190 $depends[$res]= explode(',', preg_replace('/\s+/', '', $plugin['depends']));
191 }
192 if (isset($plugin['conflicts'])){
193 $conflicts[$res]= explode(',', preg_replace('/\s+/', '', $plugin['conflicts']));
194 }
195 }
196 }
197 }
199 return $res;
200 }
203 function dependency_check()
204 {
205 global $description, $provides, $depends;
207 foreach ($depends as $name => $pl_depends){
208 foreach ($pl_depends as $pl){
209 if (!in_array($pl, $provides)){
210 echo "! Error: plugin '$name' depends on '$pl' which is not provided by any plugin\n\n";
211 exit (1);
212 }
213 }
214 }
215 }
218 function load_plugins()
219 {
220 if (!is_dir(PLUGSTATE_DIR)){
221 if (!mkdir (PLUGSTATE_DIR, 0755, TRUE)){
222 echo "Cannot create plugstate dir '".PLUGSTATE_DIR."' - aborted\n";
223 exit (2);
224 }
225 }
226 $dir= new DirectoryIterator(PLUGSTATE_DIR);
227 foreach ($dir as $entry){
228 if ($dir->isDir() && !preg_match('/^\./', $dir->__toString())){
229 $file= $dir->getPathName()."/plugin.dsc";
230 if (parse_ini($file) == ""){
231 echo "! Warning: plugin ".$dir->getPathName()." is missing declarations\n";
232 }
233 }
234 }
235 }
238 function list_plugins()
239 {
240 global $description, $versions;
241 $count= 0;
243 /* Load plugin list */
244 load_plugins();
246 /* Show plugins */
247 foreach ($description as $name => $dsc){
248 if ($count == 0){
249 echo "Plugin\t\t|Version |Description\n";
250 echo "----------------------------------------------------------------------------\n";
251 }
252 $ver= $versions[$name];
253 echo "$name\t\t|$ver\t |$dsc\n";
254 $count++;
255 }
257 /* Yell about non existing plugins... */
258 if ($count == 0){
259 echo "No plugins found...\n\n";
260 } else {
261 # Check for dependencies
262 dependency_check();
263 echo "\n";
264 }
265 }
268 function install_plugin($file)
269 {
270 global $description, $provides, $depends, $conflicts;
272 /* Load plugin list */
273 load_plugins();
275 /* Load .dsc file */
276 if (file_exists($file)){
277 $tmp= parse_ini_file($file, TRUE);
279 if (isset($tmp['gosa-plugin'])){
280 $plugin= &$tmp['gosa-plugin'];
281 if (isset($plugin['name'])&& isset($plugin['description'])){
282 $name= $plugin['name'];
283 $description= $plugin['description'];
284 $depends= array();
285 if (isset($plugin['depends'])){
286 $depends= explode(',', preg_replace('/\s+/', '', $plugin['depends']));
287 }
289 /* Already installed? */
290 if (isset($provides[$name])){
291 echo "! Error: plugin already installed\n\n";
292 exit (3);
293 }
295 /* Check if dependencies are fullfilled */
296 foreach ($depends as $dep){
297 $found= false;
298 foreach ($provides as $provide => $dummy){
299 if ($dep == $provide){
300 $found= true;
301 break;
302 }
303 }
304 if (!$found){
305 echo "! Error: plugin depends on '$dep', but this is not installed\n\n";
306 exit (3);
307 }
308 }
310 /* Check for conflicts */
311 foreach ($conflicts as $conf){
312 if (!in_array($conf, $provides)){
313 echo "! Warning: plugin conflicts with '$conf'\n\n";
314 }
315 }
317 /* Create plugstate directory and touch plugin.lst */
318 if (!mkdir (PLUGSTATE_DIR."/$name", 0755, TRUE)){
319 echo "Failed to create '".PLUGSTATE_DIR."/$name - aborted";
320 exit (3);
321 }
322 if (!$handle= fopen(PLUGSTATE_DIR."/$name/plugin.lst", 'w')) {
323 echo "Cannot open file '$filename' - aborted\n";
324 exit (1);
325 }
327 echo "Installing plugin '$name'...\n";
329 /* Copy and fill plugin.lst */
330 $path= dirname($file);
331 $dir= new RecursiveDirectoryIterator($path);
332 $all= new RecursiveIteratorIterator($dir);
333 foreach ( $all as $entry ){
334 $source= $path."/".substr($entry->getPathName(), strlen($path) + 1);
336 /* Skip description - it belongs to the state dir */
337 if (preg_match('/\/plugin.dsc$/', $source)){
338 copy ($source, PLUGSTATE_DIR."/$name/plugin.dsc");
339 continue;
340 }
342 /* Skip well known directories */
343 if (preg_match('/^\.+$/', $source) || preg_match('/\/\.svn\//', $source)) {
344 continue;
345 }
347 /* Calculate destination */
348 if (preg_match("%^.*locale/%", $source)){
349 $dest= GOSA_HOME."/locale/plugin/$name/".preg_replace("%^.*locale/%", "", $source);
350 } else {
351 $dest= GOSA_HOME."/plugins/".substr($entry->getPathName(), strlen($path) + 1);
352 }
354 /* Destination exists in case of directories? */
355 if ($entry->isDir()){
356 if (!is_dir($dest)){
357 mkdir($dest, 0755, TRUE);
358 fwrite ($handle, "$dest\n");
359 }
360 } else {
361 if (!is_dir(dirname($dest))){
362 mkdir(dirname($dest), 0755, TRUE);
363 fwrite ($handle, dirname($dest)."\n");
364 }
365 }
367 /* Copy files */
368 if ($entry->isFile()){
369 copy ($source, $dest);
370 }
372 /* Note what we did... */
373 fwrite ($handle, "$dest\n");
374 }
376 fclose($handle);
377 }
378 }
379 }
381 /* Update caches */
382 rescan_classes();
383 rescan_i18n();
384 }
387 function remove_plugin($name)
388 {
389 global $description, $depends;
391 /* Load plugin list */
392 load_plugins();
394 /* Present? */
395 if (!isset($description[$name])){
396 echo "! Error: cannot find a plugin named '$name'\n\n";
397 exit (1);
398 }
400 /* Depends? */
401 foreach ($depends as $sname => $pl_depends){
402 if (in_array($name, $pl_depends)){
403 echo "! Error: plugin '$sname' depends on '$name' - cannot remove it\n\n";
404 exit (1);
405 }
406 }
408 /* Load information */
409 if (!file_exists(PLUGSTATE_DIR."/$name/plugin.lst")){
410 echo "! Error: cannot remove plugin '$name' - no install history found\n\n";
411 exit (1);
412 }
414 echo "Removing plugin '$name'...\n";
415 $contents= file(PLUGSTATE_DIR."/$name/plugin.lst");
416 $cnv= array();
417 foreach($contents as $line){
418 $entry= chop($line);
419 $cnv[strlen($entry).":$entry"]= $entry;
420 }
421 krsort($cnv);
423 /* Remove files first */
424 clearstatcache();
425 foreach ($cnv as $entry){
426 if (is_dir($entry)){
427 rmdir($entry);
428 continue;
429 }
430 if (file_exists($entry)){
431 unlink($entry);
432 }
433 }
435 /* Remove state directory for plugin */
436 rmdirRecursive(PLUGSTATE_DIR."/$name");
438 /* Update caches */
439 rescan_classes();
440 rescan_i18n();
441 }
445 /* Fill global values */
446 $description= $provides= $depends= $versions= $conflicts= array();
448 /* Action specified? */
449 if ($argc < 2){
450 rescan_classes();
451 rescan_i18n();
452 exit (0);
453 }
455 switch ($argv[1]){
456 case 'install':
457 if (isset($argv[2])){
458 install_plugin($argv[2]);
459 } else {
460 echo "Usage: update-gosa install dsc-file\n\n";
461 exit (1);
462 }
463 break;
464 case 'list':
465 list_plugins();
466 break;
467 case 'remove':
468 if (isset($argv[2])){
469 remove_plugin($argv[2]);
470 } else {
471 echo "Usage: update-gosa remove plugin-name\n\n";
472 exit (1);
473 }
474 break;
475 case 'rescan-i18n':
476 rescan_i18n();
477 break;
478 case 'rescan-classes':
479 rescan_classes();
480 break;
481 default:
482 echo "Error: Supplied command not known\n\n";
483 print_usage();
484 break;
485 }
488 ?>