aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--application/Utils.php89
-rw-r--r--index.php32
-rw-r--r--tests/UtilsTest.php80
-rw-r--r--tpl/vintage/import.html2
4 files changed, 172 insertions, 31 deletions
diff --git a/application/Utils.php b/application/Utils.php
index d6e06610..3ef2a7e2 100644
--- a/application/Utils.php
+++ b/application/Utils.php
@@ -345,3 +345,92 @@ function format_date($date, $time = true, $intl = true)
345 345
346 return $formatter->format($date); 346 return $formatter->format($date);
347} 347}
348
349/**
350 * Check if the input is an integer, no matter its real type.
351 *
352 * PHP is a bit messy regarding this:
353 * - is_int returns false if the input is a string
354 * - ctype_digit returns false if the input is an integer or negative
355 *
356 * @param mixed $input value
357 *
358 * @return bool true if the input is an integer, false otherwise
359 */
360function is_integer_mixed($input)
361{
362 if (is_array($input) || is_bool($input) || is_object($input)) {
363 return false;
364 }
365 $input = strval($input);
366 return ctype_digit($input) || (startsWith($input, '-') && ctype_digit(substr($input, 1)));
367}
368
369/**
370 * Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes.
371 *
372 * @param string $val Size expressed in string.
373 *
374 * @return int Size expressed in bytes.
375 */
376function return_bytes($val)
377{
378 if (is_integer_mixed($val) || $val === '0' || empty($val)) {
379 return $val;
380 }
381 $val = trim($val);
382 $last = strtolower($val[strlen($val)-1]);
383 $val = intval(substr($val, 0, -1));
384 switch($last) {
385 case 'g': $val *= 1024;
386 case 'm': $val *= 1024;
387 case 'k': $val *= 1024;
388 }
389 return $val;
390}
391
392/**
393 * Return a human readable size from bytes.
394 *
395 * @param int $bytes value
396 *
397 * @return string Human readable size
398 */
399function human_bytes($bytes)
400{
401 if ($bytes === '') {
402 return t('Setting not set');
403 }
404 if (! is_integer_mixed($bytes)) {
405 return $bytes;
406 }
407 $bytes = intval($bytes);
408 if ($bytes === 0) {
409 return t('Unlimited');
410 }
411
412 $units = [t('B'), t('kiB'), t('MiB'), t('GiB')];
413 for ($i = 0; $i < count($units) && $bytes >= 1024; ++$i) {
414 $bytes /= 1024;
415 }
416
417 return $bytes . $units[$i];
418}
419
420/**
421 * Try to determine max file size for uploads (POST).
422 * Returns an integer (in bytes)
423 *
424 * @param mixed $limitPost post_max_size PHP setting
425 * @param mixed $limitUpload upload_max_filesize PHP setting
426 *
427 * @return int max upload file size in bytes.
428 */
429function get_max_upload_size($limitPost, $limitUpload)
430{
431 $size1 = return_bytes($limitPost);
432 $size2 = return_bytes($limitUpload);
433 // Return the smaller of two:
434 $maxsize = min($size1, $size2);
435 return human_bytes($maxsize);
436}
diff --git a/index.php b/index.php
index 863d5093..8f26c390 100644
--- a/index.php
+++ b/index.php
@@ -473,34 +473,6 @@ if (isset($_POST['login']))
473} 473}
474 474
475// ------------------------------------------------------------------------------------------ 475// ------------------------------------------------------------------------------------------
476// Misc utility functions:
477
478// Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes.
479function return_bytes($val)
480{
481 $val = trim($val); $last=strtolower($val[strlen($val)-1]);
482 switch($last)
483 {
484 case 'g': $val *= 1024;
485 case 'm': $val *= 1024;
486 case 'k': $val *= 1024;
487 }
488 return $val;
489}
490
491// Try to determine max file size for uploads (POST).
492// Returns an integer (in bytes)
493function getMaxFileSize()
494{
495 $size1 = return_bytes(ini_get('post_max_size'));
496 $size2 = return_bytes(ini_get('upload_max_filesize'));
497 // Return the smaller of two:
498 $maxsize = min($size1,$size2);
499 // FIXME: Then convert back to readable notations ? (e.g. 2M instead of 2000000)
500 return $maxsize;
501}
502
503// ------------------------------------------------------------------------------------------
504// Token management for XSRF protection 476// Token management for XSRF protection
505// Token should be used in any form which acts on data (create,update,delete,import...). 477// Token should be used in any form which acts on data (create,update,delete,import...).
506if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are attached to the session. 478if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are attached to the session.
@@ -1517,7 +1489,7 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1517 1489
1518 if (! isset($_POST['token']) || ! isset($_FILES['filetoupload'])) { 1490 if (! isset($_POST['token']) || ! isset($_FILES['filetoupload'])) {
1519 // Show import dialog 1491 // Show import dialog
1520 $PAGE->assign('maxfilesize', getMaxFileSize()); 1492 $PAGE->assign('maxfilesize', get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize')));
1521 $PAGE->renderPage('import'); 1493 $PAGE->renderPage('import');
1522 exit; 1494 exit;
1523 } 1495 }
@@ -1527,7 +1499,7 @@ function renderPage($conf, $pluginManager, $LINKSDB)
1527 // The file is too big or some form field may be missing. 1499 // The file is too big or some form field may be missing.
1528 echo '<script>alert("The file you are trying to upload is probably' 1500 echo '<script>alert("The file you are trying to upload is probably'
1529 .' bigger than what this webserver can accept (' 1501 .' bigger than what this webserver can accept ('
1530 .getMaxFileSize().' bytes).' 1502 .get_max_upload_size(ini_get('post_max_size'), ini_get('upload_max_filesize')).').'
1531 .' Please upload in smaller chunks.");document.location=\'?do=' 1503 .' Please upload in smaller chunks.");document.location=\'?do='
1532 .Router::$PAGE_IMPORT .'\';</script>'; 1504 .Router::$PAGE_IMPORT .'\';</script>';
1533 exit; 1505 exit;
diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php
index e70cc1ae..e5ff01e6 100644
--- a/tests/UtilsTest.php
+++ b/tests/UtilsTest.php
@@ -4,6 +4,7 @@
4 */ 4 */
5 5
6require_once 'application/Utils.php'; 6require_once 'application/Utils.php';
7require_once 'application/Languages.php';
7require_once 'tests/utils/ReferenceSessionIdHashes.php'; 8require_once 'tests/utils/ReferenceSessionIdHashes.php';
8 9
9// Initialize reference data before PHPUnit starts a session 10// Initialize reference data before PHPUnit starts a session
@@ -326,4 +327,83 @@ class UtilsTest extends PHPUnit_Framework_TestCase
326 $this->assertFalse(format_date([])); 327 $this->assertFalse(format_date([]));
327 $this->assertFalse(format_date(null)); 328 $this->assertFalse(format_date(null));
328 } 329 }
330
331 /**
332 * Test is_integer_mixed with valid values
333 */
334 public function testIsIntegerMixedValid()
335 {
336 $this->assertTrue(is_integer_mixed(12));
337 $this->assertTrue(is_integer_mixed('12'));
338 $this->assertTrue(is_integer_mixed(-12));
339 $this->assertTrue(is_integer_mixed('-12'));
340 $this->assertTrue(is_integer_mixed(0));
341 $this->assertTrue(is_integer_mixed('0'));
342 $this->assertTrue(is_integer_mixed(0x0a));
343 }
344
345 /**
346 * Test is_integer_mixed with invalid values
347 */
348 public function testIsIntegerMixedInvalid()
349 {
350 $this->assertFalse(is_integer_mixed(true));
351 $this->assertFalse(is_integer_mixed(false));
352 $this->assertFalse(is_integer_mixed([]));
353 $this->assertFalse(is_integer_mixed(['test']));
354 $this->assertFalse(is_integer_mixed([12]));
355 $this->assertFalse(is_integer_mixed(new DateTime()));
356 $this->assertFalse(is_integer_mixed('0x0a'));
357 $this->assertFalse(is_integer_mixed('12k'));
358 $this->assertFalse(is_integer_mixed('k12'));
359 $this->assertFalse(is_integer_mixed(''));
360 }
361
362 /**
363 * Test return_bytes
364 */
365 public function testReturnBytes()
366 {
367 $this->assertEquals(2 * 1024, return_bytes('2k'));
368 $this->assertEquals(2 * 1024, return_bytes('2K'));
369 $this->assertEquals(2 * (pow(1024, 2)), return_bytes('2m'));
370 $this->assertEquals(2 * (pow(1024, 2)), return_bytes('2M'));
371 $this->assertEquals(2 * (pow(1024, 3)), return_bytes('2g'));
372 $this->assertEquals(2 * (pow(1024, 3)), return_bytes('2G'));
373 $this->assertEquals(374, return_bytes('374'));
374 $this->assertEquals(374, return_bytes(374));
375 $this->assertEquals(0, return_bytes('0'));
376 $this->assertEquals(0, return_bytes(0));
377 $this->assertEquals(-1, return_bytes('-1'));
378 $this->assertEquals(-1, return_bytes(-1));
379 $this->assertEquals('', return_bytes(''));
380 }
381
382 /**
383 * Test human_bytes
384 */
385 public function testHumanBytes()
386 {
387 $this->assertEquals('2kiB', human_bytes(2 * 1024));
388 $this->assertEquals('2kiB', human_bytes(strval(2 * 1024)));
389 $this->assertEquals('2MiB', human_bytes(2 * (pow(1024, 2))));
390 $this->assertEquals('2MiB', human_bytes(strval(2 * (pow(1024, 2)))));
391 $this->assertEquals('2GiB', human_bytes(2 * (pow(1024, 3))));
392 $this->assertEquals('2GiB', human_bytes(strval(2 * (pow(1024, 3)))));
393 $this->assertEquals('374B', human_bytes(374));
394 $this->assertEquals('374B', human_bytes('374'));
395 $this->assertEquals('Unlimited', human_bytes('0'));
396 $this->assertEquals('Unlimited', human_bytes(0));
397 $this->assertEquals('Setting not set', human_bytes(''));
398 }
399
400 /**
401 * Test get_max_upload_size
402 */
403 public function testGetMaxUploadSize()
404 {
405 $this->assertEquals('1MiB', get_max_upload_size(2097152, '1024k'));
406 $this->assertEquals('1MiB', get_max_upload_size('1m', '2m'));
407 $this->assertEquals('100B', get_max_upload_size(100, 100));
408 }
329} 409}
diff --git a/tpl/vintage/import.html b/tpl/vintage/import.html
index 071e1160..bb9e4a56 100644
--- a/tpl/vintage/import.html
+++ b/tpl/vintage/import.html
@@ -5,7 +5,7 @@
5<div id="pageheader"> 5<div id="pageheader">
6 {include="page.header"} 6 {include="page.header"}
7 <div id="uploaddiv"> 7 <div id="uploaddiv">
8 Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize} bytes). 8 Import Netscape HTML bookmarks (as exported from Firefox/Chrome/Opera/Delicious/Diigo...) (Max: {$maxfilesize}).
9 <form method="POST" action="?do=import" enctype="multipart/form-data" 9 <form method="POST" action="?do=import" enctype="multipart/form-data"
10 name="uploadform" id="uploadform"> 10 name="uploadform" id="uploadform">
11 <input type="hidden" name="token" value="{$token}"> 11 <input type="hidden" name="token" value="{$token}">