]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - ModuleInstall/ModuleScanner.php
Release 6.5.16
[Github/sugarcrm.git] / ModuleInstall / ModuleScanner.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM Community Edition is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 class ModuleScanner{
39         private $manifestMap = array(
40                         'pre_execute'=>'pre_execute',
41                         'install_mkdirs'=>'mkdir',
42                         'install_copy'=>'copy',
43                         'install_images'=>'image_dir',
44                         'install_menus'=>'menu',
45                         'install_userpage'=>'user_page',
46                         'install_dashlets'=>'dashlets',
47                         'install_administration'=>'administration',
48                         'install_connectors'=>'connectors',
49                         'install_vardefs'=>'vardefs',
50                         'install_layoutdefs'=>'layoutdefs',
51                         'install_layoutfields'=>'layoutfields',
52                         'install_relationships'=>'relationships',
53                         'install_languages'=>'language',
54             'install_logichooks'=>'logic_hooks',
55                         'post_execute'=>'post_execute',
56
57         );
58
59         /**
60          * config settings
61          * @var array
62          */
63         private $config = array();
64         private $config_hash;
65
66         private $blackListExempt = array();
67         private $classBlackListExempt = array();
68
69     // Bug 56717 - adding hbs extension to the whitelist - rgonzalez
70         private $validExt = array('png', 'gif', 'jpg', 'css', 'js', 'php', 'txt', 'html', 'htm', 'tpl', 'pdf', 'md5', 'xml', 'hbs');
71         private $classBlackList = array(
72         // Class names specified here must be in lowercase as the implementation
73         // of the tokenizer converts all tokens to lowercase.
74         'reflection',
75         'reflectionclass',
76         'reflectionzendextension',
77         'reflectionextension',
78         'reflectionfunction',
79         'reflectionfunctionabstract',
80         'reflectionmethod',
81         'reflectionobject',
82         'reflectionparameter',
83         'reflectionproperty',
84         'reflector',
85         'reflectionexception',
86         'lua',
87             'ziparchive',
88             'splfileinfo',
89             'splfileobject',
90             'pclzip',
91
92     );
93         private $blackList = array(
94     'popen',
95     'proc_open',
96     'escapeshellarg',
97     'escapeshellcmd',
98     'proc_close',
99     'proc_get_status',
100     'proc_nice',
101         'passthru',
102     'clearstatcache',
103     'disk_free_space',
104     'disk_total_space',
105     'diskfreespace',
106         'dir',
107     'fclose',
108     'feof',
109     'fflush',
110     'fgetc',
111     'fgetcsv',
112     'fgets',
113     'fgetss',
114     'file_exists',
115     'file_get_contents',
116     'filesize',
117     'filetype',
118     'flock',
119     'fnmatch',
120     'fpassthru',
121     'fputcsv',
122     'fputs',
123     'fread',
124     'fscanf',
125     'fseek',
126     'fstat',
127     'ftell',
128     'ftruncate',
129     'fwrite',
130     'glob',
131     'is_dir',
132     'is_file',
133     'is_link',
134     'is_readable',
135     'is_uploaded_file',
136         'opendir',
137     'parse_ini_string',
138     'pathinfo',
139     'pclose',
140     'readfile',
141     'readlink',
142     'realpath_cache_get',
143     'realpath_cache_size',
144     'realpath',
145     'rewind',
146         'readdir',
147     'set_file_buffer',
148     'tmpfile',
149     'umask',
150     'ini_set',
151     'set_time_limit',
152         'eval',
153         'exec',
154         'system',
155         'shell_exec',
156         'passthru',
157         'chgrp',
158         'chmod',
159         'chwown',
160         'file_put_contents',
161         'file',
162         'fileatime',
163         'filectime',
164         'filegroup',
165         'fileinode',
166         'filemtime',
167         'fileowner',
168         'fileperms',
169         'fopen',
170         'is_executable',
171         'is_writable',
172         'is_writeable',
173         'lchgrp',
174         'lchown',
175         'linkinfo',
176         'lstat',
177         'mkdir',
178     'mkdir_recursive',
179         'parse_ini_file',
180         'rmdir',
181     'rmdir_recursive',
182         'stat',
183         'tempnam',
184         'touch',
185         'unlink',
186         'getimagesize',
187         'call_user_func',
188         'call_user_func_array',
189         'create_function',
190
191
192         //mutliple files per function call
193         'copy',
194     'copy_recursive',
195         'link',
196         'rename',
197         'symlink',
198         'move_uploaded_file',
199         'chdir',
200         'chroot',
201         'create_cache_directory',
202         'mk_temp_dir',
203         'write_array_to_file',
204         'write_encoded_file',
205         'create_custom_directory',
206         'sugar_rename',
207         'sugar_chown',
208         'sugar_fopen',
209         'sugar_mkdir',
210         'sugar_file_put_contents',
211         'sugar_chgrp',
212         'sugar_chmod',
213         'sugar_touch',
214
215         // Functions that have callbacks can circumvent our security measures.
216         // List retrieved through PHP's XML documentation, and running the
217         // following script in the reference directory:
218
219         // grep -R callable . | grep -v \.svn | grep methodparam | cut -d: -f1 | sort -u | cut -d"." -f2 | sed 's/\-/\_/g' | cut -d"/" -f4
220
221         // AMQPQueue
222         'consume',
223
224         // PHP internal - arrays
225         'array_diff_uassoc',
226         'array_diff_ukey',
227         'array_filter',
228         'array_intersect_uassoc',
229         'array_intersect_ukey',
230         'array_map',
231         'array_reduce',
232         'array_udiff_assoc',
233         'array_udiff_uassoc',
234         'array_udiff',
235         'array_uintersect_assoc',
236         'array_uintersect_uassoc',
237         'array_uintersect',
238         'array_walk_recursive',
239         'array_walk',
240         'uasort',
241         'uksort',
242         'usort',
243
244         // EIO functions that accept callbacks.
245         'eio_busy',
246         'eio_chmod',
247         'eio_chown',
248         'eio_close',
249         'eio_custom',
250         'eio_dup2',
251         'eio_fallocate',
252         'eio_fchmod',
253         'eio_fchown',
254         'eio_fdatasync',
255         'eio_fstat',
256         'eio_fstatvfs',
257         'eio_fsync',
258         'eio_ftruncate',
259         'eio_futime',
260         'eio_grp',
261         'eio_link',
262         'eio_lstat',
263         'eio_mkdir',
264         'eio_mknod',
265         'eio_nop',
266         'eio_open',
267         'eio_read',
268         'eio_readahead',
269         'eio_readdir',
270         'eio_readlink',
271         'eio_realpath',
272         'eio_rename',
273         'eio_rmdir',
274         'eio_sendfile',
275         'eio_stat',
276         'eio_statvfs',
277         'eio_symlink',
278         'eio_sync_file_range',
279         'eio_sync',
280         'eio_syncfs',
281         'eio_truncate',
282         'eio_unlink',
283         'eio_utime',
284         'eio_write',
285
286         // PHP internal - error functions
287         'set_error_handler',
288         'set_exception_handler',
289
290         // Forms Data Format functions
291         'fdf_enum_values',
292
293         // PHP internal - function handling
294         'call_user_func_array',
295         'call_user_func',
296         'forward_static_call_array',
297         'forward_static_call',
298         'register_shutdown_function',
299         'register_tick_function',
300
301         // Gearman
302         'setclientcallback',
303         'setcompletecallback',
304         'setdatacallback',
305         'setexceptioncallback',
306         'setfailcallback',
307         'setstatuscallback',
308         'setwarningcallback',
309         'setworkloadcallback',
310         'addfunction',
311
312         // Firebird/InterBase
313         'ibase_set_event_handler',
314
315         // LDAP
316         'ldap_set_rebind_proc',
317
318         // LibXML
319         'libxml_set_external_entity_loader',
320
321         // Mailparse functions
322         'mailparse_msg_extract_part_file',
323         'mailparse_msg_extract_part',
324         'mailparse_msg_extract_whole_part_file',
325
326         // Memcache(d) functions
327         'addserver',
328         'setserverparams',
329         'get',
330         'getbykey',
331         'getdelayed',
332         'getdelayedbykey',
333
334         // MySQLi
335         'set_local_infile_handler',
336
337         // PHP internal - network functions
338         'header_register_callback',
339
340         // Newt
341         'newt_entry_set_filter',
342         'newt_set_suspend_callback',
343
344         // OAuth
345         'consumerhandler',
346         'timestampnoncehandler',
347         'tokenhandler',
348
349         // PHP internal - output control
350         'ob_start',
351
352         // PHP internal - PCNTL
353         'pcntl_signal',
354
355         // PHP internal - PCRE
356         'preg_replace_callback',
357
358         // SQLite
359         'sqlitecreateaggregate',
360         'sqlitecreatefunction',
361         'sqlite_create_aggregate',
362         'sqlite_create_function',
363
364         // RarArchive
365         'open',
366
367         // Readline
368         'readline_callback_handler_install',
369         'readline_completion_function',
370
371         // PHP internal - session handling
372         'session_set_save_handler',
373
374         // PHP internal - SPL
375         'construct',
376         'iterator_apply',
377         'spl_autoload_register',
378
379         // Sybase
380         'sybase_set_message_handler',
381
382         // PHP internal - variable handling
383         'is_callable',
384
385         // XML Parser
386         'xml_set_character_data_handler',
387         'xml_set_default_handler',
388         'xml_set_element_handler',
389         'xml_set_end_namespace_decl_handler',
390         'xml_set_external_entity_ref_handler',
391         'xml_set_notation_decl_handler',
392         'xml_set_processing_instruction_handler',
393         'xml_set_start_namespace_decl_handler',
394         'xml_set_unparsed_entity_decl_handler',
395
396             // unzip
397             'unzip',
398             'unzip_file',
399 );
400     private $methodsBlackList = array('setlevel', 'put' => array('sugarautoloader'), 'unlink' => array('sugarautoloader'));
401
402         public function printToWiki(){
403                 echo "'''Default Extensions'''<br>";
404                 foreach($this->validExt as $b){
405                         echo '#' . $b . '<br>';
406
407                 }
408                 echo "'''Default Black Listed Functions'''<br>";
409                 foreach($this->blackList as $b){
410                         echo '#' . $b . '<br>';
411
412                 }
413
414         }
415
416     public function __construct()
417     {
418         $params = array(
419             'blackListExempt'      => 'MODULE_INSTALLER_PACKAGE_SCAN_BLACK_LIST_EXEMPT',
420             'blackList'            => 'MODULE_INSTALLER_PACKAGE_SCAN_BLACK_LIST',
421             'classBlackListExempt' => 'MODULE_INSTALLER_PACKAGE_SCAN_CLASS_BLACK_LIST_EXEMPT',
422             'classBlackList'       => 'MODULE_INSTALLER_PACKAGE_SCAN_CLASS_BLACK_LIST',
423             'validExt'             => 'MODULE_INSTALLER_PACKAGE_SCAN_VALID_EXT',
424             'methodsBlackList'     => 'MODULE_INSTALLER_PACKAGE_SCAN_METHOD_LIST',
425         );
426
427         $disableConfigOverride = defined('MODULE_INSTALLER_DISABLE_CONFIG_OVERRIDE')
428             && MODULE_INSTALLER_DISABLE_CONFIG_OVERRIDE;
429
430         $disableDefineOverride = defined('MODULE_INSTALLER_DISABLE_DEFINE_OVERRIDE')
431             && MODULE_INSTALLER_DISABLE_DEFINE_OVERRIDE;
432
433         if (!$disableConfigOverride && !empty($GLOBALS['sugar_config']['moduleInstaller'])) {
434             $this->config = $GLOBALS['sugar_config']['moduleInstaller'];
435         }
436
437         foreach ($params as $param => $constName) {
438
439             if (!$disableConfigOverride && isset($this->config[$param]) && is_array($this->config[$param])) {
440                 $this->{$param} = array_merge($this->{$param}, $this->config[$param]);
441             }
442
443             if (!$disableDefineOverride && defined($constName)) {
444                 $value = constant($constName);
445                 $value = explode(',', $value);
446                 $value = array_map('trim', $value);
447                 $value = array_filter($value, 'strlen');
448                 $this->{$param} = array_merge($this->{$param}, $value);
449             }
450         }
451         }
452
453         private $issues = array();
454         private $pathToModule = '';
455
456         /**
457          *returns a list of issues
458          */
459         public function getIssues(){
460                 return $this->issues;
461         }
462
463         /**
464          *returns true or false if any issues were found
465          */
466         public function hasIssues(){
467                 return !empty($this->issues);
468         }
469
470         /**
471          *Ensures that a file has a valid extension
472          */
473         public function isValidExtension($file)
474         {
475                 $file = strtolower($file);
476                 $pi = pathinfo($file);
477
478                 //make sure they don't override the files.md5
479                 if(empty($pi['extension']) || $pi['basename'] == 'files.md5') {
480                     return false;
481                 }
482                 return in_array($pi['extension'], $this->validExt);
483
484         }
485
486         public function isConfigFile($file)
487         {
488             $real = realpath($file);
489             if($real == realpath("config.php")) {
490                 return true;
491             }
492             if(file_exists("config_override.php") && $real == realpath("config_override.php")) {
493                 return true;
494             }
495             return false;
496         }
497
498         /**
499          *Scans a directory and calls on scan file for each file
500          **/
501         public function scanDir($path){
502                 static $startPath = '';
503                 if(empty($startPath))$startPath = $path;
504                 if(!is_dir($path))return false;
505                 $d = dir($path);
506                 while($e = $d->read()){
507                 $next = $path . '/' . $e;
508                 if(is_dir($next)){
509                         if(substr($e, 0, 1) == '.')continue;
510                         $this->scanDir($next);
511                 }else{
512                         $issues = $this->scanFile($next);
513
514
515                 }
516                 }
517         return true;
518         }
519
520         /**
521          * Check if the file contents looks like PHP
522          * @param string $contents File contents
523          * @return boolean
524          */
525         public function isPHPFile($contents)
526         {
527             if(stripos($contents, '<?php') !== false) return true;
528             for($tag=0;($tag = stripos($contents, '<?', $tag)) !== false;$tag++) {
529             if(strncasecmp(substr($contents, $tag, 13), '<?xml version', 13) == 0) {
530                 // <?xml version is OK, skip it
531                 $tag++;
532                 continue;
533             }
534             // found <?, it's PHP
535             return true;
536             }
537             return false;
538         }
539
540         /**
541          * Given a file it will open it's contents and check if it is a PHP file (not safe to just rely on extensions) if it finds <?php tags it will use the tokenizer to scan the file
542          * $var()  and ` are always prevented then whatever is in the blacklist.
543          * It will also ensure that all files are of valid extension types
544          *
545          */
546         public function scanFile($file){
547                 $issues = array();
548                 if(!$this->isValidExtension($file)){
549                         $issues[] = translate('ML_INVALID_EXT');
550                         $this->issues['file'][$file] = $issues;
551                         return $issues;
552                 }
553                 if($this->isConfigFile($file)){
554                         $issues[] = translate('ML_OVERRIDE_CORE_FILES');
555                         $this->issues['file'][$file] = $issues;
556                         return $issues;
557                 }
558                 $contents = file_get_contents($file);
559                 if(!$this->isPHPFile($contents)) return $issues;
560                 $tokens = @token_get_all($contents);
561                 $checkFunction = false;
562                 $possibleIssue = '';
563                 $lastToken = false;
564                 foreach($tokens as $index=>$token){
565                         if(is_string($token[0])){
566                                 switch($token[0]){
567                                         case '`':
568                                                 $issues['backtick'] = translate('ML_INVALID_FUNCTION') . " '`'";
569                                         case '(':
570                                                 if($checkFunction)$issues[] = $possibleIssue;
571                                                 break;
572                                 }
573                                 $checkFunction = false;
574                                 $possibleIssue = '';
575                         }else{
576                                 $token['_msi'] = token_name($token[0]);
577                                 switch($token[0]){
578                                         case T_WHITESPACE: continue;
579                                         case T_EVAL:
580                                                 if(in_array('eval', $this->blackList) && !in_array('eval', $this->blackListExempt))
581                                                 $issues[]= translate('ML_INVALID_FUNCTION') . ' eval()';
582                                                 break;
583                                         case T_STRING:
584                                                 $token[1] = strtolower($token[1]);
585                                                 if($lastToken !== false && $lastToken[0] == T_NEW) {
586                             if(!in_array($token[1], $this->classBlackList))break;
587                             if(in_array($token[1], $this->classBlackListExempt))break;
588                         } elseif ($token[0] == T_DOUBLE_COLON) {
589                             if(!in_array($lastToken[1], $this->classBlackList))break;
590                             if(in_array($lastToken[1], $this->classBlackListExempt))break;
591                         } else {
592                             //if nothing else fit, lets check the last token to see if this is a possible method call
593                             if ($lastToken !== false &&
594                             ($lastToken[0] == T_OBJECT_OPERATOR ||  $lastToken[0] == T_DOUBLE_COLON))
595                             {
596                                 // check static blacklist for methods
597                                 if(!empty($this->methodsBlackList[$token[1]])) {
598                                     if($this->methodsBlackList[$token[1]] == '*') {
599                                         $issues[]= translate('ML_INVALID_METHOD') . ' ' .$token[1].  '()';
600                                         break;
601                                     } else {
602                                         if($lastToken[0] == T_DOUBLE_COLON && $index > 2 && $tokens[$index-2][0] == T_STRING) {
603                                             $classname = strtolower($tokens[$index-2][1]);
604                                             if(in_array($classname, $this->methodsBlackList[$token[1]])) {
605                                                 $issues[]= translate('ML_INVALID_METHOD') . ' ' .$classname . '::' . $token[1]. '()';
606                                                 break;
607                                             }
608                                         }
609                                     }
610                                 }
611                                 //this is a method call, check the black list
612                                 if(in_array($token[1], $this->methodsBlackList)){
613                                     $issues[]= translate('ML_INVALID_METHOD') . ' ' .$token[1].  '()';
614                                 }
615                                 break;
616                             }
617
618
619                             if(!in_array($token[1], $this->blackList))break;
620                             if(in_array($token[1], $this->blackListExempt))break;
621
622                         }
623                                         case T_VARIABLE:
624                                                 $checkFunction = true;
625                                                 $possibleIssue = translate('ML_INVALID_FUNCTION') . ' ' .  $token[1] . '()';
626                                                 break;
627
628                                         default:
629                                                 $checkFunction = false;
630                                                 $possibleIssue = '';
631
632                                 }
633                                 if ($token[0] != T_WHITESPACE)
634                                 {
635                                         $lastToken = $token;
636                                 }
637                         }
638
639                 }
640                 if(!empty($issues)){
641                         $this->issues['file'][$file] = $issues;
642                 }
643
644                 return $issues;
645         }
646
647
648         /*
649          * checks files.md5 file to see if the file is from sugar
650          * ONLY WORKS ON FILES
651          */
652         public function sugarFileExists($path){
653                 static $md5 = array();
654                 if(empty($md5) && file_exists('files.md5'))
655                 {
656                         include('files.md5');
657                         $md5 = $md5_string;
658                 }
659                 if(isset($md5['./' . $path]))return true;
660
661
662         }
663
664
665         /**
666          *This function will scan the Manifest for disabled actions specified in $GLOBALS['sugar_config']['moduleInstaller']['disableActions']
667          *if $GLOBALS['sugar_config']['moduleInstaller']['disableRestrictedCopy'] is set to false or not set it will call on scanCopy to ensure that it is not overriding files
668          */
669         public function scanManifest($manifestPath){
670                 $issues = array();
671                 if(!file_exists($manifestPath)){
672                         $this->issues['manifest'][$manifestPath] = translate('ML_NO_MANIFEST');
673                         return $issues;
674                 }
675                 $fileIssues = $this->scanFile($manifestPath);
676                 //if the manifest contains malicious code do not open it
677                 if(!empty($fileIssues)){
678                         return $fileIssues;
679                 }
680                 $this->lockConfig();
681                 list($manifest, $installdefs) = MSLoadManifest($manifestPath);
682                 $fileIssues = $this->checkConfig($manifestPath);
683                 if(!empty($fileIssues)){
684                         return $fileIssues;
685                 }
686
687                 //scan for disabled actions
688                 if(isset($this->config['disableActions'])){
689                         foreach($this->config['disableActions'] as $action){
690                                 if(isset($installdefs[$this->manifestMap[$action]])){
691                                         $issues[] = translate('ML_INVALID_ACTION_IN_MANIFEST') . $this->manifestMap[$action];
692                                 }
693                         }
694                 }
695
696                 //now lets scan for files that will override our files
697                 if(empty($this->config['disableRestrictedCopy']) && isset($installdefs['copy'])){
698                         foreach($installdefs['copy'] as $copy){
699                                 $from = str_replace('<basepath>', $this->pathToModule, $copy['from']);
700                                 $to = $copy['to'];
701                                 if(substr_count($from, '..')){
702                                         $this->issues['copy'][$from] = translate('ML_PATH_MAY_NOT_CONTAIN').' ".." -' . $from;
703                                 }
704                                 if(substr_count($to, '..')){
705                                         $this->issues['copy'][$to] = translate('ML_PATH_MAY_NOT_CONTAIN'). ' ".." -' . $to;
706                                 }
707                                 while(substr_count($from, '//')){
708                                         $from = str_replace('//', '/', $from);
709                                 }
710                                 while(substr_count($to, '//')){
711                                         $to = str_replace('//', '/', $to);
712                                 }
713                                 $this->scanCopy($from, $to);
714                         }
715                 }
716                 if(!empty($issues)){
717                         $this->issues['manifest'][$manifestPath] = $issues;
718                 }
719
720
721
722         }
723
724
725
726         /**
727          * Takes in where the file will is specified to be copied from and to
728          * and ensures that there is no official sugar file there. If the file exists it will check
729          * against the MD5 file list to see if Sugar Created the file
730          *
731          */
732         function scanCopy($from, $to){
733                                 //if the file doesn't exist for the $to then it is not overriding anything
734                                 if(!file_exists($to))return;
735                                 //if $to is a dir and $from is a file then make $to a full file path as well
736                                 if(is_dir($to) && is_file($from)){
737                                         if(substr($to,-1) === '/'){
738                                                 $to = substr($to, 0 , strlen($to) - 1);
739                                         }
740                                         $to .= '/'. basename($from);
741                                 }
742                                 //if the $to is a file and it is found in sugarFileExists then don't allow overriding it
743                                 if(is_file($to) && $this->sugarFileExists($to)){
744                                         $this->issues['copy'][$from] = translate('ML_OVERRIDE_CORE_FILES') . '(' . $to . ')';
745                                 }
746
747                                 if(is_dir($from)){
748                                         $d = dir($from);
749                                         while($e = $d->read()){
750                                                 if($e == '.' || $e == '..')continue;
751                                                 $this->scanCopy($from .'/'. $e, $to .'/' . $e);
752                                         }
753                                 }
754
755
756
757
758
759                         }
760
761
762         /**
763          *Main external function that takes in a path to a package and then scans
764          *that package's manifest for disabled actions and then it scans the PHP files
765          *for restricted function calls
766          *
767          */
768         public function scanPackage($path){
769                 $this->pathToModule = $path;
770                 $this->scanManifest($path . '/manifest.php');
771                 if(empty($this->config['disableFileScan'])){
772                         $this->scanDir($path);
773                 }
774         }
775
776         /**
777          *This function will take all issues of the current instance and print them to the screen
778          **/
779         public function displayIssues($package='Package'){
780                 echo '<h2>'.str_replace('{PACKAGE}' , $package ,translate('ML_PACKAGE_SCANNING')). '</h2><BR><h2 class="error">' . translate('ML_INSTALLATION_FAILED') . '</h2><br><p>' .str_replace('{PACKAGE}' , $package ,translate('ML_PACKAGE_NOT_CONFIRM')). '</p><ul><li>'. translate('ML_OBTAIN_NEW_PACKAGE') . '<li>' . translate('ML_RELAX_LOCAL').
781 '</ul></p><br>' . translate('ML_SUGAR_LOADING_POLICY') .  ' <a href=" http://kb.sugarcrm.com/custom/module-loader-restrictions-for-sugar-open-cloud/">' . translate('ML_SUGAR_KB') . '</a>.'.
782 '<br>' . translate('ML_AVAIL_RESTRICTION'). ' <a href=" http://developers.sugarcrm.com/wordpress/2009/08/14/module-loader-restrictions/">' . translate('ML_SUGAR_DZ') .  '</a>.<br><br>';
783
784
785                 foreach($this->issues as $type=>$issues){
786                         echo '<div class="error"><h2>'. ucfirst($type) .' ' .  translate('ML_ISSUES') . '</h2> </div>';
787                         echo '<div id="details' . $type . '" >';
788                         foreach($issues as $file=>$issue){
789                                 $file = str_replace($this->pathToModule . '/', '', $file);
790                                 echo '<div style="position:relative;left:10px"><b>' . $file . '</b></div><div style="position:relative;left:20px">';
791                                 if(is_array($issue)){
792                                         foreach($issue as $i){
793                                                 echo "$i<br>";
794                                         }
795                                 }else{
796                                         echo "$issue<br>";
797                                 }
798                                 echo "</div>";
799                         }
800                         echo '</div>';
801
802                 }
803                 echo "<br><input class='button' onclick='document.location.href=\"index.php?module=Administration&action=UpgradeWizard&view=module\"' type='button' value=\"" . translate('LBL_UW_BTN_BACK_TO_MOD_LOADER') . "\" />";
804
805         }
806
807         /**
808          * Lock config settings
809          */
810         public function lockConfig()
811         {
812             if(empty($this->config_hash)) {
813                 $this->config_hash = md5(serialize($GLOBALS['sugar_config']));
814             }
815         }
816
817         /**
818          * Check if config was modified. Return
819          * @param string $file
820          * @return array Errors if something wrong, false if no problems
821          */
822         public function checkConfig($file)
823         {
824             $config_hash_after = md5(serialize($GLOBALS['sugar_config']));
825             if($config_hash_after != $this->config_hash) {
826                 $this->issues['file'][$file] = array(translate('ML_CONFIG_OVERRIDE'));
827                 return $this->issues;
828             }
829             return false;
830         }
831
832 }
833
834 /**
835  * Load manifest file
836  * Outside of the class to isolate the context
837  * @param string $manifest_file
838  * @return array
839  */
840 function MSLoadManifest($manifest_file)
841 {
842         include( $manifest_file );
843         return array($manifest, $installdefs);
844 }
845
846 ?>