Completed
Branch develop (c604ce)
by
unknown
32:31
created

files.lib.php ➔ deleteFilesIntoDatabaseIndex()   C

Complexity

Conditions 7
Paths 19

Size

Total Lines 45
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 27
nc 19
nop 3
dl 0
loc 45
rs 6.7272
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2008-2012  Laurent Destailleur <[email protected]>
3
 * Copyright (C) 2012-2015  Regis Houssin       <[email protected]>
4
 * Copyright (C) 2012-2016  Juanjo Menent       <[email protected]>
5
 * Copyright (C) 2015       Marcos García       <[email protected]>
6
 * Copyright (C) 2016       Raphaël Doursenaud  <[email protected]>
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20
 * or see http://www.gnu.org/
21
 */
22
23
/**
24
 *  \file		htdocs/core/lib/files.lib.php
25
 *  \brief		Library for file managing functions
26
 */
27
28
/**
29
 * Make a basename working with all page code (default PHP basenamed fails with cyrillic).
30
 * We supose dir separator for input is '/'.
31
 *
32
 * @param	string	$pathfile	String to find basename.
33
 * @return	string				Basename of input
34
 */
35
function dol_basename($pathfile)
36
{
37
	return preg_replace('/^.*\/([^\/]+)$/','$1',rtrim($pathfile,'/'));
38
}
39
40
/**
41
 *  Scan a directory and return a list of files/directories.
42
 *  Content for string is UTF8 and dir separator is "/".
43
 *
44
 *  @param	string		$path        	Starting path from which to search. This is a full path.
45
 *  @param	string		$types        	Can be "directories", "files", or "all"
46
 *  @param	int			$recursive		Determines whether subdirectories are searched
47
 *  @param	string		$filter        	Regex filter to restrict list. This regex value must be escaped for '/' by doing preg_quote($var,'/'), since this char is used for preg_match function,
48
 *                                      but must not contains the start and end '/'. Filter is checked into basename only.
49
 *  @param	array		$excludefilter  Array of Regex for exclude filter (example: array('(\.meta|_preview.*\.png)$','^\.')). Exclude is checked both into fullpath and into basename (So '^xxx' may exclude 'xxx/dirscanned/...' and dirscanned/xxx').
50
 *  @param	string		$sortcriteria	Sort criteria ('','fullname','relativename','name','date','size')
51
 *  @param	string		$sortorder		Sort order (SORT_ASC, SORT_DESC)
52
 *	@param	int			$mode			0=Return array minimum keys loaded (faster), 1=Force all keys like date and size to be loaded (slower), 2=Force load of date only, 3=Force load of size only
53
 *  @param	int			$nohook			Disable all hooks
54
 *  @param	string		$relativename	For recursive purpose only. Must be "" at first call.
55
 *  @return	array						Array of array('name'=>'xxx','fullname'=>'/abc/xxx','date'=>'yyy','size'=>99,'type'=>'dir|file',...)
56
 *  @see dol_dir_list_indatabase
57
 */
58
function dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="")
59
{
60
	global $db, $hookmanager;
61
	global $object;
62
63
	dol_syslog("files.lib.php::dol_dir_list path=".$path." types=".$types." recursive=".$recursive." filter=".$filter." excludefilter=".json_encode($excludefilter));
64
	//print 'xxx'."files.lib.php::dol_dir_list path=".$path." types=".$types." recursive=".$recursive." filter=".$filter." excludefilter=".json_encode($excludefilter);
65
66
	$loaddate=($mode==1||$mode==2)?true:false;
67
	$loadsize=($mode==1||$mode==3)?true:false;
68
69
	// Clean parameters
70
	$path=preg_replace('/([\\/]+)$/i','',$path);
71
	$newpath=dol_osencode($path);
72
73
	$reshook = 0;
74
	$file_list = array();
75
76
	if (is_object($hookmanager) && ! $nohook)
77
	{
78
		$hookmanager->resArray=array();
79
80
		$hookmanager->initHooks(array('fileslib'));
81
82
		$parameters=array(
83
				'path' => $newpath,
84
				'types'=> $types,
85
				'recursive' => $recursive,
86
				'filter' => $filter,
87
				'excludefilter' => $excludefilter,
88
				'sortcriteria' => $sortcriteria,
89
				'sortorder' => $sortorder,
90
				'loaddate' => $loaddate,
91
				'loadsize' => $loadsize,
92
				'mode' => $mode
93
		);
94
		$reshook=$hookmanager->executeHooks('getDirList', $parameters, $object);
95
	}
96
97
	// $hookmanager->resArray may contain array stacked by other modules
98
	if (empty($reshook))
99
	{
100
		if (! is_dir($newpath)) return array();
101
102
		if ($dir = opendir($newpath))
103
		{
104
			$filedate='';
105
			$filesize='';
106
107
			while (false !== ($file = readdir($dir)))        // $file is always a basename (into directory $newpath)
108
			{
109
				if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure data is stored in utf8 in memory
110
				$fullpathfile=($newpath?$newpath.'/':'').$file;
111
112
				$qualified=1;
113
114
				// Define excludefilterarray
115
				$excludefilterarray=array('^\.');
116
				if (is_array($excludefilter))
117
				{
118
					$excludefilterarray=array_merge($excludefilterarray,$excludefilter);
119
				}
120
				else if ($excludefilter) $excludefilterarray[]=$excludefilter;
121
				// Check if file is qualified
122
				foreach($excludefilterarray as $filt)
123
				{
124
					if (preg_match('/'.$filt.'/i', $file) || preg_match('/'.$filt.'/i', $fullpathfile)) {
125
						$qualified=0; break;
126
					}
127
				}
128
				//print $fullpathfile.' '.$file.' '.$qualified.'<br>';
129
130
				if ($qualified)
131
				{
132
					$isdir=is_dir(dol_osencode($path."/".$file));
133
					// Check whether this is a file or directory and whether we're interested in that type
134
					if ($isdir && (($types=="directories") || ($types=="all") || $recursive))
135
					{
136
						// Add entry into file_list array
137
						if (($types=="directories") || ($types=="all"))
138
						{
139
							if ($loaddate || $sortcriteria == 'date') $filedate=dol_filemtime($path."/".$file);
140
							if ($loadsize || $sortcriteria == 'size') $filesize=dol_filesize($path."/".$file);
141
142
							if (! $filter || preg_match('/'.$filter.'/i',$file))	// We do not search key $filter into all $path, only into $file part
143
							{
144
								preg_match('/([^\/]+)\/[^\/]+$/',$path.'/'.$file,$reg);
145
								$level1name=(isset($reg[1])?$reg[1]:'');
146
								$file_list[] = array(
147
										"name" => $file,
148
										"path" => $path,
149
										"level1name" => $level1name,
150
										"relativename" => ($relativename?$relativename.'/':'').$file,
151
										"fullname" => $path.'/'.$file,
152
										"date" => $filedate,
153
										"size" => $filesize,
154
										"type" => 'dir'
155
								);
156
							}
157
						}
158
159
						// if we're in a directory and we want recursive behavior, call this function again
160
						if ($recursive)
161
						{
162
							$file_list = array_merge($file_list, dol_dir_list($path."/".$file, $types, $recursive, $filter, $excludefilter, $sortcriteria, $sortorder, $mode, $nohook, ($relativename!=''?$relativename.'/':'').$file));
163
						}
164
					}
165
					else if (! $isdir && (($types == "files") || ($types == "all")))
166
					{
167
						// Add file into file_list array
168
						if ($loaddate || $sortcriteria == 'date') $filedate=dol_filemtime($path."/".$file);
169
						if ($loadsize || $sortcriteria == 'size') $filesize=dol_filesize($path."/".$file);
170
171
						if (! $filter || preg_match('/'.$filter.'/i',$file))	// We do not search key $filter into $path, only into $file
172
						{
173
							preg_match('/([^\/]+)\/[^\/]+$/',$path.'/'.$file,$reg);
174
							$level1name=(isset($reg[1])?$reg[1]:'');
175
							$file_list[] = array(
176
									"name" => $file,
177
									"path" => $path,
178
									"level1name" => $level1name,
179
									"relativename" => ($relativename?$relativename.'/':'').$file,
180
									"fullname" => $path.'/'.$file,
181
									"date" => $filedate,
182
									"size" => $filesize,
183
									"type" => 'file'
184
							);
185
						}
186
					}
187
				}
188
			}
189
			closedir($dir);
190
191
			// Obtain a list of columns
192
			if (! empty($sortcriteria))
193
			{
194
				$myarray=array();
195
				foreach ($file_list as $key => $row)
196
				{
197
					$myarray[$key] = (isset($row[$sortcriteria])?$row[$sortcriteria]:'');
198
				}
199
				// Sort the data
200
				if ($sortorder) array_multisort($myarray, $sortorder, $file_list);
201
			}
202
		}
203
	}
204
205
	if (is_object($hookmanager) && is_array($hookmanager->resArray)) $file_list = array_merge($file_list, $hookmanager->resArray);
206
207
	return $file_list;
208
}
209
210
211
/**
212
 *  Scan a directory and return a list of files/directories.
213
 *  Content for string is UTF8 and dir separator is "/".
214
 *
215
 *  @param	string		$path        	Starting path from which to search. Example: 'produit/MYPROD'
216
 *  @param	string		$filter        	Regex filter to restrict list. This regex value must be escaped for '/', since this char is used for preg_match function
217
 *  @param	array|null	$excludefilter  Array of Regex for exclude filter (example: array('(\.meta|_preview.*\.png)$','^\.'))
218
 *  @param	string		$sortcriteria	Sort criteria ("","fullname","name","date","size")
219
 *  @param	string		$sortorder		Sort order (SORT_ASC, SORT_DESC)
220
 *	@param	int			$mode			0=Return array minimum keys loaded (faster), 1=Force all keys like description
221
 *  @return	array						Array of array('name'=>'xxx','fullname'=>'/abc/xxx','type'=>'dir|file',...)
222
 *  @see dol_dir_list
223
 */
224
function dol_dir_list_in_database($path, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0)
225
{
226
	global $conf, $db;
227
228
	$sql =" SELECT rowid, label, entity, filename, filepath, fullpath_orig, keywords, cover, gen_or_uploaded, extraparams, date_c, date_m, fk_user_c, fk_user_m,";
229
	$sql.=" acl, position, share";
230
	if ($mode) $sql.=", description";
231
	$sql.=" FROM ".MAIN_DB_PREFIX."ecm_files";
232
	$sql.=" WHERE filepath = '".$db->escape($path)."'";
233
	$sql.=" AND entity = ".$conf->entity;
234
235
	$resql = $db->query($sql);
236
	if ($resql)
237
	{
238
		$file_list=array();
239
		$num = $db->num_rows($resql);
240
		$i = 0;
241
		while ($i < $num)
242
		{
243
			$obj = $db->fetch_object($resql);
244
			if ($obj)
245
			{
246
				preg_match('/([^\/]+)\/[^\/]+$/',DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename,$reg);
247
				$level1name=(isset($reg[1])?$reg[1]:'');
248
				$file_list[] = array(
249
					"rowid" => $obj->rowid,
250
					"label" => $obj->label,         // md5
251
					"name" => $obj->filename,
252
					"path" => DOL_DATA_ROOT.'/'.$obj->filepath,
253
					"level1name" => $level1name,
254
					"fullname" => DOL_DATA_ROOT.'/'.$obj->filepath.'/'.$obj->filename,
255
					"fullpath_orig" => $obj->fullpath_orig,
256
					"date_c" => $db->jdate($obj->date_c),
257
					"date_m" => $db->jdate($obj->date_m),
258
					"type" => 'file',
259
					"keywords" => $obj->keywords,
260
					"cover" => $obj->cover,
261
					"position" => (int) $obj->position,
262
					"acl" => $obj->acl,
263
					"share" => $obj->share
264
				);
265
			}
266
			$i++;
267
		}
268
269
		// Obtain a list of columns
270
		if (! empty($sortcriteria))
271
		{
272
			$myarray=array();
273
			foreach ($file_list as $key => $row)
274
			{
275
				$myarray[$key] = (isset($row[$sortcriteria])?$row[$sortcriteria]:'');
276
			}
277
			// Sort the data
278
			if ($sortorder) array_multisort($myarray, $sortorder, $file_list);
279
		}
280
281
		return $file_list;
282
	}
283
	else
284
	{
285
		dol_print_error($db);
286
		return array();
287
	}
288
}
289
290
291
/**
292
 * Complete $filearray with data from database.
293
 * This will call doldir_list_indatabase to complate filearray.
294
 *
295
 * @param	array	$filearray		Array of files get using dol_dir_list
296
 * @param	string	$relativedir		Relative dir from DOL_DATA_ROOT
297
 * @return	void
298
 */
299
function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir)
300
{
301
	global $db, $user;
302
303
	$filearrayindatabase = dol_dir_list_in_database($relativedir, '', null, 'name', SORT_ASC);
304
305
	//var_dump($filearray);
306
	//var_dump($filearrayindatabase);
307
308
	// Complete filearray with properties found into $filearrayindatabase
309
	foreach($filearray as $key => $val)
310
	{
311
		$found=0;
312
		// Search if it exists into $filearrayindatabase
313
		foreach($filearrayindatabase as $key2 => $val2)
314
		{
315
			if ($filearrayindatabase[$key2]['name'] == $filearray[$key]['name'])
316
			{
317
				$filearray[$key]['position_name']=($filearrayindatabase[$key2]['position']?$filearrayindatabase[$key2]['position']:'0').'_'.$filearrayindatabase[$key2]['name'];
318
				$filearray[$key]['position']=$filearrayindatabase[$key2]['position'];
319
				$filearray[$key]['cover']=$filearrayindatabase[$key2]['cover'];
320
				$filearray[$key]['acl']=$filearrayindatabase[$key2]['acl'];
321
				$filearray[$key]['rowid']=$filearrayindatabase[$key2]['rowid'];
322
				$filearray[$key]['label']=$filearrayindatabase[$key2]['label'];
323
				$filearray[$key]['share']=$filearrayindatabase[$key2]['share'];
324
				$found=1;
325
				break;
326
			}
327
		}
328
329
		if (! $found)    // This happen in transition toward version 6, or if files were added manually into os dir.
330
		{
331
			$filearray[$key]['position']='999999';     // File not indexed are at end. So if we add a file, it will not replace an existing position
332
			$filearray[$key]['cover']=0;
333
			$filearray[$key]['acl']='';
334
335
			$rel_filename = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $filearray[$key]['fullname']);
336
			if (! preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetorenameafter))     // If not a tmp file
0 ignored issues
show
Bug introduced by
The variable $rel_filetorenameafter does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
337
			{
338
				dol_syslog("list_of_documents We found a file called '".$filearray[$key]['name']."' not indexed into database. We add it");
339
				include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
340
				$ecmfile=new EcmFiles($db);
341
342
				// Add entry into database
343
				$filename = basename($rel_filename);
344
				$rel_dir = dirname($rel_filename);
345
				$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
346
				$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
347
348
				$ecmfile->filepath = $rel_dir;
349
				$ecmfile->filename = $filename;
350
				$ecmfile->label = md5_file(dol_osencode($filearray[$key]['fullname']));        // $destfile is a full path to file
351
				$ecmfile->fullpath_orig = $filearray[$key]['fullname'];
352
				$ecmfile->gen_or_uploaded = 'unknown';
353
				$ecmfile->description = '';    // indexed content
354
				$ecmfile->keyword = '';        // keyword content
355
				$result = $ecmfile->create($user);
356
				if ($result < 0)
357
				{
358
					setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
359
				}
360
				else
361
				{
362
					$filearray[$key]['rowid']=$result;
363
				}
364
			}
365
			else
366
			{
367
				$filearray[$key]['rowid']=0;     // Should not happened
368
			}
369
		}
370
	}
371
372
	/*var_dump($filearray);*/
373
}
374
375
376
/**
377
 * Fast compare of 2 files identified by their properties ->name, ->date and ->size
378
 *
379
 * @param	string 	$a		File 1
380
 * @param 	string	$b		File 2
381
 * @return 	int				1, 0, 1
382
 */
383
function dol_compare_file($a, $b)
384
{
385
	global $sortorder;
386
	global $sortfield;
387
388
	$sortorder=strtoupper($sortorder);
389
390
	if ($sortorder == 'ASC') { $retup=-1; $retdown=1; }
391
	else { $retup=1; $retdown=-1; }
392
393
	if ($sortfield == 'name')
394
	{
395
		if ($a->name == $b->name) return 0;
396
		return ($a->name < $b->name) ? $retup : $retdown;
397
	}
398
	if ($sortfield == 'date')
399
	{
400
		if ($a->date == $b->date) return 0;
401
		return ($a->date < $b->date) ? $retup : $retdown;
402
	}
403
	if ($sortfield == 'size')
404
	{
405
		if ($a->size == $b->size) return 0;
406
		return ($a->size < $b->size) ? $retup : $retdown;
407
	}
408
}
409
410
411
/**
412
 * Test if filename is a directory
413
 *
414
 * @param	string		$folder     Name of folder
415
 * @return	boolean     			True if it's a directory, False if not found
416
 */
417
function dol_is_dir($folder)
418
{
419
	$newfolder=dol_osencode($folder);
420
	if (is_dir($newfolder)) return true;
421
	else return false;
422
}
423
424
/**
425
 * Return if path is a file
426
 *
427
 * @param   string		$pathoffile		Path of file
428
 * @return  boolean     			    True or false
429
 */
430
function dol_is_file($pathoffile)
431
{
432
	$newpathoffile=dol_osencode($pathoffile);
433
	return is_file($newpathoffile);
434
}
435
436
/**
437
 * Return if path is an URL
438
 *
439
 * @param   string		$url	Url
440
 * @return  boolean      	   	True or false
441
 */
442
function dol_is_url($url)
443
{
444
	$tmpprot=array('file','http','https','ftp','zlib','data','ssh','ssh2','ogg','expect');
445
	foreach($tmpprot as $prot)
446
	{
447
		if (preg_match('/^'.$prot.':/i',$url)) return true;
448
	}
449
	return false;
450
}
451
452
/**
453
 * 	Test if a folder is empty
454
 *
455
 * 	@param	string	$folder		Name of folder
456
 * 	@return boolean				True if dir is empty or non-existing, False if it contains files
457
 */
458
function dol_dir_is_emtpy($folder)
459
{
460
	$newfolder=dol_osencode($folder);
461
	if (is_dir($newfolder))
462
	{
463
		$handle = opendir($newfolder);
464
		$folder_content = '';
465
		while ((gettype($name = readdir($handle)) != "boolean"))
466
		{
467
			$name_array[] = $name;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$name_array was never initialized. Although not strictly required by PHP, it is generally a good practice to add $name_array = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
468
		}
469
		foreach($name_array as $temp) $folder_content .= $temp;
0 ignored issues
show
Bug introduced by
The variable $name_array does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
470
471
		closedir($handle);
472
473
		if ($folder_content == "...") return true;
474
		else return false;
475
	}
476
	else
477
	return true; // Dir does not exists
478
}
479
480
/**
481
 * 	Count number of lines in a file
482
 *
483
 * 	@param	string	$file		Filename
484
 * 	@return int					<0 if KO, Number of lines in files if OK
485
 *  @see dol_nboflines
486
 */
487
function dol_count_nb_of_line($file)
488
{
489
	$nb=0;
490
491
	$newfile=dol_osencode($file);
492
	//print 'x'.$file;
493
	$fp=fopen($newfile,'r');
494
	if ($fp)
495
	{
496
		while (!feof($fp))
497
		{
498
			$line=fgets($fp);
499
			// We increase count only if read was success. We need test because feof return true only after fgets so we do n+1 fgets for a file with n lines.
500
			if (! $line === false) $nb++;
501
		}
502
		fclose($fp);
503
	}
504
	else
505
	{
506
		$nb=-1;
507
	}
508
509
	return $nb;
510
}
511
512
513
/**
514
 * Return size of a file
515
 *
516
 * @param 	string		$pathoffile		Path of file
517
 * @return 	integer						File size
518
 */
519
function dol_filesize($pathoffile)
520
{
521
	$newpathoffile=dol_osencode($pathoffile);
522
	return filesize($newpathoffile);
523
}
524
525
/**
526
 * Return time of a file
527
 *
528
 * @param 	string		$pathoffile		Path of file
529
 * @return 	int					Time of file
530
 */
531
function dol_filemtime($pathoffile)
532
{
533
	$newpathoffile=dol_osencode($pathoffile);
534
	return @filemtime($newpathoffile); // @Is to avoid errors if files does not exists
535
}
536
537
/**
538
 * Make replacement of strings into a file.
539
 *
540
 * @param	string	$srcfile			Source file (can't be a directory)
541
 * @param	array	$arrayreplacement	Array with strings to replace. Example: array('valuebefore'=>'valueafter', ...)
542
 * @param	string	$destfile			Destination file (can't be a directory). If empty, will be same than source file.
543
 * @param	int		$newmask			Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666'
544
 * @param	int		$indexdatabase		Index new file into database.
545
 * @return	int							<0 if error, 0 if nothing done (dest file already exists), >0 if OK
546
 * @see		dol_copy dolReplaceRegExInFile
547
 */
548
function dolReplaceInFile($srcfile, $arrayreplacement, $destfile='', $newmask=0, $indexdatabase=0)
549
{
550
	global $conf;
551
552
	dol_syslog("files.lib.php::dolReplaceInFile srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." indexdatabase=".$indexdatabase);
553
554
	if (empty($srcfile)) return -1;
555
	if (empty($destfile)) $destfile=$srcfile;
556
557
	$destexists=dol_is_file($destfile);
558
	if (($destfile != $srcfile) && $destexists) return 0;
559
560
	$tmpdestfile=$destfile.'.tmp';
561
562
	$newpathofsrcfile=dol_osencode($srcfile);
563
	$newpathoftmpdestfile=dol_osencode($tmpdestfile);
564
	$newpathofdestfile=dol_osencode($destfile);
565
	$newdirdestfile=dirname($newpathofdestfile);
566
567
	if ($destexists && ! is_writable($newpathofdestfile))
568
	{
569
		dol_syslog("files.lib.php::dolReplaceInFile failed Permission denied to overwrite target file", LOG_WARNING);
570
		return -1;
571
	}
572
	if (! is_writable($newdirdestfile))
573
	{
574
		dol_syslog("files.lib.php::dolReplaceInFile failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING);
575
		return -2;
576
	}
577
578
	dol_delete_file($tmpdestfile);
579
580
	// Create $newpathoftmpdestfile from $newpathofsrcfile
581
	$content=file_get_contents($newpathofsrcfile, 'r');
582
583
	$content = make_substitutions($content, $arrayreplacement, null);
584
585
	file_put_contents($newpathoftmpdestfile, $content);
586
	@chmod($newpathoftmpdestfile, octdec($newmask));
587
588
	// Rename
589
	$result=dol_move($newpathoftmpdestfile, $newpathofdestfile, $newmask, (($destfile == $srcfile)?1:0), 0, $indexdatabase);
590
	if (! $result)
591
	{
592
		dol_syslog("files.lib.php::dolReplaceInFile failed to move tmp file to final dest", LOG_WARNING);
593
		return -3;
594
	}
595
	if (empty($newmask) && ! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK;
596
	if (empty($newmask))	// This should no happen
597
	{
598
		dol_syslog("Warning: dolReplaceInFile called with empty value for newmask and no default value defined", LOG_WARNING);
599
		$newmask='0664';
600
	}
601
602
	@chmod($newpathofdestfile, octdec($newmask));
603
604
	return 1;
605
}
606
607
/**
608
 * Make replacement of strings into a file.
609
 *
610
 * @param	string	$srcfile			Source file (can't be a directory)
611
 * @param	array	$arrayreplacement	Array with strings to replace. Example: array('valuebefore'=>'valueafter', ...)
612
 * @param	string	$destfile			Destination file (can't be a directory). If empty, will be same than source file.
613
 * @param	int		$newmask			Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666'
614
 * @param	int		$indexdatabase		Index new file into database.
615
 * @return	int							<0 if error, 0 if nothing done (dest file already exists), >0 if OK
616
 * @see		dol_copy dolReplaceInFile
617
 */
618
function dolReplaceRegExInFile($srcfile, $arrayreplacement, $destfile='', $newmask=0, $indexdatabase=0)
619
{
620
	// TODO
621
622
}
623
624
/**
625
 * Copy a file to another file.
626
 *
627
 * @param	string	$srcfile			Source file (can't be a directory)
628
 * @param	string	$destfile			Destination file (can't be a directory)
629
 * @param	int		$newmask			Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666'
630
 * @param 	int		$overwriteifexists	Overwrite file if exists (1 by default)
631
 * @return	int							<0 if error, 0 if nothing done (dest file already exists and overwriteifexists=0), >0 if OK
632
 * @see		dol_delete_file
633
 */
634
function dol_copy($srcfile, $destfile, $newmask=0, $overwriteifexists=1)
635
{
636
	global $conf;
637
638
	dol_syslog("files.lib.php::dol_copy srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists);
639
640
	if (empty($srcfile) || empty($destfile)) return -1;
641
642
	$destexists=dol_is_file($destfile);
643
	if (! $overwriteifexists && $destexists) return 0;
644
645
	$newpathofsrcfile=dol_osencode($srcfile);
646
	$newpathofdestfile=dol_osencode($destfile);
647
	$newdirdestfile=dirname($newpathofdestfile);
648
649
	if ($destexists && ! is_writable($newpathofdestfile))
650
	{
651
		dol_syslog("files.lib.php::dol_copy failed Permission denied to overwrite target file", LOG_WARNING);
652
		return -1;
653
	}
654
	if (! is_writable($newdirdestfile))
655
	{
656
		dol_syslog("files.lib.php::dol_copy failed Permission denied to write into target directory ".$newdirdestfile, LOG_WARNING);
657
		return -2;
658
	}
659
	// Copy with overwriting if exists
660
	$result=@copy($newpathofsrcfile, $newpathofdestfile);
661
	//$result=copy($newpathofsrcfile, $newpathofdestfile);	// To see errors, remove @
662
	if (! $result)
663
	{
664
		dol_syslog("files.lib.php::dol_copy failed to copy", LOG_WARNING);
665
		return -3;
666
	}
667
	if (empty($newmask) && ! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK;
668
	if (empty($newmask))	// This should no happen
669
	{
670
		dol_syslog("Warning: dol_copy called with empty value for newmask and no default value defined", LOG_WARNING);
671
		$newmask='0664';
672
	}
673
674
	@chmod($newpathofdestfile, octdec($newmask));
675
676
	return 1;
677
}
678
679
/**
680
 * Copy a dir to another dir. This include recursive subdirectories.
681
 *
682
 * @param	string	$srcfile			Source file (a directory)
683
 * @param	string	$destfile			Destination file (a directory)
684
 * @param	int		$newmask			Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0666'
685
 * @param 	int		$overwriteifexists	Overwrite file if exists (1 by default)
686
 * @param	array	$arrayreplacement	Array to use to replace filenames with another one during the copy (works only on file names, not on directory names).
687
 * @return	int							<0 if error, 0 if nothing done (all files already exists and overwriteifexists=0), >0 if OK
688
 * @see		dol_copy
689
 */
690
function dolCopyDir($srcfile, $destfile, $newmask, $overwriteifexists, $arrayreplacement=null)
691
{
692
	global $conf;
693
694
	$result=0;
695
696
	dol_syslog("files.lib.php::dolCopyDir srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwriteifexists=".$overwriteifexists);
697
698
	if (empty($srcfile) || empty($destfile)) return -1;
699
700
	$destexists=dol_is_dir($destfile);
701
	//if (! $overwriteifexists && $destexists) return 0;	// The overwriteifexists is for files only, so propagated to dol_copy only.
702
703
	if (! $destexists)
704
	{
705
		// We must set mask just before creating dir, becaause it can be set differently by dol_copy
706
		umask(0);
707
		$dirmaskdec=octdec($newmask);
708
		if (empty($newmask) && ! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK);
709
		$dirmaskdec |= octdec('0200');  // Set w bit required to be able to create content for recursive subdirs files
710
		dol_mkdir($destfile, '', decoct($dirmaskdec));
711
	}
712
713
	$ossrcfile=dol_osencode($srcfile);
714
	$osdestfile=dol_osencode($destfile);
715
716
	// Recursive function to copy all subdirectories and contents:
717
	if (is_dir($ossrcfile))
718
	{
719
		$dir_handle=opendir($ossrcfile);
720
		while ($file=readdir($dir_handle))
721
		{
722
			if ($file != "." && $file != ".." && ! is_link($ossrcfile."/".$file))
723
			{
724
				if (is_dir($ossrcfile."/".$file))
725
				{
726
					//var_dump("xxx dolCopyDir $srcfile/$file, $destfile/$file, $newmask, $overwriteifexists");
727
					$tmpresult=dolCopyDir($srcfile."/".$file, $destfile."/".$file, $newmask, $overwriteifexists, $arrayreplacement);
728
				}
729
				else
730
				{
731
					$newfile = $file;
732
					// Replace destination filename with a new one
733
					if (is_array($arrayreplacement))
734
					{
735
						foreach($arrayreplacement as $key => $val)
736
						{
737
							$newfile = str_replace($key, $val, $newfile);
738
						}
739
					}
740
					$tmpresult=dol_copy($srcfile."/".$file, $destfile."/".$newfile, $newmask, $overwriteifexists);
741
				}
742
				// Set result
743
				if ($result > 0 && $tmpresult >= 0)
744
				{
745
					// Do nothing, so we don't set result to 0 if tmpresult is 0 and result was success in a previous pass
746
				}
747
				else
748
				{
749
					$result=$tmpresult;
750
				}
751
				if ($result < 0) break;
752
753
			}
754
		}
755
		closedir($dir_handle);
756
	}
757
	else
758
	{
759
		// Source directory does not exists
760
		$result = -2;
761
	}
762
763
	return $result;
764
}
765
766
767
/**
768
 * Move a file into another name.
769
 * Note:
770
 *  - This function differs from dol_move_uploaded_file, because it can be called in any context.
771
 *  - Database indexes for files are updated.
772
 *  - Test on antivirus is done only if param testvirus is provided and an antivirus was set.
773
 *
774
 * @param	string  $srcfile            Source file (can't be a directory. use native php @rename() to move a directory)
775
 * @param   string	$destfile           Destination file (can't be a directory. use native php @rename() to move a directory)
776
 * @param   integer	$newmask            Mask in octal string for new file (0 by default means $conf->global->MAIN_UMASK)
777
 * @param   int		$overwriteifexists  Overwrite file if exists (1 by default)
778
 * @param   int     $testvirus          Do an antivirus test. Move is canceled if a virus is found.
779
 * @param	int		$indexdatabase		Index new file into database.
780
 * @return  boolean 		            True if OK, false if KO
781
 * @see dol_move_uploaded_file
782
 */
783
function dol_move($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=1)
784
{
785
	global $user, $db, $conf;
786
	$result=false;
787
788
	dol_syslog("files.lib.php::dol_move srcfile=".$srcfile." destfile=".$destfile." newmask=".$newmask." overwritifexists=".$overwriteifexists);
789
	$srcexists=dol_is_file($srcfile);
790
	$destexists=dol_is_file($destfile);
791
792
	if (! $srcexists)
793
	{
794
		dol_syslog("files.lib.php::dol_move srcfile does not exists. we ignore the move request.");
795
		return false;
796
	}
797
798
	if ($overwriteifexists || ! $destexists)
799
	{
800
		$newpathofsrcfile=dol_osencode($srcfile);
801
		$newpathofdestfile=dol_osencode($destfile);
802
803
		// Check virus
804
		$testvirusarray=array();
805
		if ($testvirus)
806
		{
807
			$testvirusarray=dolCheckVirus($newpathofsrcfile);
808
			if (count($testvirusarray))
809
			{
810
				dol_syslog("files.lib.php::dol_move canceled because a virus was found into source file. we ignore the move request.", LOG_WARNING);
811
				return false;
812
			}
813
		}
814
815
		$result=@rename($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
816
		if (! $result)
817
		{
818
			if ($destexists)
819
			{
820
				dol_syslog("files.lib.php::dol_move Failed. We try to delete target first and move after.", LOG_WARNING);
821
				// We force delete and try again. Rename function sometimes fails to replace dest file with some windows NTFS partitions.
822
				dol_delete_file($destfile);
823
				$result=@rename($newpathofsrcfile, $newpathofdestfile); // To see errors, remove @
824
			}
825
			else dol_syslog("files.lib.php::dol_move Failed.", LOG_WARNING);
826
		}
827
828
		// Move ok
829
		if ($result && $indexdatabase)
830
		{
831
			// Rename entry into ecm database
832
			$rel_filetorenamebefore = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $srcfile);
833
			$rel_filetorenameafter = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $destfile);
834
			if (! preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetorenameafter))     // If not a tmp file
835
			{
836
				$rel_filetorenamebefore = preg_replace('/^[\\/]/', '', $rel_filetorenamebefore);
837
				$rel_filetorenameafter = preg_replace('/^[\\/]/', '', $rel_filetorenameafter);
838
				//var_dump($rel_filetorenamebefore.' - '.$rel_filetorenameafter);
839
840
				dol_syslog("Try to rename also entries in database for full relative path before = ".$rel_filetorenamebefore." after = ".$rel_filetorenameafter, LOG_DEBUG);
841
				include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
842
843
				$ecmfiletarget=new EcmFiles($db);
844
				$resultecmtarget = $ecmfiletarget->fetch(0, '', $rel_filetorenameafter);
845
				if ($resultecmtarget > 0)   // An entry for target name already exists for target, we delete it, a new one will be created.
846
				{
847
					$ecmfiletarget->delete($user);
848
				}
849
850
				$ecmfile=new EcmFiles($db);
851
				$resultecm = $ecmfile->fetch(0, '', $rel_filetorenamebefore);
852
				if ($resultecm > 0)   // If an entry was found for src file, we use it to move entry
853
				{
854
					$filename = basename($rel_filetorenameafter);
855
					$rel_dir = dirname($rel_filetorenameafter);
856
					$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
857
					$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
858
859
					$ecmfile->filepath = $rel_dir;
860
					$ecmfile->filename = $filename;
861
					$resultecm = $ecmfile->update($user);
862
				}
863
				elseif ($resultecm == 0)   // If no entry were found for src files, create/update target file
864
				{
865
					$filename = basename($rel_filetorenameafter);
866
					$rel_dir = dirname($rel_filetorenameafter);
867
					$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
868
					$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
869
870
					$ecmfile->filepath = $rel_dir;
871
					$ecmfile->filename = $filename;
872
					$ecmfile->label = md5_file(dol_osencode($destfile));        // $destfile is a full path to file
873
					$ecmfile->fullpath_orig = $srcfile;
874
					$ecmfile->gen_or_uploaded = 'unknown';
875
					$ecmfile->description = '';    // indexed content
876
					$ecmfile->keyword = '';        // keyword content
877
					$resultecm = $ecmfile->create($user);
878
					if ($resultecm < 0)
879
					{
880
						setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
881
					}
882
				}
883
				elseif ($resultecm < 0)
884
				{
885
					setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
886
				}
887
888
				if ($resultecm > 0) $result=true;
889
				else $result = false;
890
			}
891
		}
892
893
		if (empty($newmask)) $newmask=empty($conf->global->MAIN_UMASK)?'0755':$conf->global->MAIN_UMASK;
894
		$newmaskdec=octdec($newmask);
895
		// Currently method is restricted to files (dol_delete_files previously used is for files, and mask usage if for files too)
896
		// to allow mask usage for dir, we shoul introduce a new param "isdir" to 1 to complete newmask like this
897
		// if ($isdir) $newmaskdec |= octdec('0111');  // Set x bit required for directories
898
		@chmod($newpathofdestfile, $newmaskdec);
899
	}
900
901
	return $result;
902
}
903
904
/**
905
 *	Unescape a file submitted by upload.
906
 *  PHP escape char " (%22) or char ' (%27) into $FILES.
907
 *
908
 *	@param	string	$filename		Filename
909
 *	@return	string					Filename sanitized
910
 */
911
function dol_unescapefile($filename)
912
{
913
	// Remove path information and dots around the filename, to prevent uploading
914
	// into different directories or replacing hidden system files.
915
	// Also remove control characters and spaces (\x00..\x20) around the filename:
916
	return trim(basename($filename), ".\x00..\x20");
917
}
918
919
920
/**
921
 * Check virus into a file
922
 *
923
 * @param   string      $src_file       Source file to check
924
 * @return  array                       Array of errors or empty array if not virus found
925
 */
926
function dolCheckVirus($src_file)
927
{
928
	global $conf;
929
930
	if (! empty($conf->global->MAIN_ANTIVIRUS_COMMAND))
931
	{
932
		if (! class_exists('AntiVir')) {
933
			require_once DOL_DOCUMENT_ROOT.'/core/class/antivir.class.php';
934
		}
935
		$antivir=new AntiVir($db);
0 ignored issues
show
Bug introduced by
The variable $db does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
936
		$result = $antivir->dol_avscan_file($src_file);
937
		if ($result < 0)	// If virus or error, we stop here
938
		{
939
			$reterrors=$antivir->errors;
940
			return $reterrors;
941
		}
942
	}
943
	return array();
944
}
945
946
947
/**
948
 *	Make control on an uploaded file from an GUI page and move it to final destination.
949
 * 	If there is errors (virus found, antivir in error, bad filename), file is not moved.
950
 *  Note:
951
 *  - This function can be used only into a HTML page context. Use dol_move if you are outside.
952
 *  - Test on antivirus is always done (if antivirus set).
953
 *  - Database of files is NOT updated (this is done by dol_add_file_process() that calls this function).
954
 *
955
 *	@param	string	$src_file			Source full path filename ($_FILES['field']['tmp_name'])
956
 *	@param	string	$dest_file			Target full path filename  ($_FILES['field']['name'])
957
 * 	@param	int		$allowoverwrite		1=Overwrite target file if it already exists
958
 * 	@param	int		$disablevirusscan	1=Disable virus scan
959
 * 	@param	integer	$uploaderrorcode	Value of PHP upload error code ($_FILES['field']['error'])
960
 * 	@param	int		$nohook				Disable all hooks
961
 * 	@param	string	$varfiles			_FILES var name
962
 *	@return int       			  		>0 if OK, <0 or string if KO
963
 *  @see    dol_move
964
 */
965
function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite, $disablevirusscan=0, $uploaderrorcode=0, $nohook=0, $varfiles='addedfile')
966
{
967
	global $conf, $db, $user, $langs;
968
	global $object, $hookmanager;
969
970
	$reshook=0;
971
	$file_name = $dest_file;
972
973
	if (empty($nohook))
974
	{
975
		$reshook=$hookmanager->initHooks(array('fileslib'));
976
977
		$parameters=array('dest_file' => $dest_file, 'src_file' => $src_file, 'file_name' => $file_name, 'varfiles' => $varfiles, 'allowoverwrite' => $allowoverwrite);
978
		$reshook=$hookmanager->executeHooks('moveUploadedFile', $parameters, $object);
979
	}
980
981
	if (empty($reshook))
982
	{
983
		// If an upload error has been reported
984
		if ($uploaderrorcode)
985
		{
986
			switch($uploaderrorcode)
987
			{
988
				case UPLOAD_ERR_INI_SIZE:	// 1
989
					return 'ErrorFileSizeTooLarge';
990
					break;
991
				case UPLOAD_ERR_FORM_SIZE:	// 2
992
					return 'ErrorFileSizeTooLarge';
993
					break;
994
				case UPLOAD_ERR_PARTIAL:	// 3
995
					return 'ErrorPartialFile';
996
					break;
997
				case UPLOAD_ERR_NO_TMP_DIR:	//
998
					return 'ErrorNoTmpDir';
999
					break;
1000
				case UPLOAD_ERR_CANT_WRITE:
1001
					return 'ErrorFailedToWriteInDir';
1002
					break;
1003
				case UPLOAD_ERR_EXTENSION:
1004
					return 'ErrorUploadBlockedByAddon';
1005
					break;
1006
				default:
1007
					break;
1008
			}
1009
		}
1010
1011
		// If we need to make a virus scan
1012
		if (empty($disablevirusscan) && file_exists($src_file))
1013
		{
1014
			$checkvirusarray=dolCheckVirus($src_file);
1015
			if (count($checkvirusarray))
1016
			{
1017
			   dol_syslog('Files.lib::dol_move_uploaded_file File "'.$src_file.'" (target name "'.$dest_file.'") KO with antivirus: result='.$result.' errors='.join(',',$checkvirusarray), LOG_WARNING);
0 ignored issues
show
Bug introduced by
The variable $result does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1018
			   return 'ErrorFileIsInfectedWithAVirus: '.join(',',$checkvirusarray);
1019
			}
1020
		}
1021
1022
		// Security:
1023
		// Disallow file with some extensions. We rename them.
1024
		// Because if we put the documents directory into a directory inside web root (very bad), this allows to execute on demand arbitrary code.
1025
		if (preg_match('/\.htm|\.html|\.php|\.pl|\.cgi$/i',$dest_file) && empty($conf->global->MAIN_DOCUMENT_IS_OUTSIDE_WEBROOT_SO_NOEXE_NOT_REQUIRED))
1026
		{
1027
			$file_name.= '.noexe';
1028
		}
1029
1030
		// Security:
1031
		// We refuse cache files/dirs, upload using .. and pipes into filenames.
1032
		if (preg_match('/^\./',$src_file) || preg_match('/\.\./',$src_file) || preg_match('/[<>|]/',$src_file))
1033
		{
1034
			dol_syslog("Refused to deliver file ".$src_file, LOG_WARNING);
1035
			return -1;
1036
		}
1037
1038
		// Security:
1039
		// On interdit fichiers caches, remontees de repertoire ainsi que les pipe dans les noms de fichiers.
1040
		if (preg_match('/^\./',$dest_file) || preg_match('/\.\./',$dest_file) || preg_match('/[<>|]/',$dest_file))
1041
		{
1042
			dol_syslog("Refused to deliver file ".$dest_file, LOG_WARNING);
1043
			return -2;
1044
		}
1045
	}
1046
1047
	if ($reshook < 0)	// At least one blocking error returned by one hook
1048
	{
1049
		$errmsg = join(',', $hookmanager->errors);
1050
		if (empty($errmsg)) $errmsg = 'ErrorReturnedBySomeHooks';	// Should not occurs. Added if hook is bugged and does not set ->errors when there is error.
1051
		return $errmsg;
1052
	}
1053
	elseif (empty($reshook))
1054
	{
1055
		// The file functions must be in OS filesystem encoding.
1056
		$src_file_osencoded=dol_osencode($src_file);
1057
		$file_name_osencoded=dol_osencode($file_name);
1058
1059
		// Check if destination dir is writable
1060
		if (! is_writable(dirname($file_name_osencoded)))
1061
		{
1062
			dol_syslog("Files.lib::dol_move_uploaded_file Dir ".dirname($file_name_osencoded)." is not writable. Return 'ErrorDirNotWritable'", LOG_WARNING);
1063
			return 'ErrorDirNotWritable';
1064
		}
1065
1066
		// Check if destination file already exists
1067
		if (! $allowoverwrite)
1068
		{
1069
			if (file_exists($file_name_osencoded))
1070
			{
1071
				dol_syslog("Files.lib::dol_move_uploaded_file File ".$file_name." already exists. Return 'ErrorFileAlreadyExists'", LOG_WARNING);
1072
				return 'ErrorFileAlreadyExists';
1073
			}
1074
		}
1075
1076
		// Move file
1077
		$return=move_uploaded_file($src_file_osencoded, $file_name_osencoded);
1078
		if ($return)
1079
		{
1080
			if (! empty($conf->global->MAIN_UMASK)) @chmod($file_name_osencoded, octdec($conf->global->MAIN_UMASK));
1081
			dol_syslog("Files.lib::dol_move_uploaded_file Success to move ".$src_file." to ".$file_name." - Umask=".$conf->global->MAIN_UMASK, LOG_DEBUG);
1082
			return 1;	// Success
1083
		}
1084
		else
1085
		{
1086
			dol_syslog("Files.lib::dol_move_uploaded_file Failed to move ".$src_file." to ".$file_name, LOG_ERR);
1087
			return -3;	// Unknown error
1088
		}
1089
	}
1090
1091
	return 1;	// Success
1092
}
1093
1094
/**
1095
 *  Remove a file or several files with a mask.
1096
 *  This delete file physically but also database indexes.
1097
 *
1098
 *  @param	string	$file           File to delete or mask of files to delete
1099
 *  @param  int		$disableglob    Disable usage of glob like * so function is an exact delete function that will return error if no file found
1100
 *  @param  int		$nophperrors    Disable all PHP output errors
1101
 *  @param	int		$nohook			Disable all hooks
1102
 *  @param	object	$object			Current object in use
1103
 *  @return boolean         		True if no error (file is deleted or if glob is used and there's nothing to delete), False if error
1104
 *  @see dol_delete_dir
1105
 */
1106
function dol_delete_file($file,$disableglob=0,$nophperrors=0,$nohook=0,$object=null)
0 ignored issues
show
Coding Style introduced by
dol_delete_file uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1107
{
1108
	global $db, $conf, $user, $langs;
1109
	global $hookmanager;
1110
1111
	$langs->load("other");
1112
	$langs->load("errors");
1113
1114
	dol_syslog("dol_delete_file file=".$file." disableglob=".$disableglob." nophperrors=".$nophperrors." nohook=".$nohook);
1115
1116
	// Security:
1117
	// We refuse transversal using .. and pipes into filenames.
1118
	if (preg_match('/\.\./',$file) || preg_match('/[<>|]/',$file))
1119
	{
1120
		dol_syslog("Refused to delete file ".$file, LOG_WARNING);
1121
		return False;
1122
	}
1123
1124
	if (empty($nohook))
1125
	{
1126
		$hookmanager->initHooks(array('fileslib'));
1127
1128
		$parameters=array(
1129
				'GET' => $_GET,
1130
				'file' => $file,
1131
				'disableglob'=> $disableglob,
1132
				'nophperrors' => $nophperrors
1133
		);
1134
		$reshook=$hookmanager->executeHooks('deleteFile', $parameters, $object);
1135
	}
1136
1137
	if (empty($nohook) && $reshook != 0) // reshook = 0 to do standard actions, 1 = ok, -1 = ko
1138
	{
1139
		if ($reshook < 0) return false;
0 ignored issues
show
Bug introduced by
The variable $reshook does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1140
		return true;
1141
	}
1142
	else
1143
	{
1144
		$error=0;
1145
1146
		//print "x".$file." ".$disableglob;exit;
1147
		$file_osencoded=dol_osencode($file);    // New filename encoded in OS filesystem encoding charset
1148
		if (empty($disableglob) && ! empty($file_osencoded))
1149
		{
1150
			$ok=true;
1151
			$globencoded=str_replace('[','\[',$file_osencoded);
1152
			$globencoded=str_replace(']','\]',$globencoded);
1153
			$listofdir=glob($globencoded);
1154
			if (! empty($listofdir) && is_array($listofdir))
1155
			{
1156
				foreach ($listofdir as $filename)
1157
				{
1158
					if ($nophperrors) $ok=@unlink($filename);
1159
					else $ok=unlink($filename);
1160
					if ($ok)
1161
					{
1162
						dol_syslog("Removed file ".$filename, LOG_DEBUG);
1163
1164
						// Delete entry into ecm database
1165
						$rel_filetodelete = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $filename);
1166
						if (! preg_match('/(\/temp\/|\/thumbs\/|\.meta$)/', $rel_filetodelete))     // If not a tmp file
1167
						{
1168
							$rel_filetodelete = preg_replace('/^[\\/]/', '', $rel_filetodelete);
1169
1170
							dol_syslog("Try to remove also entries in database for full relative path = ".$rel_filetodelete, LOG_DEBUG);
1171
							include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1172
							$ecmfile=new EcmFiles($db);
1173
							$result = $ecmfile->fetch(0, '', $rel_filetodelete);
1174
							if ($result >= 0 && $ecmfile->id > 0)
1175
							{
1176
								$result = $ecmfile->delete($user);
1177
							}
1178
							if ($result < 0)
1179
							{
1180
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1181
							}
1182
						}
1183
					}
1184
					else dol_syslog("Failed to remove file ".$filename, LOG_WARNING);
1185
					// TODO Failure to remove can be because file was already removed or because of permission
1186
					// If error because of not exists, we must should return true and we should return false if this is a permission problem
1187
				}
1188
			}
1189
			else dol_syslog("No files to delete found", LOG_DEBUG);
1190
		}
1191
		else
1192
		{
1193
			$ok=false;
1194
			if ($nophperrors) $ok=@unlink($file_osencoded);
1195
			else $ok=unlink($file_osencoded);
1196
			if ($ok) dol_syslog("Removed file ".$file_osencoded, LOG_DEBUG);
1197
			else dol_syslog("Failed to remove file ".$file_osencoded, LOG_WARNING);
1198
		}
1199
1200
		return $ok;
1201
	}
1202
}
1203
1204
/**
1205
 *  Remove a directory (not recursive, so content must be empty).
1206
 *  If directory is not empty, return false
1207
 *
1208
 *  @param	string	$dir            Directory to delete
1209
 *  @param  int		$nophperrors    Disable all PHP output errors
1210
 *  @return boolean         		True if success, false if error
1211
 *  @see dol_delete_file dol_copy
1212
 */
1213
function dol_delete_dir($dir,$nophperrors=0)
1214
{
1215
	// Security:
1216
	// We refuse transversal using .. and pipes into filenames.
1217
	if (preg_match('/\.\./',$dir) || preg_match('/[<>|]/',$dir))
1218
	{
1219
		dol_syslog("Refused to delete dir ".$dir, LOG_WARNING);
1220
		return False;
1221
	}
1222
1223
	$dir_osencoded=dol_osencode($dir);
1224
	return ($nophperrors?@rmdir($dir_osencoded):rmdir($dir_osencoded));
1225
}
1226
1227
/**
1228
 *  Remove a directory $dir and its subdirectories (or only files and subdirectories)
1229
 *
1230
 *  @param	string	$dir            Dir to delete
1231
 *  @param  int		$count          Counter to count nb of elements found to delete
1232
 *  @param  int		$nophperrors    Disable all PHP output errors
1233
 *  @param	int		$onlysub		Delete only files and subdir, not main directory
1234
 *  @param  int		$countdeleted   Counter to count nb of elements found really deleted
1235
 *  @return int             		Number of files and directory we try to remove. NB really removed is returned into $countdeleted.
1236
 */
1237
function dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0)
1238
{
1239
	dol_syslog("functions.lib:dol_delete_dir_recursive ".$dir,LOG_DEBUG);
1240
	if (dol_is_dir($dir))
1241
	{
1242
		$dir_osencoded=dol_osencode($dir);
1243
		if ($handle = opendir("$dir_osencoded"))
1244
		{
1245
			while (false !== ($item = readdir($handle)))
1246
			{
1247
				if (! utf8_check($item)) $item=utf8_encode($item);  // should be useless
1248
1249
				if ($item != "." && $item != "..")
1250
				{
1251
					if (is_dir(dol_osencode("$dir/$item")) && ! is_link(dol_osencode("$dir/$item")))
1252
					{
1253
						$count=dol_delete_dir_recursive("$dir/$item", $count, $nophperrors, 0, $countdeleted);
1254
					}
1255
					else
1256
					{
1257
						$result=dol_delete_file("$dir/$item", 1, $nophperrors);
1258
						$count++;
1259
						if ($result) $countdeleted++;
1260
					}
1261
				}
1262
			}
1263
			closedir($handle);
1264
1265
			if (empty($onlysub))
1266
			{
1267
				$result=dol_delete_dir($dir, $nophperrors);
1268
				$count++;
1269
				if ($result) $countdeleted++;
1270
			}
1271
		}
1272
	}
1273
1274
	return $count;
1275
}
1276
1277
1278
/**
1279
 *  Delete all preview files linked to object instance.
1280
 *  Note that preview image of PDF files is generated when required, by dol_banner_tab() for example.
1281
 *
1282
 *  @param	object	$object		Object to clean
1283
 *  @return	int					0 if error, 1 if OK
1284
 *  @see dol_convert_file
1285
 */
1286
function dol_delete_preview($object)
1287
{
1288
	global $langs,$conf;
1289
1290
	// Define parent dir of elements
1291
	$element = $object->element;
1292
1293
	if ($object->element == 'order_supplier')		$dir = $conf->fournisseur->commande->dir_output;
1294
	elseif ($object->element == 'invoice_supplier')	$dir = $conf->fournisseur->facture->dir_output;
1295
	elseif ($object->element == 'project')			$dir = $conf->projet->dir_output;
1296
	elseif ($object->element == 'shipping')			$dir = $conf->expedition->dir_output.'/sending';
1297
	elseif ($object->element == 'delivery')			$dir = $conf->expedition->dir_output.'/receipt';
1298
	elseif ($object->element == 'fichinter')		$dir = $conf->ficheinter->dir_output;
1299
	else $dir=empty($conf->$element->dir_output)?'':$conf->$element->dir_output;
1300
1301
	if (empty($dir)) return 'ErrorObjectNoSupportedByFunction';
1302
1303
	$refsan = dol_sanitizeFileName($object->ref);
1304
	$dir = $dir . "/" . $refsan ;
1305
	$filepreviewnew = $dir . "/" . $refsan . ".pdf_preview.png";
1306
	$filepreviewnewbis = $dir . "/" . $refsan . ".pdf_preview-0.png";
1307
	$filepreviewold = $dir . "/" . $refsan . ".pdf.png";
1308
1309
	// For new preview files
1310
	if (file_exists($filepreviewnew) && is_writable($filepreviewnew))
1311
	{
1312
		if (! dol_delete_file($filepreviewnew,1))
1313
		{
1314
			$object->error=$langs->trans("ErrorFailedToDeleteFile",$filepreviewnew);
1315
			return 0;
1316
		}
1317
	}
1318
	if (file_exists($filepreviewnewbis) && is_writable($filepreviewnewbis))
1319
	{
1320
		if (! dol_delete_file($filepreviewnewbis,1))
1321
		{
1322
			$object->error=$langs->trans("ErrorFailedToDeleteFile",$filepreviewnewbis);
1323
			return 0;
1324
		}
1325
	}
1326
	// For old preview files
1327
	if (file_exists($filepreviewold) && is_writable($filepreviewold))
1328
	{
1329
		if (! dol_delete_file($filepreviewold,1))
1330
		{
1331
			$object->error=$langs->trans("ErrorFailedToDeleteFile",$filepreviewold);
1332
			return 0;
1333
		}
1334
	}
1335
	else
1336
	{
1337
		$multiple = $filepreviewold . ".";
1338
		for ($i = 0; $i < 20; $i++)
1339
		{
1340
			$preview = $multiple.$i;
1341
1342
			if (file_exists($preview) && is_writable($preview))
1343
			{
1344
				if ( ! dol_delete_file($preview,1) )
1345
				{
1346
					$object->error=$langs->trans("ErrorFailedToOpenFile",$preview);
1347
					return 0;
1348
				}
1349
			}
1350
		}
1351
	}
1352
1353
	return 1;
1354
}
1355
1356
/**
1357
 *	Create a meta file with document file into same directory.
1358
 *	This make "grep" search possible.
1359
 *  This feature to generate the meta file is enabled only if option MAIN_DOC_CREATE_METAFILE is set.
1360
 *
1361
 *	@param	CommonObject	$object		Object
1362
 *	@return	int							0 if do nothing, >0 if we update meta file too, <0 if KO
1363
 */
1364
function dol_meta_create($object)
1365
{
1366
	global $conf;
1367
1368
	// Create meta file
1369
	if (empty($conf->global->MAIN_DOC_CREATE_METAFILE)) return 0;	// By default, no metafile.
1370
1371
	// Define parent dir of elements
1372
	$element=$object->element;
1373
1374
	if ($object->element == 'order_supplier')		$dir = $conf->fournisseur->dir_output.'/commande';
1375
	elseif ($object->element == 'invoice_supplier')	$dir = $conf->fournisseur->dir_output.'/facture';
1376
	elseif ($object->element == 'project')			$dir = $conf->projet->dir_output;
1377
	elseif ($object->element == 'shipping')			$dir = $conf->expedition->dir_output.'/sending';
1378
	elseif ($object->element == 'delivery')			$dir = $conf->expedition->dir_output.'/receipt';
1379
	elseif ($object->element == 'fichinter')		$dir = $conf->ficheinter->dir_output;
1380
	else $dir=empty($conf->$element->dir_output)?'':$conf->$element->dir_output;
1381
1382
	if ($dir)
1383
	{
1384
		$object->fetch_thirdparty();
1385
1386
		$objectref = dol_sanitizeFileName($object->ref);
1387
		$dir = $dir . "/" . $objectref;
1388
		$file = $dir . "/" . $objectref . ".meta";
1389
1390
		if (! is_dir($dir))
1391
		{
1392
			dol_mkdir($dir);
1393
		}
1394
1395
		if (is_dir($dir))
1396
		{
1397
			$nblignes = count($object->lines);
1398
			$client = $object->thirdparty->name . " " . $object->thirdparty->address . " " . $object->thirdparty->zip . " " . $object->thirdparty->town;
1399
			$meta = "REFERENCE=\"" . $object->ref . "\"
1400
			DATE=\"" . dol_print_date($object->date,'') . "\"
1401
			NB_ITEMS=\"" . $nblignes . "\"
1402
			CLIENT=\"" . $client . "\"
1403
			AMOUNT_EXCL_TAX=\"" . $object->total_ht . "\"
1404
			AMOUNT=\"" . $object->total_ttc . "\"\n";
1405
1406
			for ($i = 0 ; $i < $nblignes ; $i++)
1407
			{
1408
				//Pour les articles
1409
				$meta .= "ITEM_" . $i . "_QUANTITY=\"" . $object->lines[$i]->qty . "\"
1410
				ITEM_" . $i . "_AMOUNT_WO_TAX=\"" . $object->lines[$i]->total_ht . "\"
1411
				ITEM_" . $i . "_VAT=\"" .$object->lines[$i]->tva_tx . "\"
1412
				ITEM_" . $i . "_DESCRIPTION=\"" . str_replace("\r\n","",nl2br($object->lines[$i]->desc)) . "\"
1413
				";
1414
			}
1415
		}
1416
1417
		$fp = fopen($file,"w");
1418
		fputs($fp,$meta);
0 ignored issues
show
Bug introduced by
The variable $meta does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1419
		fclose($fp);
1420
		if (! empty($conf->global->MAIN_UMASK))
1421
		@chmod($file, octdec($conf->global->MAIN_UMASK));
1422
1423
		return 1;
1424
	}
1425
	else
1426
	{
1427
		dol_syslog('FailedToDetectDirInDolMetaCreateFor'.$object->element, LOG_WARNING);
1428
	}
1429
1430
	return 0;
1431
}
1432
1433
1434
1435
/**
1436
 * Scan a directory and init $_SESSION to manage uploaded files with list of all found files.
1437
 * Note: Only email module seems to use this. Other feature initialize the $_SESSION doing $formmail->clear_attached_files(); $formmail->add_attached_files()
1438
 *
1439
 * @param	string	$pathtoscan				Path to scan
1440
 * @param   string  $trackid                Track id (used to prefix name of session vars to avoid conflict)
1441
 * @return	void
1442
 */
1443
function dol_init_file_process($pathtoscan='', $trackid='')
1444
{
1445
	$listofpaths=array();
1446
	$listofnames=array();
1447
	$listofmimes=array();
1448
1449
	if ($pathtoscan)
1450
	{
1451
		$listoffiles=dol_dir_list($pathtoscan,'files');
1452
		foreach($listoffiles as $key => $val)
1453
		{
1454
			$listofpaths[]=$val['fullname'];
1455
			$listofnames[]=$val['name'];
1456
			$listofmimes[]=dol_mimetype($val['name']);
1457
		}
1458
	}
1459
	$keytoavoidconflict = empty($trackid)?'':'-'.$trackid;
1460
	$_SESSION["listofpaths".$keytoavoidconflict]=join(';',$listofpaths);
1461
	$_SESSION["listofnames".$keytoavoidconflict]=join(';',$listofnames);
1462
	$_SESSION["listofmimes".$keytoavoidconflict]=join(';',$listofmimes);
1463
}
1464
1465
1466
/**
1467
 * Get and save an upload file (for example after submitting a new file a mail form). Database index of file is also updated if donotupdatesession is set.
1468
 * All information used are in db, conf, langs, user and _FILES.
1469
 * Note: This function can be used only into a HTML page context.
1470
 *
1471
 * @param	string	$upload_dir				Directory where to store uploaded file (note: used to forge $destpath = $upload_dir + filename)
1472
 * @param	int		$allowoverwrite			1=Allow overwrite existing file
1473
 * @param	int		$donotupdatesession		1=Do no edit _SESSION variable but update database index. 0=Update _SESSION and not database index.
1474
 * @param	string	$varfiles				_FILES var name
1475
 * @param	string	$savingdocmask			Mask to use to define output filename. For example 'XXXXX-__YYYYMMDD__-__file__'
1476
 * @param	string	$link					Link to add (to add a link instead of a file)
1477
 * @param   string  $trackid                Track id (used to prefix name of session vars to avoid conflict)
1478
 * @param	int		$generatethumbs			1=Generate also thumbs for uploaded image files
1479
 * @return	int                             <=0 if KO, >0 if OK
1480
 */
1481
function dol_add_file_process($upload_dir, $allowoverwrite=0, $donotupdatesession=0, $varfiles='addedfile', $savingdocmask='', $link=null, $trackid='', $generatethumbs=1)
0 ignored issues
show
Coding Style introduced by
dol_add_file_process uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1482
{
1483
	global $db,$user,$conf,$langs;
1484
1485
	$res = 0;
1486
1487
	if (! empty($_FILES[$varfiles])) // For view $_FILES[$varfiles]['error']
1488
	{
1489
		dol_syslog('dol_add_file_process upload_dir='.$upload_dir.' allowoverwrite='.$allowoverwrite.' donotupdatesession='.$donotupdatesession.' savingdocmask='.$savingdocmask, LOG_DEBUG);
1490
		if (dol_mkdir($upload_dir) >= 0)
1491
		{
1492
			$TFile = $_FILES[$varfiles];
1493
			if (!is_array($TFile['name']))
1494
			{
1495
				foreach ($TFile as $key => &$val)
1496
				{
1497
					$val = array($val);
1498
				}
1499
			}
1500
1501
			$nbfile = count($TFile['name']);
1502
			$nbok = 0;
1503
			for ($i = 0; $i < $nbfile; $i++)
1504
			{
1505
				// Define $destfull (path to file including filename) and $destfile (only filename)
1506
				$destfull=$upload_dir . "/" . $TFile['name'][$i];
1507
				$destfile=$TFile['name'][$i];
1508
1509
				if ($savingdocmask)
1510
				{
1511
					$destfull=$upload_dir . "/" . preg_replace('/__file__/',$TFile['name'][$i],$savingdocmask);
1512
					$destfile=preg_replace('/__file__/',$TFile['name'][$i],$savingdocmask);
1513
				}
1514
1515
				// dol_sanitizeFileName the file name and lowercase extension
1516
				$info = pathinfo($destfull);
1517
				$destfull = $info['dirname'].'/'.dol_sanitizeFileName($info['filename'].'.'.strtolower($info['extension']));
1518
				$info = pathinfo($destfile);
1519
				$destfile = dol_sanitizeFileName($info['filename'].'.'.strtolower($info['extension']));
1520
1521
				$resupload = dol_move_uploaded_file($TFile['tmp_name'][$i], $destfull, $allowoverwrite, 0, $TFile['error'][$i], 0, $varfiles);
1522
1523
				if (is_numeric($resupload) && $resupload > 0)   // $resupload can be 'ErrorFileAlreadyExists'
1524
				{
1525
					global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini;
1526
1527
					include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
1528
1529
					// Generate thumbs.
1530
					if ($generatethumbs)
1531
					{
1532
						if (image_format_supported($destfull) == 1)
1533
						{
1534
							// Create thumbs
1535
							// We can't use $object->addThumbs here because there is no $object known
1536
1537
							// Used on logon for example
1538
							$imgThumbSmall = vignette($destfull, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
1539
							// Create mini thumbs for image (Ratio is near 16/9)
1540
							// Used on menu or for setup page for example
1541
							$imgThumbMini = vignette($destfull, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
1542
						}
1543
					}
1544
1545
					// Update session
1546
					if (empty($donotupdatesession))
1547
					{
1548
						include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
1549
						$formmail = new FormMail($db);
1550
						$formmail->trackid = $trackid;
1551
						$formmail->add_attached_files($destfull, $destfile, $TFile['type'][$i]);
1552
					}
1553
1554
					// Update table of files
1555
					if ($donotupdatesession)
1556
					{
1557
						$result = addFileIntoDatabaseIndex($upload_dir, basename($destfile), $TFile['name'][$i], 'uploaded', 0);
1558
						if ($result < 0)
1559
						{
1560
							setEventMessages('FailedToAddFileIntoDatabaseIndex', '', 'warnings');
1561
						}
1562
					}
1563
1564
					$nbok++;
1565
				}
1566
				else
1567
				{
1568
					$langs->load("errors");
1569
					if ($resupload < 0)	// Unknown error
1570
					{
1571
						setEventMessages($langs->trans("ErrorFileNotUploaded"), null, 'errors');
1572
					}
1573
					else if (preg_match('/ErrorFileIsInfectedWithAVirus/',$resupload))	// Files infected by a virus
1574
					{
1575
						setEventMessages($langs->trans("ErrorFileIsInfectedWithAVirus"), null, 'errors');
1576
					}
1577
					else	// Known error
1578
					{
1579
						setEventMessages($langs->trans($resupload), null, 'errors');
1580
					}
1581
				}
1582
			}
1583
			if ($nbok > 0)
1584
			{
1585
				$res = 1;
1586
				setEventMessages($langs->trans("FileTransferComplete"), null, 'mesgs');
1587
			}
1588
		}
1589
	} elseif ($link) {
1590
		require_once DOL_DOCUMENT_ROOT . '/core/class/link.class.php';
1591
		$linkObject = new Link($db);
1592
		$linkObject->entity = $conf->entity;
1593
		$linkObject->url = $link;
1594
		$linkObject->objecttype = GETPOST('objecttype', 'alpha');
1595
		$linkObject->objectid = GETPOST('objectid', 'int');
1596
		$linkObject->label = GETPOST('label', 'alpha');
1597
		$res = $linkObject->create($user);
1598
		$langs->load('link');
1599
		if ($res > 0) {
1600
			setEventMessages($langs->trans("LinkComplete"), null, 'mesgs');
1601
		} else {
1602
			setEventMessages($langs->trans("ErrorFileNotLinked"), null, 'errors');
1603
		}
1604
	}
1605
	else
1606
	{
1607
		$langs->load("errors");
1608
		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("File")), null, 'errors');
1609
	}
1610
1611
	return $res;
1612
}
1613
1614
1615
/**
1616
 * Remove an uploaded file (for example after submitting a new file a mail form).
1617
 * All information used are in db, conf, langs, user and _FILES.
1618
 *
1619
 * @param	int		$filenb					File nb to delete
1620
 * @param	int		$donotupdatesession		1=Do not edit _SESSION variable
1621
 * @param   int		$donotdeletefile        1=Do not delete physically file
1622
 * @param   string  $trackid                Track id (used to prefix name of session vars to avoid conflict)
1623
 * @return	void
1624
 */
1625
function dol_remove_file_process($filenb,$donotupdatesession=0,$donotdeletefile=1,$trackid='')
0 ignored issues
show
Coding Style introduced by
dol_remove_file_process uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1626
{
1627
	global $db,$user,$conf,$langs,$_FILES;
1628
1629
	$keytodelete=$filenb;
1630
	$keytodelete--;
1631
1632
	$listofpaths=array();
1633
	$listofnames=array();
1634
	$listofmimes=array();
1635
	$keytoavoidconflict = empty($trackid)?'':'-'.$trackid;
1636
	if (! empty($_SESSION["listofpaths".$keytoavoidconflict])) $listofpaths=explode(';',$_SESSION["listofpaths".$keytoavoidconflict]);
1637
	if (! empty($_SESSION["listofnames".$keytoavoidconflict])) $listofnames=explode(';',$_SESSION["listofnames".$keytoavoidconflict]);
1638
	if (! empty($_SESSION["listofmimes".$keytoavoidconflict])) $listofmimes=explode(';',$_SESSION["listofmimes".$keytoavoidconflict]);
1639
1640
	if ($keytodelete >= 0)
1641
	{
1642
		$pathtodelete=$listofpaths[$keytodelete];
1643
		$filetodelete=$listofnames[$keytodelete];
1644
		if (empty($donotdeletefile)) $result = dol_delete_file($pathtodelete,1);  // The delete of ecm database is inside the function dol_delete_file
1645
		else $result=0;
1646
		if ($result >= 0)
1647
		{
1648
			if (empty($donotdeletefile))
1649
			{
1650
				$langs->load("other");
1651
				setEventMessages($langs->trans("FileWasRemoved",$filetodelete), null, 'mesgs');
1652
			}
1653
			if (empty($donotupdatesession))
1654
			{
1655
				include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
1656
				$formmail = new FormMail($db);
1657
				$formmail->trackid = $trackid;
1658
				$formmail->remove_attached_files($keytodelete);
1659
			}
1660
		}
1661
	}
1662
}
1663
1664
1665
/**
1666
 *  Add a file into database index.
1667
 *  Called by dol_add_file_process when uploading a file and on other cases.
1668
 *  See also commonGenerateDocument that also add/update database index when a file is generated.
1669
 *
1670
 *  @param      string	$dir			Directory name (full real path without ending /)
1671
 *  @param		string	$file			File name
1672
 *  @param		string	$fullpathorig	Full path of origin for file (can be '')
1673
 *  @param		string	$mode			How file was created ('uploaded', 'generated', ...)
1674
 *  @param		int		$setsharekey	Set also the share key
1675
 *	@return		int						<0 if KO, 0 if nothing done, >0 if OK
1676
 */
1677
function addFileIntoDatabaseIndex($dir, $file, $fullpathorig='', $mode='uploaded', $setsharekey=0)
1678
{
1679
	global $db, $user;
1680
1681
	$result = 0;
1682
1683
	$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir);
1684
1685
	if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
1686
	{
1687
		$filename = basename($file);
1688
		$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
1689
		$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
1690
1691
		include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1692
		$ecmfile=new EcmFiles($db);
1693
		$ecmfile->filepath = $rel_dir;
1694
		$ecmfile->filename = $filename;
1695
		$ecmfile->label = md5_file(dol_osencode($dir.'/'.$file));	// MD5 of file content
1696
		$ecmfile->fullpath_orig = $fullpathorig;
1697
		$ecmfile->gen_or_uploaded = $mode;
1698
		$ecmfile->description = '';    // indexed content
1699
		$ecmfile->keyword = '';        // keyword content
1700
		if ($setsharekey)
1701
		{
1702
			require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1703
			$ecmfile->share = getRandomPassword(true);
1704
		}
1705
1706
		$result = $ecmfile->create($user);
1707
		if ($result < 0)
1708
		{
1709
			dol_syslog($ecmfile->error);
1710
		}
1711
	}
1712
1713
	return $result;
1714
}
1715
1716
1717
/**
1718
 *  Delete files into database index using search criterias.
1719
 *
1720
 *  @param      string	$dir			Directory name (full real path without ending /)
1721
 *  @param		string	$file			File name
1722
 *  @param		string	$mode			How file was created ('uploaded', 'generated', ...)
1723
 *	@return		int						<0 if KO, 0 if nothing done, >0 if OK
1724
 */
1725
function deleteFilesIntoDatabaseIndex($dir, $file, $mode='uploaded')
1726
{
1727
	global $conf, $db, $user;
1728
1729
	$error = 0;
1730
1731
	if (empty($dir))
1732
	{
1733
		dol_syslog("deleteFilesIntoDatabaseIndex: dir parameter can't be empty", LOG_ERR);
1734
		return -1;
1735
	}
1736
1737
	$db->begin();
1738
1739
	$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir);
1740
1741
	$filename = basename($file);
1742
	$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
1743
	$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
1744
1745
	if (! $error)
1746
	{
1747
		$sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'ecm_files';
1748
		$sql.= ' WHERE entity = '.$conf->entity;
1749
		$sql.= " AND filepath = '" . $db->escape($rel_dir) . "'";
1750
		if ($file) $sql.= " AND filename = '" . $db->escape($file) . "'";
1751
		if ($mode) $sql.= " AND gen_or_uploaded = '" . $db->escape($mode) . "'";
1752
1753
		$resql = $db->query($sql);
1754
		if (!$resql)
1755
		{
1756
			$error++;
1757
			dol_syslog(__METHOD__ . ' ' . $db->lasterror(), LOG_ERR);
1758
		}
1759
	}
1760
1761
	// Commit or rollback
1762
	if ($error) {
1763
		$db->rollback();
1764
		return - 1 * $error;
1765
	} else {
1766
		$db->commit();
1767
		return 1;
1768
	}
1769
}
1770
1771
1772
/**
1773
 * 	Convert an image file into another format.
1774
 *  This need Imagick php extension.
1775
 *
1776
 *  @param	string	$fileinput  Input file name
1777
 *  @param  string	$ext        Format of target file (It is also extension added to file if fileoutput is not provided).
1778
 *  @param	string	$fileoutput	Output filename
1779
 *  @return	int					<0 if KO, 0=Nothing done, >0 if OK
1780
 */
1781
function dol_convert_file($fileinput, $ext='png', $fileoutput='')
1782
{
1783
	global $langs;
1784
1785
	if (class_exists('Imagick'))
1786
	{
1787
		$image=new Imagick();
1788
		try {
1789
			$ret = $image->readImage($fileinput);
1790
		} catch(Exception $e) {
1791
			dol_syslog("Failed to read image using Imagick. Try to install package 'apt-get install ghostscript'.", LOG_WARNING);
1792
			return 0;
1793
		}
1794
		if ($ret)
1795
		{
1796
			$ret = $image->setImageFormat($ext);
1797
			if ($ret)
1798
			{
1799
				if (empty($fileoutput)) $fileoutput=$fileinput.".".$ext;
1800
1801
				$count = $image->getNumberImages();
1802
1803
				if (! dol_is_file($fileoutput) || is_writeable($fileoutput))
1804
				{
1805
					$ret = $image->writeImages($fileoutput, true);
1806
				}
1807
				else
1808
				{
1809
					dol_syslog("Warning: Failed to write cache preview file '.$fileoutput.'. Check permission on file/dir", LOG_ERR);
1810
				}
1811
				if ($ret) return $count;
1812
				else return -3;
1813
			}
1814
			else
1815
			{
1816
				return -2;
1817
			}
1818
		}
1819
		else
1820
		{
1821
			return -1;
1822
		}
1823
	}
1824
	else
1825
	{
1826
		return 0;
1827
	}
1828
}
1829
1830
1831
/**
1832
 * Compress a file
1833
 *
1834
 * @param 	string	$inputfile		Source file name
1835
 * @param 	string	$outputfile		Target file name
1836
 * @param 	string	$mode			'gz' or 'bz' or 'zip'
1837
 * @return	int						<0 if KO, >0 if OK
1838
 */
1839
function dol_compress_file($inputfile, $outputfile, $mode="gz")
1840
{
1841
	$foundhandler=0;
1842
1843
	try
1844
	{
1845
		$data = implode("", file(dol_osencode($inputfile)));
1846
		if ($mode == 'gz')     { $foundhandler=1; $compressdata = gzencode($data, 9); }
1847
		elseif ($mode == 'bz') { $foundhandler=1; $compressdata = bzcompress($data, 9); }
1848
		elseif ($mode == 'zip')
1849
		{
1850
			if (defined('ODTPHP_PATHTOPCLZIP'))
1851
			{
1852
				$foundhandler=1;
1853
1854
				include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
1855
				$archive = new PclZip($outputfile);
1856
				$archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
1857
				//$archive->add($inputfile);
1858
				return 1;
1859
			}
1860
		}
1861
1862
		if ($foundhandler)
1863
		{
1864
			$fp = fopen($outputfile, "w");
1865
			fwrite($fp, $compressdata);
0 ignored issues
show
Bug introduced by
The variable $compressdata does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1866
			fclose($fp);
1867
			return 1;
1868
		}
1869
		else
1870
		{
1871
			dol_syslog("Try to zip with format ".$mode." with no handler for this format",LOG_ERR);
1872
			return -2;
1873
		}
1874
	}
1875
	catch (Exception $e)
1876
	{
1877
		global $langs, $errormsg;
1878
		$langs->load("errors");
1879
		dol_syslog("Failed to open file ".$outputfile,LOG_ERR);
1880
		$errormsg=$langs->trans("ErrorFailedToWriteInDir");
1881
		return -1;
1882
	}
1883
}
1884
1885
/**
1886
 * Uncompress a file
1887
 *
1888
 * @param 	string 	$inputfile		File to uncompress
1889
 * @param 	string	$outputdir		Target dir name
1890
 * @return 	array					array('error'=>'Error code') or array() if no error
1891
 */
1892
function dol_uncompress($inputfile,$outputdir)
1893
{
1894
	global $langs;
1895
1896
	if (defined('ODTPHP_PATHTOPCLZIP'))
1897
	{
1898
		dol_syslog("Constant ODTPHP_PATHTOPCLZIP for pclzip library is set to ".ODTPHP_PATHTOPCLZIP.", so we use Pclzip to unzip into ".$outputdir);
1899
		include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
1900
		$archive = new PclZip($inputfile);
1901
		$result=$archive->extract(PCLZIP_OPT_PATH, $outputdir);
1902
		//var_dump($result);
1903
		if (! is_array($result) && $result <= 0) return array('error'=>$archive->errorInfo(true));
1904
		else
1905
		{
1906
			$ok=1; $errmsg='';
1907
			// Loop on each file to check result for unzipping file
1908
			foreach($result as $key => $val)
1909
			{
1910
				if ($val['status'] == 'path_creation_fail')
1911
				{
1912
					$langs->load("errors");
1913
					$ok=0;
1914
					$errmsg=$langs->trans("ErrorFailToCreateDir", $val['filename']);
1915
					break;
1916
				}
1917
			}
1918
1919
			if ($ok) return array();
1920
			else return array('error'=>$errmsg);
1921
		}
1922
	}
1923
1924
	if (class_exists('ZipArchive'))
1925
	{
1926
		dol_syslog("Class ZipArchive is set so we unzip using ZipArchive to unzip into ".$outputdir);
1927
		$zip = new ZipArchive;
1928
		$res = $zip->open($inputfile);
1929
		if ($res === TRUE)
1930
		{
1931
			$zip->extractTo($outputdir.'/');
1932
			$zip->close();
1933
			return array();
1934
		}
1935
		else
1936
		{
1937
			return array('error'=>'ErrUnzipFails');
1938
		}
1939
	}
1940
1941
	return array('error'=>'ErrNoZipEngine');
1942
}
1943
1944
1945
/**
1946
 * Compress a directory and subdirectories into a package file.
1947
 *
1948
 * @param 	string	$inputdir		Source dir name
1949
 * @param 	string	$outputfile		Target file name (output directory must exists and be writable)
1950
 * @param 	string	$mode			'zip'
1951
 * @return	int						<0 if KO, >0 if OK
1952
 */
1953
function dol_compress_dir($inputdir, $outputfile, $mode="zip")
1954
{
1955
	$foundhandler=0;
1956
1957
	dol_syslog("Try to zip dir ".$inputdir." into ".$outputdir." mode=".$mode);
0 ignored issues
show
Bug introduced by
The variable $outputdir does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1958
1959
	if (! dol_is_dir(dirname($outputfile)) || ! is_writable(dirname($outputfile)))
1960
	{
1961
		global $langs, $errormsg;
1962
		$langs->load("errors");
1963
		$errormsg=$langs->trans("ErrorFailedToWriteInDir",$outputfile);
1964
		return -3;
1965
	}
1966
1967
	try
1968
	{
1969
		if ($mode == 'gz')     { $foundhandler=0; }
1970
		elseif ($mode == 'bz') { $foundhandler=0; }
1971
		elseif ($mode == 'zip')
1972
		{
1973
			/*if (defined('ODTPHP_PATHTOPCLZIP'))
1974
            {
1975
                $foundhandler=0;        // TODO implement this
1976
1977
                include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
1978
                $archive = new PclZip($outputfile);
1979
                $archive->add($inputfile, PCLZIP_OPT_REMOVE_PATH, dirname($inputfile));
1980
                //$archive->add($inputfile);
1981
                return 1;
1982
            }
1983
            else*/
1984
			if (class_exists('ZipArchive'))
1985
			{
1986
				$foundhandler=1;
1987
1988
				// Initialize archive object
1989
				$zip = new ZipArchive();
1990
				$result = $zip->open($outputfile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
1991
1992
				// Create recursive directory iterator
1993
				/** @var SplFileInfo[] $files */
1994
				$files = new RecursiveIteratorIterator(
1995
					new RecursiveDirectoryIterator($inputdir),
1996
					RecursiveIteratorIterator::LEAVES_ONLY
1997
					);
1998
1999
				foreach ($files as $name => $file)
2000
				{
2001
					// Skip directories (they would be added automatically)
2002
					if (!$file->isDir())
2003
					{
2004
						// Get real and relative path for current file
2005
						$filePath = $file->getRealPath();
2006
						$relativePath = substr($filePath, strlen($inputdir) + 1);
2007
2008
						// Add current file to archive
2009
						$zip->addFile($filePath, $relativePath);
2010
					}
2011
				}
2012
2013
				// Zip archive will be created only after closing object
2014
				$zip->close();
2015
2016
				return 1;
2017
			}
2018
		}
2019
2020
		if (! $foundhandler)
2021
		{
2022
			dol_syslog("Try to zip with format ".$mode." with no handler for this format",LOG_ERR);
2023
			return -2;
2024
		}
2025
		else
2026
		{
2027
			return 0;
2028
		}
2029
	}
2030
	catch (Exception $e)
2031
	{
2032
		global $langs, $errormsg;
2033
		$langs->load("errors");
2034
		dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
2035
		dol_syslog($e->getMessage(), LOG_ERR);
2036
		$errormsg=$langs->trans("ErrorFailedToWriteInDir",$outputfile);
2037
		return -1;
2038
	}
2039
}
2040
2041
2042
2043
/**
2044
 * Return file(s) into a directory (by default most recent)
2045
 *
2046
 * @param 	string		$dir			Directory to scan
2047
 * @param	string		$regexfilter	Regex filter to restrict list. This regex value must be escaped for '/', since this char is used for preg_match function
2048
 * @param	array		$excludefilter  Array of Regex for exclude filter (example: array('(\.meta|_preview.*\.png)$','^\.')). This regex value must be escaped for '/', since this char is used for preg_match function
2049
 * @param	int			$nohook			Disable all hooks
2050
 * @param	int			$mode			0=Return array minimum keys loaded (faster), 1=Force all keys like date and size to be loaded (slower), 2=Force load of date only, 3=Force load of size only
2051
 * @return	string						Full path to most recent file
2052
 */
2053
function dol_most_recent_file($dir,$regexfilter='',$excludefilter=array('(\.meta|_preview.*\.png)$','^\.'),$nohook=false,$mode='')
2054
{
2055
	$tmparray=dol_dir_list($dir,'files',0,$regexfilter,$excludefilter,'date',SORT_DESC,$mode,$nohook);
0 ignored issues
show
Bug introduced by
It seems like $nohook defined by parameter $nohook on line 2053 can also be of type false; however, dol_dir_list() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2056
	return $tmparray[0];
2057
}
2058
2059
/**
2060
 * Security check when accessing to a document (used by document.php, viewimage.php and webservices)
2061
 *
2062
 * @param	string	$modulepart			Module of document ('module', 'module_user_temp', 'module_user' or 'module_temp')
2063
 * @param	string	$original_file		Relative path with filename, relative to modulepart.
2064
 * @param	string	$entity				Restrict onto entity (0=no restriction)
2065
 * @param  	User	$fuser				User object (forced)
2066
 * @param	string	$refname			Ref of object to check permission for external users (autodetect if not provided)
2067
 * @param   string  $mode               Check permission for 'read' or 'write'
2068
 * @return	mixed						Array with access information : 'accessallowed' & 'sqlprotectagainstexternals' & 'original_file' (as a full path name)
2069
 * @see restrictedArea
2070
 */
2071
function dol_check_secure_access_document($modulepart, $original_file, $entity, $fuser='', $refname='', $mode='read')
2072
{
2073
	global $conf, $db, $user;
2074
	global $dolibarr_main_data_root, $dolibarr_main_document_root_alt;
2075
2076
	if (! is_object($fuser)) $fuser=$user;
2077
2078
	if (empty($modulepart)) return 'ErrorBadParameter';
2079
	if (empty($entity))
2080
	{
2081
		if (empty($conf->multicompany->enabled)) $entity=1;
2082
		else $entity=0;
2083
	}
2084
	dol_syslog('modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity);
2085
	// We define $accessallowed and $sqlprotectagainstexternals
2086
	$accessallowed=0;
2087
	$sqlprotectagainstexternals='';
2088
	$ret=array();
2089
2090
	// Find the subdirectory name as the reference. For exemple original_file='10/myfile.pdf' -> refname='10'
2091
	if (empty($refname)) $refname=basename(dirname($original_file)."/");
2092
2093
	$relative_original_file = $original_file;
2094
2095
	// Define possible keys to use for permission check
2096
	$lire='lire'; $read='read'; $download='download';
2097
	if ($mode == 'write')
2098
	{
2099
		$lire='creer'; $read='write'; $download='upload';
2100
	}
2101
2102
	// Wrapping for miscellaneous medias files
2103
	if ($modulepart == 'medias' && !empty($dolibarr_main_data_root))
2104
	{
2105
		if (empty($entity) || empty($conf->medias->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided');
2106
		$accessallowed=1;
2107
		$original_file=$conf->medias->multidir_output[$entity].'/'.$original_file;
2108
	}
2109
	// Wrapping for *.log files, like when used with url http://.../document.php?modulepart=logs&file=dolibarr.log
2110
	elseif ($modulepart == 'logs' && !empty($dolibarr_main_data_root))
2111
	{
2112
		$accessallowed=($user->admin && basename($original_file) == $original_file && preg_match('/^dolibarr.*\.log$/', basename($original_file)));
2113
		$original_file=$dolibarr_main_data_root.'/'.$original_file;
2114
	}
2115
	// Wrapping for *.zip files, like when used with url http://.../document.php?modulepart=packages&file=module_myfile.zip
2116
	elseif ($modulepart == 'packages' && !empty($dolibarr_main_data_root))
2117
	{
2118
		// Dir for custom dirs
2119
		$tmp=explode(',', $dolibarr_main_document_root_alt);
2120
		$dirins = $tmp[0];
2121
2122
		$accessallowed=($user->admin && preg_match('/^module_.*\.zip$/', basename($original_file)));
2123
		$original_file=$dirins.'/'.$original_file;
2124
	}
2125
	// Wrapping for some images
2126
	elseif (($modulepart == 'mycompany' || $modulepart == 'companylogo') && !empty($conf->mycompany->dir_output))
2127
	{
2128
		$accessallowed=1;
2129
		$original_file=$conf->mycompany->dir_output.'/logos/'.$original_file;
2130
	}
2131
	// Wrapping for users photos
2132
	elseif ($modulepart == 'userphoto' && !empty($conf->user->dir_output))
2133
	{
2134
		$accessallowed=1;
2135
		$original_file=$conf->user->dir_output.'/'.$original_file;
2136
	}
2137
	// Wrapping for members photos
2138
	elseif ($modulepart == 'memberphoto' && !empty($conf->adherent->dir_output))
2139
	{
2140
		$accessallowed=1;
2141
		$original_file=$conf->adherent->dir_output.'/'.$original_file;
2142
	}
2143
	// Wrapping pour les apercu factures
2144
	elseif ($modulepart == 'apercufacture' && !empty($conf->facture->dir_output))
2145
	{
2146
		if ($fuser->rights->facture->{$lire}) $accessallowed=1;
2147
		$original_file=$conf->facture->dir_output.'/'.$original_file;
2148
	}
2149
	// Wrapping pour les apercu propal
2150
	elseif ($modulepart == 'apercupropal' && !empty($conf->propal->dir_output))
2151
	{
2152
		if ($fuser->rights->propale->{$lire}) $accessallowed=1;
2153
		$original_file=$conf->propal->dir_output.'/'.$original_file;
2154
	}
2155
	// Wrapping pour les apercu commande
2156
	elseif ($modulepart == 'apercucommande' && !empty($conf->commande->dir_output))
2157
	{
2158
		if ($fuser->rights->commande->{$lire}) $accessallowed=1;
2159
		$original_file=$conf->commande->dir_output.'/'.$original_file;
2160
	}
2161
	// Wrapping pour les apercu intervention
2162
	elseif (($modulepart == 'apercufichinter' || $modulepart == 'apercuficheinter') && !empty($conf->ficheinter->dir_output))
2163
	{
2164
		if ($fuser->rights->ficheinter->{$lire}) $accessallowed=1;
2165
		$original_file=$conf->ficheinter->dir_output.'/'.$original_file;
2166
	}
2167
	// Wrapping pour les apercu conat
2168
	elseif (($modulepart == 'apercucontract') && !empty($conf->contrat->dir_output))
2169
	{
2170
		if ($fuser->rights->contrat->{$lire}) $accessallowed=1;
2171
		$original_file=$conf->contrat->dir_output.'/'.$original_file;
2172
	}
2173
	// Wrapping pour les apercu supplier proposal
2174
	elseif (($modulepart == 'apercusupplier_proposal' || $modulepart == 'apercusupplier_proposal') && !empty($conf->supplier_proposal->dir_output))
2175
	{
2176
		if ($fuser->rights->supplier_proposal->{$lire}) $accessallowed=1;
2177
		$original_file=$conf->supplier_proposal->dir_output.'/'.$original_file;
2178
	}
2179
	// Wrapping pour les apercu supplier order
2180
	elseif (($modulepart == 'apercusupplier_order' || $modulepart == 'apercusupplier_order') && !empty($conf->fournisseur->commande->dir_output))
2181
	{
2182
		if ($fuser->rights->fournisseur->commande->{$lire}) $accessallowed=1;
2183
		$original_file=$conf->fournisseur->commande->dir_output.'/'.$original_file;
2184
	}
2185
	// Wrapping pour les apercu supplier invoice
2186
	elseif (($modulepart == 'apercusupplier_invoice' || $modulepart == 'apercusupplier_invoice') && !empty($conf->fournisseur->facture->dir_output))
2187
	{
2188
		if ($fuser->rights->fournisseur->facture->{$lire}) $accessallowed=1;
2189
		$original_file=$conf->fournisseur->facture->dir_output.'/'.$original_file;
2190
	}
2191
	// Wrapping pour les apercu supplier invoice
2192
	elseif (($modulepart == 'apercuexpensereport') && !empty($conf->expensereport->dir_output))
2193
	{
2194
		if ($fuser->rights->expensereport->{$lire}) $accessallowed=1;
2195
		$original_file=$conf->expensereport->dir_output.'/'.$original_file;
2196
	}
2197
	// Wrapping pour les images des stats propales
2198
	elseif ($modulepart == 'propalstats' && !empty($conf->propal->dir_temp))
2199
	{
2200
		if ($fuser->rights->propale->{$lire}) $accessallowed=1;
2201
		$original_file=$conf->propal->dir_temp.'/'.$original_file;
2202
	}
2203
	// Wrapping pour les images des stats commandes
2204
	elseif ($modulepart == 'orderstats' && !empty($conf->commande->dir_temp))
2205
	{
2206
		if ($fuser->rights->commande->{$lire}) $accessallowed=1;
2207
		$original_file=$conf->commande->dir_temp.'/'.$original_file;
2208
	}
2209
	elseif ($modulepart == 'orderstatssupplier' && !empty($conf->fournisseur->dir_output))
2210
	{
2211
		if ($fuser->rights->fournisseur->commande->{$lire}) $accessallowed=1;
2212
		$original_file=$conf->fournisseur->commande->dir_temp.'/'.$original_file;
2213
	}
2214
	// Wrapping pour les images des stats factures
2215
	elseif ($modulepart == 'billstats' && !empty($conf->facture->dir_temp))
2216
	{
2217
		if ($fuser->rights->facture->{$lire}) $accessallowed=1;
2218
		$original_file=$conf->facture->dir_temp.'/'.$original_file;
2219
	}
2220
	elseif ($modulepart == 'billstatssupplier' && !empty($conf->fournisseur->dir_output))
2221
	{
2222
		if ($fuser->rights->fournisseur->facture->{$lire}) $accessallowed=1;
2223
		$original_file=$conf->fournisseur->facture->dir_temp.'/'.$original_file;
2224
	}
2225
	// Wrapping pour les images des stats expeditions
2226
	elseif ($modulepart == 'expeditionstats' && !empty($conf->expedition->dir_temp))
2227
	{
2228
		if ($fuser->rights->expedition->{$lire}) $accessallowed=1;
2229
		$original_file=$conf->expedition->dir_temp.'/'.$original_file;
2230
	}
2231
	// Wrapping pour les images des stats expeditions
2232
	elseif ($modulepart == 'tripsexpensesstats' && !empty($conf->deplacement->dir_temp))
2233
	{
2234
		if ($fuser->rights->deplacement->{$lire}) $accessallowed=1;
2235
		$original_file=$conf->deplacement->dir_temp.'/'.$original_file;
2236
	}
2237
	// Wrapping pour les images des stats expeditions
2238
	elseif ($modulepart == 'memberstats' && !empty($conf->adherent->dir_temp))
2239
	{
2240
		if ($fuser->rights->adherent->{$lire}) $accessallowed=1;
2241
		$original_file=$conf->adherent->dir_temp.'/'.$original_file;
2242
	}
2243
	// Wrapping pour les images des stats produits
2244
	elseif (preg_match('/^productstats_/i',$modulepart) && !empty($conf->product->dir_temp))
2245
	{
2246
		if ($fuser->rights->produit->{$lire} || $fuser->rights->service->{$lire}) $accessallowed=1;
2247
		$original_file=(!empty($conf->product->multidir_temp[$entity])?$conf->product->multidir_temp[$entity]:$conf->service->multidir_temp[$entity]).'/'.$original_file;
2248
	}
2249
	// Wrapping for taxes
2250
	elseif ($modulepart == 'tax' && !empty($conf->tax->dir_output))
2251
	{
2252
		if ($fuser->rights->tax->charges->{$lire}) $accessallowed=1;
2253
		$original_file=$conf->tax->dir_output.'/'.$original_file;
2254
	}
2255
	// Wrapping for events
2256
	elseif ($modulepart == 'actions' && !empty($conf->agenda->dir_output))
2257
	{
2258
		if ($fuser->rights->agenda->myactions->{$read}) $accessallowed=1;
2259
		$original_file=$conf->agenda->dir_output.'/'.$original_file;
2260
	}
2261
	// Wrapping for categories
2262
	elseif ($modulepart == 'category' && !empty($conf->categorie->dir_output))
2263
	{
2264
		if (empty($entity) || empty($conf->categorie->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided');
2265
		if ($fuser->rights->categorie->{$lire}) $accessallowed=1;
2266
		$original_file=$conf->categorie->multidir_output[$entity].'/'.$original_file;
2267
	}
2268
	// Wrapping pour les prelevements
2269
	elseif ($modulepart == 'prelevement' && !empty($conf->prelevement->dir_output))
2270
	{
2271
		if ($fuser->rights->prelevement->bons->{$lire} || preg_match('/^specimen/i',$original_file)) $accessallowed=1;
2272
		$original_file=$conf->prelevement->dir_output.'/'.$original_file;
2273
	}
2274
	// Wrapping pour les graph energie
2275
	elseif ($modulepart == 'graph_stock' && !empty($conf->stock->dir_temp))
2276
	{
2277
		$accessallowed=1;
2278
		$original_file=$conf->stock->dir_temp.'/'.$original_file;
2279
	}
2280
	// Wrapping pour les graph fournisseurs
2281
	elseif ($modulepart == 'graph_fourn' && !empty($conf->fournisseur->dir_temp))
2282
	{
2283
		$accessallowed=1;
2284
		$original_file=$conf->fournisseur->dir_temp.'/'.$original_file;
2285
	}
2286
	// Wrapping pour les graph des produits
2287
	elseif ($modulepart == 'graph_product' && !empty($conf->product->dir_temp))
2288
	{
2289
		$accessallowed=1;
2290
		$original_file=$conf->product->multidir_temp[$entity].'/'.$original_file;
2291
	}
2292
	// Wrapping pour les code barre
2293
	elseif ($modulepart == 'barcode')
2294
	{
2295
		$accessallowed=1;
2296
		// If viewimage is called for barcode, we try to output an image on the fly, with no build of file on disk.
2297
		//$original_file=$conf->barcode->dir_temp.'/'.$original_file;
2298
		$original_file='';
2299
	}
2300
	// Wrapping pour les icones de background des mailings
2301
	elseif ($modulepart == 'iconmailing' && !empty($conf->mailing->dir_temp))
2302
	{
2303
		$accessallowed=1;
2304
		$original_file=$conf->mailing->dir_temp.'/'.$original_file;
2305
	}
2306
	// Wrapping pour le scanner
2307
	elseif ($modulepart == 'scanner_user_temp' && !empty($conf->scanner->dir_temp))
2308
	{
2309
		$accessallowed=1;
2310
		$original_file=$conf->scanner->dir_temp.'/'.$fuser->id.'/'.$original_file;
2311
	}
2312
	// Wrapping pour les images fckeditor
2313
	elseif ($modulepart == 'fckeditor' && !empty($conf->fckeditor->dir_output))
2314
	{
2315
		$accessallowed=1;
2316
		$original_file=$conf->fckeditor->dir_output.'/'.$original_file;
2317
	}
2318
2319
	// Wrapping for users
2320
	else if ($modulepart == 'user' && !empty($conf->user->dir_output))
2321
	{
2322
		$canreaduser=(! empty($fuser->admin) || $fuser->rights->user->user->{$lire});
2323
		if ($fuser->id == (int) $refname) { $canreaduser=1; } // A user can always read its own card
2324
		if ($canreaduser || preg_match('/^specimen/i',$original_file))
2325
		{
2326
			$accessallowed=1;
2327
		}
2328
		$original_file=$conf->user->dir_output.'/'.$original_file;
2329
	}
2330
2331
	// Wrapping for third parties
2332
	else if (($modulepart == 'company' || $modulepart == 'societe') && !empty($conf->societe->dir_output))
2333
	{
2334
		if (empty($entity) || empty($conf->societe->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided');
2335
		if ($fuser->rights->societe->{$lire} || preg_match('/^specimen/i',$original_file))
2336
		{
2337
			$accessallowed=1;
2338
		}
2339
		$original_file=$conf->societe->multidir_output[$entity].'/'.$original_file;
2340
		$sqlprotectagainstexternals = "SELECT rowid as fk_soc FROM ".MAIN_DB_PREFIX."societe WHERE rowid='".$db->escape($refname)."' AND entity IN (".getEntity('societe').")";
2341
	}
2342
2343
	// Wrapping for contact
2344
	else if ($modulepart == 'contact' && !empty($conf->societe->dir_output))
2345
	{
2346
		if (empty($entity) || empty($conf->societe->multidir_output[$entity])) return array('accessallowed'=>0, 'error'=>'Value entity must be provided');
2347
		if ($fuser->rights->societe->{$lire})
2348
		{
2349
			$accessallowed=1;
2350
		}
2351
		$original_file=$conf->societe->multidir_output[$entity].'/contact/'.$original_file;
2352
	}
2353
2354
	// Wrapping for invoices
2355
	else if (($modulepart == 'facture' || $modulepart == 'invoice') && !empty($conf->facture->dir_output))
2356
	{
2357
		if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i',$original_file))
2358
		{
2359
			$accessallowed=1;
2360
		}
2361
		$original_file=$conf->facture->dir_output.'/'.$original_file;
2362
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
2363
	}
2364
	// Wrapping for mass actions
2365
	else if ($modulepart == 'massfilesarea_proposals' && !empty($conf->propal->dir_output))
2366
	{
2367
		if ($fuser->rights->propal->{$lire} || preg_match('/^specimen/i',$original_file))
2368
		{
2369
			$accessallowed=1;
2370
		}
2371
		$original_file=$conf->propal->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2372
	}
2373
	else if ($modulepart == 'massfilesarea_orders')
2374
	{
2375
		if ($fuser->rights->commande->{$lire} || preg_match('/^specimen/i',$original_file))
2376
		{
2377
			$accessallowed=1;
2378
		}
2379
		$original_file=$conf->commande->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2380
	}
2381
	else if ($modulepart == 'massfilesarea_invoices')
2382
	{
2383
		if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i',$original_file))
2384
		{
2385
			$accessallowed=1;
2386
		}
2387
		$original_file=$conf->facture->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2388
	}
2389
	else if ($modulepart == 'massfilesarea_expensereport')
2390
	{
2391
		if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i',$original_file))
2392
		{
2393
			$accessallowed=1;
2394
		}
2395
		$original_file=$conf->expensereport->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2396
	}
2397
	else if ($modulepart == 'massfilesarea_interventions')
2398
	{
2399
		if ($fuser->rights->ficheinter->{$lire} || preg_match('/^specimen/i',$original_file))
2400
		{
2401
			$accessallowed=1;
2402
		}
2403
		$original_file=$conf->ficheinter->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2404
	}
2405
	else if ($modulepart == 'massfilesarea_supplier_proposal' && !empty($conf->propal->dir_output))
2406
	{
2407
		if ($fuser->rights->supplier_proposal->{$lire} || preg_match('/^specimen/i',$original_file))
2408
		{
2409
			$accessallowed=1;
2410
		}
2411
		$original_file=$conf->supplier_proposal->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2412
	}
2413
	else if ($modulepart == 'massfilesarea_supplier_order')
2414
	{
2415
		if ($fuser->rights->fournisseur->commande->{$lire} || preg_match('/^specimen/i',$original_file))
2416
		{
2417
			$accessallowed=1;
2418
		}
2419
		$original_file=$conf->fournisseur->commande->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2420
	}
2421
	else if ($modulepart == 'massfilesarea_supplier_invoice')
2422
	{
2423
		if ($fuser->rights->fournisseur->facture->{$lire} || preg_match('/^specimen/i',$original_file))
2424
		{
2425
			$accessallowed=1;
2426
		}
2427
		$original_file=$conf->fournisseur->facture->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2428
	}
2429
	else if ($modulepart == 'massfilesarea_contract' && !empty($conf->contrat->dir_output))
2430
	{
2431
		if ($fuser->rights->contrat->{$lire} || preg_match('/^specimen/i',$original_file))
2432
		{
2433
			$accessallowed=1;
2434
		}
2435
		$original_file=$conf->contrat->dir_output.'/temp/massgeneration/'.$user->id.'/'.$original_file;
2436
	}
2437
2438
	// Wrapping for interventions
2439
	else if (($modulepart == 'fichinter' || $modulepart == 'ficheinter') && !empty($conf->ficheinter->dir_output))
2440
	{
2441
		if ($fuser->rights->ficheinter->{$lire} || preg_match('/^specimen/i',$original_file))
2442
		{
2443
			$accessallowed=1;
2444
		}
2445
		$original_file=$conf->ficheinter->dir_output.'/'.$original_file;
2446
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
2447
	}
2448
2449
	// Wrapping pour les deplacements et notes de frais
2450
	else if ($modulepart == 'deplacement' && !empty($conf->deplacement->dir_output))
2451
	{
2452
		if ($fuser->rights->deplacement->{$lire} || preg_match('/^specimen/i',$original_file))
2453
		{
2454
			$accessallowed=1;
2455
		}
2456
		$original_file=$conf->deplacement->dir_output.'/'.$original_file;
2457
		//$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."fichinter WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
2458
	}
2459
	// Wrapping pour les propales
2460
	else if ($modulepart == 'propal' && !empty($conf->propal->dir_output))
2461
	{
2462
		if ($fuser->rights->propale->{$lire} || preg_match('/^specimen/i',$original_file))
2463
		{
2464
			$accessallowed=1;
2465
		}
2466
2467
		$original_file=$conf->propal->dir_output.'/'.$original_file;
2468
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."propal WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
2469
	}
2470
2471
	// Wrapping pour les commandes
2472
	else if (($modulepart == 'commande' || $modulepart == 'order') && !empty($conf->commande->dir_output))
2473
	{
2474
		if ($fuser->rights->commande->{$lire} || preg_match('/^specimen/i',$original_file))
2475
		{
2476
			$accessallowed=1;
2477
		}
2478
		$original_file=$conf->commande->dir_output.'/'.$original_file;
2479
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
2480
	}
2481
2482
	// Wrapping pour les projets
2483
	else if ($modulepart == 'project' && !empty($conf->projet->dir_output))
2484
	{
2485
		if ($fuser->rights->projet->{$lire} || preg_match('/^specimen/i',$original_file))
2486
		{
2487
			$accessallowed=1;
2488
		}
2489
		$original_file=$conf->projet->dir_output.'/'.$original_file;
2490
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
2491
	}
2492
	else if ($modulepart == 'project_task' && !empty($conf->projet->dir_output))
2493
	{
2494
		if ($fuser->rights->projet->{$lire} || preg_match('/^specimen/i',$original_file))
2495
		{
2496
			$accessallowed=1;
2497
		}
2498
		$original_file=$conf->projet->dir_output.'/'.$original_file;
2499
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."projet WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('project').")";
2500
	}
2501
2502
	// Wrapping pour les commandes fournisseurs
2503
	else if (($modulepart == 'commande_fournisseur' || $modulepart == 'order_supplier') && !empty($conf->fournisseur->commande->dir_output))
2504
	{
2505
		if ($fuser->rights->fournisseur->commande->{$lire} || preg_match('/^specimen/i',$original_file))
2506
		{
2507
			$accessallowed=1;
2508
		}
2509
		$original_file=$conf->fournisseur->commande->dir_output.'/'.$original_file;
2510
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."commande_fournisseur WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
2511
	}
2512
2513
	// Wrapping pour les factures fournisseurs
2514
	else if (($modulepart == 'facture_fournisseur' || $modulepart == 'invoice_supplier') && !empty($conf->fournisseur->facture->dir_output))
2515
	{
2516
		if ($fuser->rights->fournisseur->facture->{$lire} || preg_match('/^specimen/i',$original_file))
2517
		{
2518
			$accessallowed=1;
2519
		}
2520
		$original_file=$conf->fournisseur->facture->dir_output.'/'.$original_file;
2521
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."facture_fourn WHERE facnumber='".$db->escape($refname)."' AND entity=".$conf->entity;
2522
	}
2523
	// Wrapping pour les rapport de paiements
2524
	else if ($modulepart == 'supplier_payment')
2525
	{
2526
		if ($fuser->rights->fournisseur->facture->{$lire} || preg_match('/^specimen/i',$original_file))
2527
		{
2528
			$accessallowed=1;
2529
		}
2530
		$original_file=$conf->fournisseur->payment->dir_output.'/'.$original_file;
2531
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."paiementfournisseur WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
2532
	}
2533
2534
	// Wrapping pour les rapport de paiements
2535
	else if ($modulepart == 'facture_paiement' && !empty($conf->facture->dir_output))
2536
	{
2537
		if ($fuser->rights->facture->{$lire} || preg_match('/^specimen/i',$original_file))
2538
		{
2539
			$accessallowed=1;
2540
		}
2541
		if ($fuser->societe_id > 0) $original_file=$conf->facture->dir_output.'/payments/private/'.$fuser->id.'/'.$original_file;
2542
		else $original_file=$conf->facture->dir_output.'/payments/'.$original_file;
2543
	}
2544
2545
	// Wrapping for accounting exports
2546
	else if ($modulepart == 'export_compta' && !empty($conf->accounting->dir_output))
2547
	{
2548
		if ($fuser->rights->accounting->bind->write || preg_match('/^specimen/i',$original_file))
2549
		{
2550
			$accessallowed=1;
2551
		}
2552
		$original_file=$conf->accounting->dir_output.'/'.$original_file;
2553
	}
2554
2555
	// Wrapping pour les expedition
2556
	else if ($modulepart == 'expedition' && !empty($conf->expedition->dir_output))
2557
	{
2558
		if ($fuser->rights->expedition->{$lire} || preg_match('/^specimen/i',$original_file))
2559
		{
2560
			$accessallowed=1;
2561
		}
2562
		$original_file=$conf->expedition->dir_output."/sending/".$original_file;
2563
	}
2564
	// Wrapping pour les bons de livraison
2565
	else if ($modulepart == 'livraison' && !empty($conf->expedition->dir_output))
2566
	{
2567
		if ($fuser->rights->expedition->livraison->{$lire} || preg_match('/^specimen/i',$original_file))
2568
		{
2569
			$accessallowed=1;
2570
		}
2571
		$original_file=$conf->expedition->dir_output."/receipt/".$original_file;
2572
	}
2573
2574
	// Wrapping pour les actions
2575
	else if ($modulepart == 'actions' && !empty($conf->agenda->dir_output))
2576
	{
2577
		if ($fuser->rights->agenda->myactions->{$read} || preg_match('/^specimen/i',$original_file))
2578
		{
2579
			$accessallowed=1;
2580
		}
2581
		$original_file=$conf->agenda->dir_output.'/'.$original_file;
2582
	}
2583
2584
	// Wrapping pour les actions
2585
	else if ($modulepart == 'actionsreport' && !empty($conf->agenda->dir_temp))
2586
	{
2587
		if ($fuser->rights->agenda->allactions->{$read} || preg_match('/^specimen/i',$original_file))
2588
		{
2589
			$accessallowed=1;
2590
		}
2591
		$original_file = $conf->agenda->dir_temp."/".$original_file;
2592
	}
2593
2594
	// Wrapping pour les produits et services
2595
	else if ($modulepart == 'product' || $modulepart == 'produit' || $modulepart == 'service' || $modulepart == 'produit|service')
2596
	{
2597
		if (empty($entity) || (empty($conf->product->multidir_output[$entity]) && empty($conf->service->multidir_output[$entity]))) return array('accessallowed'=>0, 'error'=>'Value entity must be provided');
2598
		if (($fuser->rights->produit->{$lire} || $fuser->rights->service->{$lire}) || preg_match('/^specimen/i',$original_file))
2599
		{
2600
			$accessallowed=1;
2601
		}
2602
		if (! empty($conf->product->enabled)) $original_file=$conf->product->multidir_output[$entity].'/'.$original_file;
2603
		elseif (! empty($conf->service->enabled)) $original_file=$conf->service->multidir_output[$entity].'/'.$original_file;
2604
	}
2605
2606
	// Wrapping pour les contrats
2607
	else if ($modulepart == 'contract' && !empty($conf->contrat->dir_output))
2608
	{
2609
		if ($fuser->rights->contrat->{$lire} || preg_match('/^specimen/i',$original_file))
2610
		{
2611
			$accessallowed=1;
2612
		}
2613
		$original_file=$conf->contrat->dir_output.'/'.$original_file;
2614
		$sqlprotectagainstexternals = "SELECT fk_soc as fk_soc FROM ".MAIN_DB_PREFIX."contrat WHERE ref='".$db->escape($refname)."' AND entity IN (".getEntity('contract').")";
2615
	}
2616
2617
	// Wrapping pour les dons
2618
	else if ($modulepart == 'donation' && !empty($conf->don->dir_output))
2619
	{
2620
		if ($fuser->rights->don->{$lire} || preg_match('/^specimen/i',$original_file))
2621
		{
2622
			$accessallowed=1;
2623
		}
2624
		$original_file=$conf->don->dir_output.'/'.$original_file;
2625
	}
2626
2627
	// Wrapping pour les dons
2628
	else if ($modulepart == 'dolresource' && !empty($conf->resource->dir_output))
2629
	{
2630
		if ($fuser->rights->resource->{$read} || preg_match('/^specimen/i',$original_file))
2631
		{
2632
			$accessallowed=1;
2633
		}
2634
		$original_file=$conf->resource->dir_output.'/'.$original_file;
2635
	}
2636
2637
	// Wrapping pour les remises de cheques
2638
	else if ($modulepart == 'remisecheque' && !empty($conf->banque->dir_output))
2639
	{
2640
		if ($fuser->rights->banque->{$lire} || preg_match('/^specimen/i',$original_file))
2641
		{
2642
			$accessallowed=1;
2643
		}
2644
2645
		$original_file=$conf->bank->dir_output.'/checkdeposits/'.$original_file;		// original_file should contains relative path so include the get_exdir result
2646
	}
2647
2648
	// Wrapping for bank
2649
	else if ($modulepart == 'bank' && !empty($conf->bank->dir_output))
2650
	{
2651
		if ($fuser->rights->banque->{$lire})
2652
		{
2653
			$accessallowed=1;
2654
		}
2655
		$original_file=$conf->bank->dir_output.'/'.$original_file;
2656
	}
2657
2658
	// Wrapping for export module
2659
	else if ($modulepart == 'export' && !empty($conf->export->dir_temp))
2660
	{
2661
		// Aucun test necessaire car on force le rep de download sur
2662
		// le rep export qui est propre a l'utilisateur
2663
		$accessallowed=1;
2664
		$original_file=$conf->export->dir_temp.'/'.$fuser->id.'/'.$original_file;
2665
	}
2666
2667
	// Wrapping for import module
2668
	else if ($modulepart == 'import' && !empty($conf->import->dir_temp))
2669
	{
2670
		$accessallowed=1;
2671
		$original_file=$conf->import->dir_temp.'/'.$original_file;
2672
	}
2673
2674
	// Wrapping pour l'editeur wysiwyg
2675
	else if ($modulepart == 'editor' && !empty($conf->fckeditor->dir_output))
2676
	{
2677
		$accessallowed=1;
2678
		$original_file=$conf->fckeditor->dir_output.'/'.$original_file;
2679
	}
2680
2681
	// Wrapping for backups
2682
	else if ($modulepart == 'systemtools' && !empty($conf->admin->dir_output))
2683
	{
2684
		if ($fuser->admin) $accessallowed=1;
2685
		$original_file=$conf->admin->dir_output.'/'.$original_file;
2686
	}
2687
2688
	// Wrapping for upload file test
2689
	else if ($modulepart == 'admin_temp' && !empty($conf->admin->dir_temp))
2690
	{
2691
		if ($fuser->admin) $accessallowed=1;
2692
		$original_file=$conf->admin->dir_temp.'/'.$original_file;
2693
	}
2694
2695
	// Wrapping pour BitTorrent
2696
	else if ($modulepart == 'bittorrent' && !empty($conf->bittorrent->dir_output))
2697
	{
2698
		$accessallowed=1;
2699
		$dir='files';
2700
		if (dol_mimetype($original_file) == 'application/x-bittorrent') $dir='torrents';
2701
		$original_file=$conf->bittorrent->dir_output.'/'.$dir.'/'.$original_file;
2702
	}
2703
2704
	// Wrapping pour Foundation module
2705
	else if ($modulepart == 'member' && !empty($conf->adherent->dir_output))
2706
	{
2707
		if ($fuser->rights->adherent->{$lire} || preg_match('/^specimen/i',$original_file))
2708
		{
2709
			$accessallowed=1;
2710
		}
2711
		$original_file=$conf->adherent->dir_output.'/'.$original_file;
2712
	}
2713
2714
	// Wrapping for Scanner
2715
	else if ($modulepart == 'scanner_user_temp' && !empty($conf->scanner->dir_temp))
2716
	{
2717
		$accessallowed=1;
2718
		$original_file=$conf->scanner->dir_temp.'/'.$fuser->id.'/'.$original_file;
2719
	}
2720
2721
	// GENERIC Wrapping
2722
	// If modulepart=module_user_temp	Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp/iduser
2723
	// If modulepart=module_temp		Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/temp
2724
	// If modulepart=module_user		Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart/iduser
2725
	// If modulepart=module				Allows any module to open a file if file is in directory called DOL_DATA_ROOT/modulepart
2726
	else
2727
	{
2728
		if (preg_match('/^specimen/i',$original_file))	$accessallowed=1;    // If link to a file called specimen. Test must be done before changing $original_file int full path.
2729
		if ($fuser->admin) $accessallowed=1;    // If user is admin
2730
2731
		// Define $accessallowed
2732
		if (preg_match('/^([a-z]+)_user_temp$/i',$modulepart,$reg))
2733
		{
2734
			if (empty($conf->{$reg[1]}->dir_temp))	// modulepart not supported
2735
			{
2736
				dol_print_error('','Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
2737
				exit;
2738
			}
2739
			if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) $accessallowed=1;
2740
			$original_file=$conf->{$reg[1]}->dir_temp.'/'.$fuser->id.'/'.$original_file;
2741
		}
2742
		else if (preg_match('/^([a-z]+)_temp$/i',$modulepart,$reg))
2743
		{
2744
			if (empty($conf->{$reg[1]}->dir_temp))	// modulepart not supported
2745
			{
2746
				dol_print_error('','Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
2747
				exit;
2748
			}
2749
			if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) $accessallowed=1;
2750
			$original_file=$conf->{$reg[1]}->dir_temp.'/'.$original_file;
2751
		}
2752
		else if (preg_match('/^([a-z]+)_user$/i',$modulepart,$reg))
2753
		{
2754
			if (empty($conf->{$reg[1]}->dir_output))	// modulepart not supported
2755
			{
2756
				dol_print_error('','Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
2757
				exit;
2758
			}
2759
			if ($fuser->rights->{$reg[1]}->{$lire} || $fuser->rights->{$reg[1]}->{$read} || ($fuser->rights->{$reg[1]}->{$download})) $accessallowed=1;
2760
			$original_file=$conf->{$reg[1]}->dir_output.'/'.$fuser->id.'/'.$original_file;
2761
		}
2762
		else
2763
		{
2764
			if (empty($conf->$modulepart->dir_output))	// modulepart not supported
2765
			{
2766
				dol_print_error('','Error call dol_check_secure_access_document with not supported value for modulepart parameter ('.$modulepart.')');
2767
				exit;
2768
			}
2769
2770
			$perm=GETPOST('perm');
2771
			$subperm=GETPOST('subperm');
2772
			if ($perm || $subperm)
2773
			{
2774
				if (($perm && ! $subperm && $fuser->rights->$modulepart->$perm) || ($perm && $subperm && $fuser->rights->$modulepart->$perm->$subperm)) $accessallowed=1;
2775
				$original_file=$conf->$modulepart->dir_output.'/'.$original_file;
2776
			}
2777
			else
2778
			{
2779
				if ($fuser->rights->$modulepart->{$lire} || $fuser->rights->$modulepart->{$read}) $accessallowed=1;
2780
				$original_file=$conf->$modulepart->dir_output.'/'.$original_file;
2781
			}
2782
		}
2783
2784
		// For modules who wants to manage different levels of permissions for documents
2785
		$subPermCategoryConstName = strtoupper($modulepart).'_SUBPERMCATEGORY_FOR_DOCUMENTS';
2786
		if (! empty($conf->global->$subPermCategoryConstName))
2787
		{
2788
			$subPermCategory = $conf->global->$subPermCategoryConstName;
2789
			if (! empty($subPermCategory) && (($fuser->rights->$modulepart->$subPermCategory->{$lire}) || ($fuser->rights->$modulepart->$subPermCategory->{$read}) || ($fuser->rights->$modulepart->$subPermCategory->{$download})))
2790
			{
2791
				$accessallowed=1;
2792
			}
2793
		}
2794
2795
		// Define $sqlprotectagainstexternals for modules who want to protect access using a SQL query.
2796
		$sqlProtectConstName = strtoupper($modulepart).'_SQLPROTECTAGAINSTEXTERNALS_FOR_DOCUMENTS';
2797
		if (! empty($conf->global->$sqlProtectConstName))	// If module want to define its own $sqlprotectagainstexternals
2798
		{
2799
			// Example: mymodule__SQLPROTECTAGAINSTEXTERNALS_FOR_DOCUMENTS = "SELECT fk_soc FROM ".MAIN_DB_PREFIX.$modulepart." WHERE ref='".$db->escape($refname)."' AND entity=".$conf->entity;
2800
			eval('$sqlprotectagainstexternals = "'.$conf->global->$sqlProtectConstName.'";');
0 ignored issues
show
Coding Style introduced by
The function dol_check_secure_access_document() contains an eval expression.

On one hand, eval might be exploited by malicious users if they somehow manage to inject dynamic content. On the other hand, with the emergence of faster PHP runtimes like the HHVM, eval prevents some optimization that they perform.

Loading history...
2801
		}
2802
	}
2803
2804
	$ret = array(
2805
		'accessallowed' => $accessallowed,
2806
		'sqlprotectagainstexternals'=>$sqlprotectagainstexternals,
2807
		'original_file'=>$original_file
2808
	);
2809
2810
	return $ret;
2811
}
2812
2813
/**
2814
 * Store object in file.
2815
 *
2816
 * @param string $directory Directory of cache
2817
 * @param string $filename Name of filecache
2818
 * @param mixed $object Object to store in cachefile
2819
 * @return void
2820
 */
2821
function dol_filecache($directory, $filename, $object)
2822
{
2823
	if (! dol_is_dir($directory)) dol_mkdir($directory);
2824
	$cachefile = $directory . $filename;
2825
	file_put_contents($cachefile, serialize($object), LOCK_EX);
2826
	@chmod($cachefile, 0644);
2827
}
2828
2829
/**
2830
 * Test if Refresh needed.
2831
 *
2832
 * @param string $directory Directory of cache
2833
 * @param string $filename Name of filecache
2834
 * @param int $cachetime Cachetime delay
2835
 * @return boolean 0 no refresh 1 if refresh needed
2836
 */
2837
function dol_cache_refresh($directory, $filename, $cachetime)
2838
{
2839
	$now = dol_now();
2840
	$cachefile = $directory . $filename;
2841
	$refresh = !file_exists($cachefile) || ($now-$cachetime) > dol_filemtime($cachefile);
2842
	return $refresh;
2843
}
2844
2845
/**
2846
 * Read object from cachefile.
2847
 *
2848
 * @param string $directory Directory of cache
2849
 * @param string $filename Name of filecache
2850
 * @return mixed Unserialise from file
2851
 */
2852
function dol_readcachefile($directory, $filename)
2853
{
2854
	$cachefile = $directory . $filename;
2855
	$object = unserialize(file_get_contents($cachefile));
2856
	return $object;
2857
}
2858
2859
2860
/**
2861
 * Function to get list of updated or modified files.
2862
 * $file_list is used as global variable
2863
 *
2864
 * @param	array				$file_list	        Array for response
2865
 * @param   SimpleXMLElement	$dir    	        SimpleXMLElement of files to test
2866
 * @param   string   			$path   	        Path of files relative to $pathref. We start with ''. Used by recursive calls.
2867
 * @param   string              $pathref            Path ref (DOL_DOCUMENT_ROOT)
2868
 * @param   array               $checksumconcat     Array of checksum
2869
 * @return  array               			        Array of filenames
2870
 */
2871
function getFilesUpdated(&$file_list, SimpleXMLElement $dir, $path = '', $pathref = '', &$checksumconcat = array())
2872
{
2873
	global $conffile;
2874
2875
	$exclude = 'install';
2876
2877
	foreach ($dir->md5file as $file)    // $file is a simpleXMLElement
2878
	{
2879
		$filename = $path.$file['name'];
2880
		$file_list['insignature'][] = $filename;
2881
		$expectedmd5 = (string) $file;
2882
2883
		//if (preg_match('#'.$exclude.'#', $filename)) continue;
2884
2885
		if (!file_exists($pathref.'/'.$filename))
2886
		{
2887
			$file_list['missing'][] = array('filename'=>$filename, 'expectedmd5'=>$expectedmd5);
2888
		}
2889
		else
2890
		{
2891
			$md5_local = md5_file($pathref.'/'.$filename);
2892
2893
			if ($conffile == '/etc/dolibarr/conf.php' && $filename == '/filefunc.inc.php')	// For install with deb or rpm, we ignore test on filefunc.inc.php that was modified by package
2894
			{
2895
				$checksumconcat[] = $expectedmd5;
2896
			}
2897
			else
2898
			{
2899
				if ($md5_local != $expectedmd5) $file_list['updated'][] = array('filename'=>$filename, 'expectedmd5'=>$expectedmd5, 'md5'=>(string) $md5_local);
2900
				$checksumconcat[] = $md5_local;
2901
			}
2902
		}
2903
	}
2904
2905
	foreach ($dir->dir as $subdir)			// $subdir['name'] is  '' or '/accountancy/admin' for example
2906
	{
2907
		getFilesUpdated($file_list, $subdir, $path.$subdir['name'].'/', $pathref, $checksumconcat);
2908
	}
2909
2910
	return $file_list;
2911
}
2912
2913