Completed
Push — stable8 ( 42720e...c45eda )
by
unknown
35s
created

OC_Helper::linkToDocs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * ownCloud
4
 *
5
 * @author Frank Karlitschek
6
 * @author Jakob Sack
7
 * @copyright 2012 Frank Karlitschek [email protected]
8
 *
9
 * This library is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
11
 * License as published by the Free Software Foundation; either
12
 * version 3 of the License, or any later version.
13
 *
14
 * This library is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public
20
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
/**
25
 * Collection of useful functions
26
 */
27
class OC_Helper {
28
	private static $mimetypeIcons = array();
29
	private static $mimetypeDetector;
30
	private static $templateManager;
31
	/** @var string[] */
32
	private static $mimeTypeAlias = array(
33
		'application/octet-stream' => 'file', // use file icon as fallback
34
35
		'application/illustrator' => 'image/vector',
36
		'application/postscript' => 'image/vector',
37
		'image/svg+xml' => 'image/vector',
38
39
		'application/coreldraw' => 'image',
40
		'application/x-gimp' => 'image',
41
		'application/x-photoshop' => 'image',
42
43
		'application/x-font-ttf' => 'font',
44
		'application/font-woff' => 'font',
45
		'application/vnd.ms-fontobject' => 'font',
46
47
		'application/json' => 'text/code',
48
		'application/x-perl' => 'text/code',
49
		'application/x-php' => 'text/code',
50
		'text/x-shellscript' => 'text/code',
51
		'application/xml' => 'text/html',
52
		'text/css' => 'text/code',
53
		'application/x-tex' => 'text',
54
55
		'application/x-compressed' => 'package/x-generic',
56
		'application/x-7z-compressed' => 'package/x-generic',
57
		'application/x-deb' => 'package/x-generic',
58
		'application/x-gzip' => 'package/x-generic',
59
		'application/x-rar-compressed' => 'package/x-generic',
60
		'application/x-tar' => 'package/x-generic',
61
		'application/vnd.android.package-archive' => 'package/x-generic',
62
		'application/zip' => 'package/x-generic',
63
64
		'application/msword' => 'x-office/document',
65
		'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'x-office/document',
66
		'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'x-office/document',
67
		'application/vnd.ms-word.document.macroEnabled.12' => 'x-office/document',
68
		'application/vnd.ms-word.template.macroEnabled.12' => 'x-office/document',
69
		'application/vnd.oasis.opendocument.text' => 'x-office/document',
70
		'application/vnd.oasis.opendocument.text-template' => 'x-office/document',
71
		'application/vnd.oasis.opendocument.text-web' => 'x-office/document',
72
		'application/vnd.oasis.opendocument.text-master' => 'x-office/document',
73
74
		'application/mspowerpoint' => 'x-office/presentation',
75
		'application/vnd.ms-powerpoint' => 'x-office/presentation',
76
		'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'x-office/presentation',
77
		'application/vnd.openxmlformats-officedocument.presentationml.template' => 'x-office/presentation',
78
		'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'x-office/presentation',
79
		'application/vnd.ms-powerpoint.addin.macroEnabled.12' => 'x-office/presentation',
80
		'application/vnd.ms-powerpoint.presentation.macroEnabled.12' => 'x-office/presentation',
81
		'application/vnd.ms-powerpoint.template.macroEnabled.12' => 'x-office/presentation',
82
		'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' => 'x-office/presentation',
83
		'application/vnd.oasis.opendocument.presentation' => 'x-office/presentation',
84
		'application/vnd.oasis.opendocument.presentation-template' => 'x-office/presentation',
85
86
		'application/msexcel' => 'x-office/spreadsheet',
87
		'application/vnd.ms-excel' => 'x-office/spreadsheet',
88
		'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'x-office/spreadsheet',
89
		'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'x-office/spreadsheet',
90
		'application/vnd.ms-excel.sheet.macroEnabled.12' => 'x-office/spreadsheet',
91
		'application/vnd.ms-excel.template.macroEnabled.12' => 'x-office/spreadsheet',
92
		'application/vnd.ms-excel.addin.macroEnabled.12' => 'x-office/spreadsheet',
93
		'application/vnd.ms-excel.sheet.binary.macroEnabled.12' => 'x-office/spreadsheet',
94
		'application/vnd.oasis.opendocument.spreadsheet' => 'x-office/spreadsheet',
95
		'application/vnd.oasis.opendocument.spreadsheet-template' => 'x-office/spreadsheet',
96
		'text/csv' => 'x-office/spreadsheet',
97
98
		'application/msaccess' => 'database',
99
	);
100
101
	/**
102
	 * Creates an url using a defined route
103
	 * @param string $route
104
	 * @param array $parameters
105
	 * @return
106
	 * @internal param array $args with param=>value, will be appended to the returned url
107
	 * @return string the url
108
	 *
109
	 * Returns a url to the given app and file.
110
	 */
111
	public static function linkToRoute($route, $parameters = array()) {
112
		return OC::$server->getURLGenerator()->linkToRoute($route, $parameters);
113
	}
114
115
	/**
116
	 * Creates an url
117
	 * @param string $app app
118
	 * @param string $file file
119
	 * @param array $args array with param=>value, will be appended to the returned url
120
	 *    The value of $args will be urlencoded
121
	 * @return string the url
122
	 *
123
	 * Returns a url to the given app and file.
124
	 */
125
	public static function linkTo( $app, $file, $args = array() ) {
126
		return OC::$server->getURLGenerator()->linkTo($app, $file, $args);
0 ignored issues
show
Unused Code introduced by
The call to IURLGenerator::linkTo() has too many arguments starting with $args.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
127
	}
128
129
	/**
130
	 * @param string $key
131
	 * @return string url to the online documentation
132
	 */
133
	public static function linkToDocs($key) {
134
		return OC::$server->getURLGenerator()->linkToDocs($key);
135
	}
136
137
	/**
138
	 * Creates an absolute url
139
	 * @param string $app app
140
	 * @param string $file file
141
	 * @param array $args array with param=>value, will be appended to the returned url
142
	 *    The value of $args will be urlencoded
143
	 * @return string the url
144
	 *
145
	 * Returns a absolute url to the given app and file.
146
	 */
147
	public static function linkToAbsolute($app, $file, $args = array()) {
148
		return OC::$server->getURLGenerator()->getAbsoluteURL(
149
			self::linkTo($app, $file, $args)
150
		);
151
	}
152
153
	/**
154
	 * Makes an $url absolute
155
	 * @param string $url the url
156
	 * @return string the absolute url
157
	 *
158
	 * Returns a absolute url to the given app and file.
159
	 */
160
	public static function makeURLAbsolute($url) {
161
		return OC::$server->getURLGenerator()->getAbsoluteURL($url);
162
	}
163
164
	/**
165
	 * Creates an url for remote use
166
	 * @param string $service id
167
	 * @return string the url
168
	 *
169
	 * Returns a url to the given service.
170
	 */
171
	public static function linkToRemoteBase($service) {
172
		return self::linkTo('', 'remote.php') . '/' . $service;
173
	}
174
175
	/**
176
	 * Creates an absolute url for remote use
177
	 * @param string $service id
178
	 * @param bool $add_slash
179
	 * @return string the url
180
	 *
181
	 * Returns a absolute url to the given service.
182
	 */
183
	public static function linkToRemote($service, $add_slash = true) {
184
		return OC::$server->getURLGenerator()->getAbsoluteURL(
185
			self::linkToRemoteBase($service)
186
				. (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : '')
187
		);
188
	}
189
190
	/**
191
	 * Creates an absolute url for public use
192
	 * @param string $service id
193
	 * @param bool $add_slash
194
	 * @return string the url
195
	 *
196
	 * Returns a absolute url to the given service.
197
	 */
198
	public static function linkToPublic($service, $add_slash = false) {
199
		if ($service === 'files') {
200
			$url = OC::$server->getURLGenerator()->getAbsoluteURL('/s');
201
		} else {
202
			$url = OC::$server->getURLGenerator()->getAbsoluteURL(self::linkTo('', 'public.php').'?service='.$service);
203
		}
204
		return $url . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : '');
205
	}
206
207
	/**
208
	 * Creates path to an image
209
	 * @param string $app app
210
	 * @param string $image image name
211
	 * @return string the url
212
	 *
213
	 * Returns the path to the image.
214
	 */
215
	public static function imagePath($app, $image) {
216
		return OC::$server->getURLGenerator()->imagePath($app, $image);
217
	}
218
219
	/**
220
	 * get path to icon of file type
221
	 * @param string $mimetype mimetype
222
	 * @return string the url
223
	 *
224
	 * Returns the path to the image of this file type.
225
	 */
226
	public static function mimetypeIcon($mimetype) {
227
228
		if (isset(self::$mimeTypeAlias[$mimetype])) {
229
			$mimetype = self::$mimeTypeAlias[$mimetype];
230
		}
231
		if (isset(self::$mimetypeIcons[$mimetype])) {
232
			return self::$mimetypeIcons[$mimetype];
233
		}
234
		// Replace slash and backslash with a minus
235
		$icon = str_replace('/', '-', $mimetype);
236
		$icon = str_replace('\\', '-', $icon);
237
238
		// Is it a dir?
239 View Code Duplication
		if ($mimetype === 'dir') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
240
			self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder.png';
241
			return OC::$WEBROOT . '/core/img/filetypes/folder.png';
242
		}
243 View Code Duplication
		if ($mimetype === 'dir-shared') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
244
			self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder-shared.png';
245
			return OC::$WEBROOT . '/core/img/filetypes/folder-shared.png';
246
		}
247 View Code Duplication
		if ($mimetype === 'dir-external') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
			self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/folder-external.png';
249
			return OC::$WEBROOT . '/core/img/filetypes/folder-external.png';
250
		}
251
252
		// Icon exists?
253
		if (file_exists(OC::$SERVERROOT . '/core/img/filetypes/' . $icon . '.png')) {
254
			self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/' . $icon . '.png';
255
			return OC::$WEBROOT . '/core/img/filetypes/' . $icon . '.png';
256
		}
257
258
		// Try only the first part of the filetype
259
		$mimePart = substr($icon, 0, strpos($icon, '-'));
260
		if (file_exists(OC::$SERVERROOT . '/core/img/filetypes/' . $mimePart . '.png')) {
261
			self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/' . $mimePart . '.png';
262
			return OC::$WEBROOT . '/core/img/filetypes/' . $mimePart . '.png';
263
		} else {
264
			self::$mimetypeIcons[$mimetype] = OC::$WEBROOT . '/core/img/filetypes/file.png';
265
			return OC::$WEBROOT . '/core/img/filetypes/file.png';
266
		}
267
	}
268
269
	/**
270
	 * get path to preview of file
271
	 * @param string $path path
272
	 * @return string the url
273
	 *
274
	 * Returns the path to the preview of the file.
275
	 */
276
	public static function previewIcon($path) {
277
		return self::linkToRoute( 'core_ajax_preview', array('x' => 36, 'y' => 36, 'file' => $path ));
278
	}
279
280
	public static function publicPreviewIcon( $path, $token ) {
281
		return self::linkToRoute( 'core_ajax_public_preview', array('x' => 36, 'y' => 36, 'file' => $path, 't' => $token));
282
	}
283
284
	/**
285
	 * shows whether the user has an avatar
286
	 * @param string $user username
287
	 * @return bool avatar set or not
288
	**/
289
	public static function userAvatarSet($user) {
290
		$avatar = new \OC_Avatar($user);
291
		$image = $avatar->get(1);
292
		if ($image instanceof \OC_Image) {
293
			return true;
294
		} else {
295
			return false;
296
		}
297
	}
298
299
	/**
300
	 * Make a human file size
301
	 * @param int $bytes file size in bytes
302
	 * @return string a human readable file size
303
	 *
304
	 * Makes 2048 to 2 kB.
305
	 */
306
	public static function humanFileSize($bytes) {
307
		if ($bytes < 0) {
308
			return "?";
309
		}
310
		if ($bytes < 1024) {
311
			return "$bytes B";
312
		}
313
		$bytes = round($bytes / 1024, 0);
314
		if ($bytes < 1024) {
315
			return "$bytes kB";
316
		}
317
		$bytes = round($bytes / 1024, 1);
318
		if ($bytes < 1024) {
319
			return "$bytes MB";
320
		}
321
		$bytes = round($bytes / 1024, 1);
322
		if ($bytes < 1024) {
323
			return "$bytes GB";
324
		}
325
		$bytes = round($bytes / 1024, 1);
326
		if ($bytes < 1024) {
327
			return "$bytes TB";
328
		}
329
330
		$bytes = round($bytes / 1024, 1);
331
		return "$bytes PB";
332
	}
333
334
	/**
335
	 * Make a php file size
336
	 * @param int $bytes file size in bytes
337
	 * @return string a php parseable file size
338
	 *
339
	 * Makes 2048 to 2k and 2^41 to 2048G
340
	 */
341
	public static function phpFileSize($bytes) {
342
		if ($bytes < 0) {
343
			return "?";
344
		}
345
		if ($bytes < 1024) {
346
			return $bytes . "B";
347
		}
348
		$bytes = round($bytes / 1024, 1);
349
		if ($bytes < 1024) {
350
			return $bytes . "K";
351
		}
352
		$bytes = round($bytes / 1024, 1);
353
		if ($bytes < 1024) {
354
			return $bytes . "M";
355
		}
356
		$bytes = round($bytes / 1024, 1);
357
		return $bytes . "G";
358
	}
359
360
	/**
361
	 * Make a computer file size
362
	 * @param string $str file size in human readable format
363
	 * @return int a file size in bytes
364
	 *
365
	 * Makes 2kB to 2048.
366
	 *
367
	 * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418
368
	 */
369
	public static function computerFileSize($str) {
370
		$str = strtolower($str);
371
372
		$bytes_array = array(
373
			'b' => 1,
374
			'k' => 1024,
375
			'kb' => 1024,
376
			'mb' => 1024 * 1024,
377
			'm' => 1024 * 1024,
378
			'gb' => 1024 * 1024 * 1024,
379
			'g' => 1024 * 1024 * 1024,
380
			'tb' => 1024 * 1024 * 1024 * 1024,
381
			't' => 1024 * 1024 * 1024 * 1024,
382
			'pb' => 1024 * 1024 * 1024 * 1024 * 1024,
383
			'p' => 1024 * 1024 * 1024 * 1024 * 1024,
384
		);
385
386
		$bytes = floatval($str);
387
388
		if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
389
			$bytes *= $bytes_array[$matches[1]];
390
		}
391
392
		$bytes = round($bytes);
393
394
		return $bytes;
395
	}
396
397
	/**
398
	 * Recursive copying of folders
399
	 * @param string $src source folder
400
	 * @param string $dest target folder
401
	 *
402
	 */
403
	static function copyr($src, $dest) {
404
		if (is_dir($src)) {
405
			if (!is_dir($dest)) {
406
				mkdir($dest);
407
			}
408
			$files = scandir($src);
409
			foreach ($files as $file) {
410
				if ($file != "." && $file != "..") {
411
					self::copyr("$src/$file", "$dest/$file");
412
				}
413
			}
414
		} elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) {
415
			copy($src, $dest);
416
		}
417
	}
418
419
	/**
420
	 * Recursive deletion of folders
421
	 * @param string $dir path to the folder
422
	 * @param bool $deleteSelf if set to false only the content of the folder will be deleted
423
	 * @return bool
424
	 */
425
	static function rmdirr($dir, $deleteSelf = true) {
426
		if (is_dir($dir)) {
427
			$files = new RecursiveIteratorIterator(
428
				new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
429
				RecursiveIteratorIterator::CHILD_FIRST
430
			);
431
432
			foreach ($files as $fileInfo) {
433
				/** @var SplFileInfo $fileInfo */
434
				if ($fileInfo->isDir()) {
435
					rmdir($fileInfo->getRealPath());
436
				} else {
437
					unlink($fileInfo->getRealPath());
438
				}
439
			}
440
			if ($deleteSelf) {
441
				rmdir($dir);
442
			}
443
		} elseif (file_exists($dir)) {
444
			if ($deleteSelf) {
445
				unlink($dir);
446
			}
447
		}
448
		if (!$deleteSelf) {
449
			return true;
450
		}
451
452
		return !file_exists($dir);
453
	}
454
455
	/**
456
	 * @return \OC\Files\Type\Detection
457
	 */
458
	static public function getMimetypeDetector() {
459
		if (!self::$mimetypeDetector) {
460
			self::$mimetypeDetector = new \OC\Files\Type\Detection();
461
			self::$mimetypeDetector->registerTypeArray(include 'mimetypes.list.php');
462
		}
463
		return self::$mimetypeDetector;
464
	}
465
466
	/**
467
	 * @return \OC\Files\Type\TemplateManager
468
	 */
469
	static public function getFileTemplateManager() {
470
		if (!self::$templateManager) {
471
			self::$templateManager = new \OC\Files\Type\TemplateManager();
472
		}
473
		return self::$templateManager;
474
	}
475
476
	/**
477
	 * Try to guess the mimetype based on filename
478
	 *
479
	 * @param string $path
480
	 * @return string
481
	 */
482
	static public function getFileNameMimeType($path) {
483
		return self::getMimetypeDetector()->detectPath($path);
484
	}
485
486
	/**
487
	 * get the mimetype form a local file
488
	 *
489
	 * @param string $path
490
	 * @return string
491
	 * does NOT work for ownClouds filesystem, use OC_FileSystem::getMimeType instead
492
	 */
493
	static function getMimeType($path) {
494
		return self::getMimetypeDetector()->detect($path);
495
	}
496
497
	/**
498
	 * Get a secure mimetype that won't expose potential XSS.
499
	 *
500
	 * @param string $mimeType
501
	 * @return string
502
	 */
503
	static function getSecureMimeType($mimeType) {
504
		return self::getMimetypeDetector()->getSecureMimeType($mimeType);
505
	}
506
507
	/**
508
	 * get the mimetype form a data string
509
	 *
510
	 * @param string $data
511
	 * @return string
512
	 */
513
	static function getStringMimeType($data) {
514
		return self::getMimetypeDetector()->detectString($data);
515
	}
516
517
	/**
518
	 * Checks $_REQUEST contains a var for the $s key. If so, returns the html-escaped value of this var; otherwise returns the default value provided by $d.
519
	 * @param string $s name of the var to escape, if set.
520
	 * @param string $d default value.
521
	 * @return string the print-safe value.
522
	 *
523
	 */
524
525
	/**
526
	 * detect if a given program is found in the search PATH
527
	 *
528
	 * @param string $name
529
	 * @param bool $path
530
	 * @internal param string $program name
531
	 * @internal param string $optional search path, defaults to $PATH
532
	 * @return bool    true if executable program found in path
533
	 */
534
	public static function canExecute($name, $path = false) {
535
		// path defaults to PATH from environment if not set
536
		if ($path === false) {
537
			$path = getenv("PATH");
538
		}
539
		// check method depends on operating system
540
		if (!strncmp(PHP_OS, "WIN", 3)) {
541
			// on Windows an appropriate COM or EXE file needs to exist
542
			$exts = array(".exe", ".com");
543
			$check_fn = "file_exists";
544
		} else {
545
			// anywhere else we look for an executable file of that name
546
			$exts = array("");
547
			$check_fn = "is_executable";
548
		}
549
		// Default check will be done with $path directories :
550
		$dirs = explode(PATH_SEPARATOR, $path);
551
		// WARNING : We have to check if open_basedir is enabled :
552
		$obd = ini_get('open_basedir');
553
		if ($obd != "none") {
554
			$obd_values = explode(PATH_SEPARATOR, $obd);
555
			if (count($obd_values) > 0 and $obd_values[0]) {
556
				// open_basedir is in effect !
557
				// We need to check if the program is in one of these dirs :
558
				$dirs = $obd_values;
559
			}
560
		}
561
		foreach ($dirs as $dir) {
562
			foreach ($exts as $ext) {
563
				if ($check_fn("$dir/$name" . $ext))
564
					return true;
565
			}
566
		}
567
		return false;
568
	}
569
570
	/**
571
	 * copy the contents of one stream to another
572
	 *
573
	 * @param resource $source
574
	 * @param resource $target
575
	 * @return array the number of bytes copied and result
576
	 */
577
	public static function streamCopy($source, $target) {
578
		if (!$source or !$target) {
579
			return array(0, false);
580
		}
581
		$bufSize = 8192;
582
		$result = true;
583
		$count = 0;
584
		while (!feof($source)) {
585
			$buf = fread($source, $bufSize);
586
			$bytesWritten = fwrite($target, $buf);
587
			if ($bytesWritten !== false) {
588
				$count += $bytesWritten;
589
			}
590
			// note: strlen is expensive so only use it when necessary,
591
			// on the last block
592
			if ($bytesWritten === false
593
				|| ($bytesWritten < $bufSize && $bytesWritten < strlen($buf))
594
			) {
595
				// write error, could be disk full ?
596
				$result = false;
597
				break;
598
			}
599
		}
600
		return array($count, $result);
601
	}
602
603
	/**
604
	 * create a temporary file with an unique filename
605
	 *
606
	 * @param string $postfix
607
	 * @return string
608
	 * @deprecated Use the TempManager instead
609
	 *
610
	 * temporary files are automatically cleaned up after the script is finished
611
	 */
612
	public static function tmpFile($postfix = '') {
613
		return \OC::$server->getTempManager()->getTemporaryFile($postfix);
614
	}
615
616
	/**
617
	 * create a temporary folder with an unique filename
618
	 *
619
	 * @return string
620
	 * @deprecated Use the TempManager instead
621
	 *
622
	 * temporary files are automatically cleaned up after the script is finished
623
	 */
624
	public static function tmpFolder() {
625
		return \OC::$server->getTempManager()->getTemporaryFolder();
626
	}
627
628
	/**
629
	 * Adds a suffix to the name in case the file exists
630
	 *
631
	 * @param string $path
632
	 * @param string $filename
633
	 * @return string
634
	 */
635
	public static function buildNotExistingFileName($path, $filename) {
636
		$view = \OC\Files\Filesystem::getView();
637
		return self::buildNotExistingFileNameForView($path, $filename, $view);
638
	}
639
640
	/**
641
	 * Adds a suffix to the name in case the file exists
642
	 *
643
	 * @param string $path
644
	 * @param string $filename
645
	 * @return string
646
	 */
647
	public static function buildNotExistingFileNameForView($path, $filename, \OC\Files\View $view) {
648
		if ($path === '/') {
649
			$path = '';
650
		}
651
		if ($pos = strrpos($filename, '.')) {
652
			$name = substr($filename, 0, $pos);
653
			$ext = substr($filename, $pos);
654
		} else {
655
			$name = $filename;
656
			$ext = '';
657
		}
658
659
		$newpath = $path . '/' . $filename;
660
		if ($view->file_exists($newpath)) {
661
			if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
662
				//Replace the last "(number)" with "(number+1)"
663
				$last_match = count($matches[0]) - 1;
664
				$counter = $matches[1][$last_match][0] + 1;
665
				$offset = $matches[0][$last_match][1];
666
				$match_length = strlen($matches[0][$last_match][0]);
667
			} else {
668
				$counter = 2;
669
				$offset = false;
670
			}
671
			do {
672
				if ($offset) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $offset of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
673
					//Replace the last "(number)" with "(number+1)"
674
					$newname = substr_replace($name, '(' . $counter . ')', $offset, $match_length);
0 ignored issues
show
Bug introduced by
The variable $match_length 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...
675
				} else {
676
					$newname = $name . ' (' . $counter . ')';
677
				}
678
				$newpath = $path . '/' . $newname . $ext;
679
				$counter++;
680
			} while ($view->file_exists($newpath));
681
		}
682
683
		return $newpath;
684
	}
685
686
	/**
687
	 * Checks if $sub is a subdirectory of $parent
688
	 *
689
	 * @param string $sub
690
	 * @param string $parent
691
	 * @return bool
692
	 */
693
	public static function isSubDirectory($sub, $parent) {
694
		$realpathSub = realpath($sub);
695
		$realpathParent = realpath($parent);
696
697
		// realpath() may return false in case the directory does not exist
698
		// since we can not be sure how different PHP versions may behave here
699
		// we do an additional check whether realpath returned false
700
		if($realpathSub === false ||  $realpathParent === false) {
701
			return false;
702
		}
703
704
		// Check whether $sub is a subdirectory of $parent
705
		if (strpos($realpathSub, $realpathParent) === 0) {
706
			return true;
707
		}
708
709
		return false;
710
	}
711
712
	/**
713
	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
714
	 *
715
	 * @param array $input The array to work on
716
	 * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default)
717
	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
718
	 * @return array
719
	 *
720
	 * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is.
721
	 * based on http://www.php.net/manual/en/function.array-change-key-case.php#107715
722
	 *
723
	 */
724
	public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') {
725
		$case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER;
726
		$ret = array();
727
		foreach ($input as $k => $v) {
728
			$ret[mb_convert_case($k, $case, $encoding)] = $v;
729
		}
730
		return $ret;
731
	}
732
733
	/**
734
	 * replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement.
735
	 *
736
	 * @param string $string
737
	 * @param string $replacement The replacement string.
738
	 * @param int $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string.
739
	 * @param int $length Length of the part to be replaced
740
	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
741
	 * @internal param string $input The input string. .Opposite to the PHP build-in function does not accept an array.
742
	 * @return string
743
	 */
744
	public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') {
745
		$start = intval($start);
746
		$length = intval($length);
747
		$string = mb_substr($string, 0, $start, $encoding) .
748
			$replacement .
749
			mb_substr($string, $start + $length, mb_strlen($string, 'UTF-8') - $start, $encoding);
750
751
		return $string;
752
	}
753
754
	/**
755
	 * Replace all occurrences of the search string with the replacement string
756
	 *
757
	 * @param string $search The value being searched for, otherwise known as the needle.
758
	 * @param string $replace The replacement
759
	 * @param string $subject The string or array being searched and replaced on, otherwise known as the haystack.
760
	 * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8
761
	 * @param int $count If passed, this will be set to the number of replacements performed.
762
	 * @return string
763
	 *
764
	 */
765
	public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) {
766
		$offset = -1;
767
		$length = mb_strlen($search, $encoding);
768
		while (($i = mb_strrpos($subject, $search, $offset, $encoding)) !== false) {
769
			$subject = OC_Helper::mb_substr_replace($subject, $replace, $i, $length);
770
			$offset = $i - mb_strlen($subject, $encoding);
771
			$count++;
772
		}
773
		return $subject;
774
	}
775
776
	/**
777
	 * performs a search in a nested array
778
	 * @param array $haystack the array to be searched
779
	 * @param string $needle the search string
780
	 * @param string $index optional, only search this key name
781
	 * @return mixed the key of the matching field, otherwise false
782
	 *
783
	 * performs a search in a nested array
784
	 *
785
	 * taken from http://www.php.net/manual/en/function.array-search.php#97645
786
	 */
787
	public static function recursiveArraySearch($haystack, $needle, $index = null) {
788
		$aIt = new RecursiveArrayIterator($haystack);
789
		$it = new RecursiveIteratorIterator($aIt);
790
791
		while ($it->valid()) {
792
			if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) {
793
				return $aIt->key();
794
			}
795
796
			$it->next();
797
		}
798
799
		return false;
800
	}
801
802
	/**
803
	 * Shortens str to maxlen by replacing characters in the middle with '...', eg.
804
	 * ellipsis('a very long string with lots of useless info to make a better example', 14) becomes 'a very ...example'
805
	 *
806
	 * @param string $str the string
807
	 * @param string $maxlen the maximum length of the result
808
	 * @return string with at most maxlen characters
809
	 */
810
	public static function ellipsis($str, $maxlen) {
811
		if (strlen($str) > $maxlen) {
812
			$characters = floor($maxlen / 2);
813
			return substr($str, 0, $characters) . '...' . substr($str, -1 * $characters);
814
		}
815
		return $str;
816
	}
817
818
	/**
819
	 * calculates the maximum upload size respecting system settings, free space and user quota
820
	 *
821
	 * @param string $dir the current folder where the user currently operates
822
	 * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly
823
	 * @return int number of bytes representing
824
	 */
825
	public static function maxUploadFilesize($dir, $freeSpace = null) {
826
		if (is_null($freeSpace) || $freeSpace < 0){
827
			$freeSpace = self::freeSpace($dir);
828
		}
829
		return min($freeSpace, self::uploadLimit());
830
	}
831
832
	/**
833
	 * Calculate free space left within user quota
834
	 *
835
	 * @param string $dir the current folder where the user currently operates
836
	 * @return int number of bytes representing
837
	 */
838
	public static function freeSpace($dir) {
839
		$freeSpace = \OC\Files\Filesystem::free_space($dir);
840
		if ($freeSpace !== \OCP\Files\FileInfo::SPACE_UNKNOWN) {
841
			$freeSpace = max($freeSpace, 0);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression max($freeSpace, 0); of type false|string|null|integer adds false to the return on line 842 which is incompatible with the return type documented by OC_Helper::freeSpace of type integer. It seems like you forgot to handle an error condition.
Loading history...
842
			return $freeSpace;
843
		} else {
844
			return INF;
845
		}
846
	}
847
848
	/**
849
	 * Calculate PHP upload limit
850
	 *
851
	 * @return PHP upload file size limit
852
	 */
853
	public static function uploadLimit() {
854
		$upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize'));
855
		$post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size'));
856
		if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) {
857
			return INF;
858
		} elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) {
859
			return max($upload_max_filesize, $post_max_size); //only the non 0 value counts
0 ignored issues
show
Bug Best Practice introduced by
The return type of return max($upload_max_filesize, $post_max_size); (double) is incompatible with the return type documented by OC_Helper::uploadLimit of type PHP.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
860
		} else {
861
			return min($upload_max_filesize, $post_max_size);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return min($upload_max_filesize, $post_max_size); (double) is incompatible with the return type documented by OC_Helper::uploadLimit of type PHP.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
862
		}
863
	}
864
865
	/**
866
	 * Checks if a function is available
867
	 *
868
	 * @param string $function_name
869
	 * @return bool
870
	 */
871
	public static function is_function_enabled($function_name) {
872
		if (!function_exists($function_name)) {
873
			return false;
874
		}
875
		$disabled = explode(',', ini_get('disable_functions'));
876
		$disabled = array_map('trim', $disabled);
877
		if (in_array($function_name, $disabled)) {
878
			return false;
879
		}
880
		$disabled = explode(',', ini_get('suhosin.executor.func.blacklist'));
881
		$disabled = array_map('trim', $disabled);
882
		if (in_array($function_name, $disabled)) {
883
			return false;
884
		}
885
		return true;
886
	}
887
888
	/**
889
	 * Try to find a program
890
	 * Note: currently windows is not supported
891
	 *
892
	 * @param string $program
893
	 * @return null|string
894
	 */
895
	public static function findBinaryPath($program) {
896
		$memcache = \OC::$server->getMemCacheFactory()->create('findBinaryPath');
897
		if ($memcache->hasKey($program)) {
898
			return $memcache->get($program);
899
		}
900
		$result = null;
901
		if (!\OC_Util::runningOnWindows() && self::is_function_enabled('exec')) {
902
			exec('command -v ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode);
903
			if ($returnCode === 0 && count($output) > 0) {
904
				$result = escapeshellcmd($output[0]);
905
			}
906
		}
907
		$memcache->set($program, $result, 3600);
908
		return $result;
909
	}
910
911
	/**
912
	 * Calculate the disc space for the given path
913
	 *
914
	 * @param string $path
915
	 * @param \OCP\Files\FileInfo $rootInfo (optional)
916
	 * @return array
917
	 */
918
	public static function getStorageInfo($path, $rootInfo = null) {
919
		// return storage info without adding mount points
920
		$includeExtStorage = \OC_Config::getValue('quota_include_external_storage', false);
921
922
		if (!$rootInfo) {
923
			$rootInfo = \OC\Files\Filesystem::getFileInfo($path, false);
924
		}
925
		if (!$rootInfo instanceof \OCP\Files\FileInfo) {
926
			throw new \OCP\Files\NotFoundException();
927
		}
928
		$used = $rootInfo->getSize();
929
		if ($used < 0) {
930
			$used = 0;
931
		}
932
		$quota = 0;
933
		$storage = $rootInfo->getStorage();
934
		if ($includeExtStorage && $storage->instanceOfStorage('\OC\Files\Storage\Shared')) {
935
			$includeExtStorage = false;
936
		}
937
		if ($includeExtStorage) {
938
			$quota = OC_Util::getUserQuota(\OCP\User::getUser());
939
			if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
940
				// always get free space / total space from root + mount points
941
				$path = '';
0 ignored issues
show
Unused Code introduced by
$path is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
942
				return self::getGlobalStorageInfo();
943
			}
944
		}
945
946
		// TODO: need a better way to get total space from storage
947
		if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota')) {
948
			$quota = $storage->getQuota();
949
		}
950
		$free = $storage->free_space('');
951
		if ($free >= 0) {
952
			$total = $free + $used;
953
		} else {
954
			$total = $free; //either unknown or unlimited
955
		}
956 View Code Duplication
		if ($total > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
957
			if ($quota > 0 && $total > $quota) {
958
				$total = $quota;
959
			}
960
			// prevent division by zero or error codes (negative values)
961
			$relative = round(($used / $total) * 10000) / 100;
962
		} else {
963
			$relative = 0;
964
		}
965
966
		return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative);
967
	}
968
969
	/**
970
	 * Get storage info including all mount points and quota
971
	 *
972
	 * @return array
973
	 */
974
	private static function getGlobalStorageInfo() {
975
		$quota = OC_Util::getUserQuota(\OCP\User::getUser());
976
977
		$rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext');
0 ignored issues
show
Documentation introduced by
'ext' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
978
		$used = $rootInfo['size'];
979
		if ($used < 0) {
980
			$used = 0;
981
		}
982
983
		$total = $quota;
984
		$free = $quota - $used;
985
986 View Code Duplication
		if ($total > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
987
			if ($quota > 0 && $total > $quota) {
988
				$total = $quota;
989
			}
990
			// prevent division by zero or error codes (negative values)
991
			$relative = round(($used / $total) * 10000) / 100;
992
		} else {
993
			$relative = 0;
994
		}
995
996
		return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative);
997
998
	}
999
1000
	/**
1001
	 * Returns whether the config file is set manually to read-only
1002
	 * @return bool
1003
	 */
1004
	public static function isReadOnlyConfigEnabled() {
1005
		return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false);
1006
	}
1007
}
1008