/*
* Jirafeau, your web file repository
* Copyright (C) 2008 Julien "axolotl" BERNARD <axolotl@magieeternelle.org>
- * Copyright (C) 2012 Jerome Jutteau <j.jutteau@gmail.com>
+ * Copyright (C) 2015 Jerome Jutteau <j.jutteau@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
return true;
} elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
return true;
+ } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
+ if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
+ return true;
}
return false;
}
/* Crypt file if option is enabled. */
$crypted = false;
$crypt_key = '';
- if ($crypt == true && extension_loaded('mcrypt'))
+ if ($crypt == true && !(extension_loaded('mcrypt') == true))
+ error_log ("PHP extension mcrypt not loaded, won't encrypt in Jirafeau");
+ if ($crypt == true && extension_loaded('mcrypt') == true)
{
$crypt_key = jirafeau_encrypt_file ($file['tmp_name'], $file['tmp_name']);
if (strlen($crypt_key) > 0)
}
/**
- * tells if a mime-type is viewable in a browser
+ * Tells if a mime-type is viewable in a browser
* @param $mime the mime type
* @returns a boolean telling if a mime type is viewable
*/
if (!empty ($mime))
{
/* Actually, verify if mime-type is an image or a text. */
- $viewable = array ('image', 'text');
+ $viewable = array ('image', 'text', 'video', 'audio');
$decomposed = explode ('/', $mime);
return in_array ($decomposed[0], $viewable);
}
}
}
-function check_errors ()
+function check_errors ($cfg)
{
if (file_exists (JIRAFEAU_ROOT . 'install.php')
- && !file_exists (JIRAFEAU_ROOT . 'lib/config.local.php'))
+ && !($cfg['installation_done'] === true))
{
header('Location: install.php');
exit;
if (!is_writable (VAR_ASYNC))
add_error (t('The async directory is not writable!'), VAR_ASYNC);
-
- if (!is_writable (VAR_BLOCK))
- add_error (t('The block directory is not writable!'), VAR_BLOCK);
}
/**
$out['link_code'] = trim ($c[9]);
if (trim ($c[10]) == 'C')
$out['crypted'] = true;
+ else
+ $out['crypted'] = false;
return $out;
}
continue;
/* Filter. */
- if (!empty ($name) && !preg_match ("/$name/i", $l['file_name']))
+ if (!empty ($name) && !preg_match ("/$name/i", htmlspecialchars($l['file_name'])))
continue;
if (!empty ($file_hash) && $file_hash != $l['md5'])
continue;
'<form action = "admin.php" method = "post">' .
'<input type = "hidden" name = "action" value = "download"/>' .
'<input type = "hidden" name = "link" value = "' . $node . '"/>' .
- '<input type = "submit" value = "' . $l['file_name'] . '" />' .
+ '<input type = "submit" value = "' . htmlspecialchars($l['file_name']) . '" />' .
'</form>';
echo '</td>';
echo '<td>' . $l['mime_type'] . '</td>';
/**
* Delete async transfert informations
- */
+ */
function
jirafeau_async_delete ($ref)
{
* @param $ref asynchronous upload reference
* @param $file piece of data
* @param $code client code for this operation
+ * @param $max_file_size maximum allowed file size
* @return a string containing a next code to use or the string "Error"
*/
function
-jirafeau_async_push ($ref, $data, $code)
+jirafeau_async_push ($ref, $data, $code, $max_file_size)
{
/* Get async infos. */
$a = jirafeau_get_async_ref ($ref);
$p = s2p ($ref);
+ /* File path. */
+ $r_path = $data['tmp_name'];
+ $w_path = VAR_ASYNC . $p . $ref . '_data';
+
+ /* Check that file size is not above upload limit. */
+ if ($max_file_size > 0 &&
+ filesize ($r_path) + filesize ($w_path) > $max_file_size * 1024 * 1024)
+ {
+ jirafeau_async_delete ($ref);
+ return "Error";
+ }
+
/* Concatenate data. */
- $r = fopen ($data['tmp_name'], 'r');
- $w = fopen (VAR_ASYNC . $p . $ref . '_data', 'a');
+ $r = fopen ($r_path, 'r');
+ $w = fopen ($w_path, 'a');
while (!feof ($r))
{
if (fwrite ($w, fread ($r, 1024)) === false)
}
fclose ($r);
fclose ($w);
- unlink ($data['tmp_name']);
+ unlink ($r_path);
/* Update async file. */
$code = jirafeau_gen_random (4);
* @param $ref asynchronous upload reference
* @param $code client code for this operation
* @param $crypt boolean asking to crypt or not
- * @param $link_name_length link name lenght
+ * @param $link_name_length link name length
* @return a string containing the download reference followed by a delete code or the string "Error"
*/
function
$crypted = false;
$crypt_key = '';
- if ($crypt == true && extension_loaded('mcrypt'))
+ if ($crypt == true && extension_loaded('mcrypt') == true)
{
$crypt_key = jirafeau_encrypt_file ($p, $p);
if (strlen($crypt_key) > 0)
return $md5_link . NL . $delete_link_code . NL . urlencode($crypt_key);
}
-/**
- * Delete a block.
- * @param $id identifier of the block.
- */
-function
-jirafeau_block_delete_ ($id)
-{
- $p = VAR_BLOCK . s2p ($id);
- if (!file_exists ($p))
- return;
-
- if (file_exists ($p . $id))
- unlink ($p . $id);
- if (file_exists ($p . $id . '_infos'))
- unlink ($p . $id . '_infos');
- $parse = $p;
- $scan = array();
- while (file_exists ($parse)
- && ($scan = scandir ($parse))
- && count ($scan) == 2 // '.' and '..' folders => empty.
- && basename ($parse) != basename (VAR_BLOCK))
- {
- rmdir ($parse);
- $parse = substr ($parse, 0, strlen($parse) - strlen(basename ($parse)) - 1);
- }
-}
-
-/**
- * Create a file filled with zeros.
- * @param $size size of the file.
- * @return a string corresponding to an id or the string "Error"
- */
-function
-jirafeau_block_init ($size)
-{
- if (!ctype_digit ($size) || $size <= 0)
- return "Error";
-
- /* Create folder. */
- $id;
- do
- {
- $id = jirafeau_gen_random (32);
- $p = VAR_BLOCK . s2p ($id);
- } while (file_exists ($p));
- @mkdir ($p, 0755, true);
- if (!file_exists ($p))
- {
- echo "Error";
- return;
- }
-
- /* Create block. */
- $p .= $id;
- $h = fopen ($p, 'w');
- $fill = str_repeat ("\0", 1024);
- for ($cnt = 0; $cnt < $size; $cnt += 1024)
- {
- if ($size - $cnt < 1024)
- $fill = str_repeat ("\0", $size - $cnt);
- if (fwrite ($h, $fill) === false)
- {
- fclose ($h);
- jirafeau_block_delete_ ($id);
- return "Error";
- }
- }
- fclose ($h);
-
- /* Generate a write/delete code. */
- $code = jirafeau_gen_random (12);
-
- /* Add block infos. */
- if (file_put_contents ($p . '_infos', date ('U') . NL . $size . NL . $code) === FALSE)
- {
- jirafeau_block_delete_ ($id);
- return "Error";
- }
-
- return $id . NL . $code;
-}
-
-/** Get block size in bytes.
- * @param $id identifier of the block
- * @return block size in bytes
- */
-function
-jirafeau_block_get_size ($id)
-{
- $p = VAR_BLOCK . s2p ($id) . $id;
- if (!file_exists ($p))
- return "Error";
-
- /* Check date. */
- $f = file ($p . '_infos');
- $date = trim ($f[0]);
- $block_size = trim ($f[1]);
- $stored_code = trim ($f[2]);
- /* Update date. */
- if (date ('U') - $date > JIRAFEAU_HOUR
- && date ('U') - $date < JIRAFEAU_MONTH)
- {
- if (file_put_contents ($p . '_infos', date ('U') . NL . $block_size . NL . $stored_code) === FALSE)
- {
- jirafeau_block_delete_ ($id);
- return "Error";
- }
- }
- /* Remove data. */
- elseif (date ('U') - $date >= JIRAFEAU_MONTH)
- {
- echo date ('U'). " $date ";
- jirafeau_block_delete_ ($id);
- return "Error";
- }
-
- return $block_size;
-}
-
-/**
- * Read some data in a block.
- * @param $id identifier of the block
- * @param $start where to read data (starting from zero).
- * @param $length length to read.
- * @return echo data
- */
-function
-jirafeau_block_read ($id, $start, $length)
-{
- if (!ctype_digit ($start) || $start < 0
- || !ctype_digit ($length) || $length <= 0)
- {
- echo "Error";
- return;
- }
-
- $p = VAR_BLOCK . s2p ($id) . $id;
- if (!file_exists ($p))
- {
- echo "Error";
- return;
- }
-
- /* Check date. */
- $f = file ($p . '_infos');
- $date = trim ($f[0]);
- $block_size = trim ($f[1]);
- $stored_code = trim ($f[2]);
- /* Update date. */
- if (date ('U') - $date > JIRAFEAU_HOUR
- && date ('U') - $date < JIRAFEAU_MONTH)
- {
- if (file_put_contents ($p . '_infos', date ('U') . NL . $block_size . NL . $stored_code) === FALSE)
- {
- jirafeau_block_delete_ ($id);
- echo "Error";
- return;
- }
- }
- /* Remove data. */
- elseif (date ('U') - $date >= JIRAFEAU_MONTH)
- {
- echo date ('U'). " $date ";
- jirafeau_block_delete_ ($id);
- echo "Error";
- return;
- }
-
- if ($start + $length > $block_size)
- {
- echo "Error";
- return;
- }
-
- /* Read content. */
- header ('Content-Length: ' . $length);
- header ('Content-Disposition: attachment');
-
- $r = fopen ($p, 'r');
- if (fseek ($r, $start) != 0)
- {
- echo "Error";
- return;
- }
- $c = 1024;
- for ($cnt = 0; $cnt < $length && !feof ($r); $cnt += 1024)
- {
- if ($length - $cnt < 1024)
- $c = $length - $cnt;
- print fread ($r, $c);
- ob_flush();
- }
- fclose ($r);
-}
-
-/**
- * Write some data in a block.
- * @param $id identifier of the block
- * @param $start where to writing data (starting from zero).
- * @param $data data to write.
- * @param $code code to allow writing.
- * @return string "Ok" or string "Error".
- */
-function
-jirafeau_block_write ($id, $start, $data, $code)
-{
- if (!ctype_digit ($start) || $start < 0
- || strlen ($code) == 0)
- return "Error";
-
- $p = VAR_BLOCK . s2p ($id) . $id;
- if (!file_exists ($p))
- return "Error";
-
- /* Check date. */
- $f = file ($p . '_infos');
- $date = trim ($f[0]);
- $block_size = trim ($f[1]);
- $stored_code = trim ($f[2]);
- /* Update date. */
- if (date ('U') - $date > JIRAFEAU_HOUR
- && date ('U') - $date < JIRAFEAU_MONTH)
- {
- if (file_put_contents ($p . '_infos', date ('U') . NL . $block_size . NL . $stored_code) === FALSE)
- {
- jirafeau_block_delete_ ($id);
- return "Error";
- }
- }
- /* Remove data. */
- elseif (date ('U') - $date >= JIRAFEAU_MONTH)
- {
- jirafeau_block_delete_ ($id);
- return "Error";
- }
-
- /* Check code. */
- if ($stored_code != $code)
- {
- echo "Error";
- return;
- }
-
- /* Check data. */
- $size = $data['size'];
- if ($size <= 0)
- return "Error";
- if ($start + $size > $block_size)
- return "Error";
-
- /* Open data. */
- $r = fopen ($data['tmp_name'], 'r');
-
- /* Open Block. */
- $w = fopen ($p, 'r+');
- if (fseek ($w, $start) != 0)
- return "Error";
-
- /* Write content. */
- $c = 1024;
- for ($cnt = 0; $cnt <= $size && !feof ($w); $cnt += 1024)
- {
- if ($size - $cnt < 1024)
- $c = $size - $cnt;
- $d = fread ($r, $c);
- fwrite ($w, $d);
- }
- fclose ($r);
- fclose ($w);
- unlink ($data['tmp_name']);
- return "Ok";
-}
-
-/**
- * Delete a block.
- * @param $id identifier of the block.
- * @param $code code to allow writing.
- * @return string "Ok" or string "Error".
- */
-function
-jirafeau_block_delete ($id, $code)
-{
- $p = VAR_BLOCK . s2p ($id) . $id;
-
- if (!file_exists ($p))
- return "Error";
-
- $f = file ($p . '_infos');
- $date = trim ($f[0]);
- $block_size = trim ($f[1]);
- $stored_code = trim ($f[2]);
-
- if ($code != $stored_code)
- return "Error";
-
- jirafeau_block_delete_ ($id);
- return "Ok";
-}
-
-/**
- * Clean old unused blocks.
- * @return number of cleaned blocks.
- */
-function
-jirafeau_admin_clean_block ()
-{
- $count = 0;
- /* Get all blocks. */
- $stack = array (VAR_BLOCK);
- while (($d = array_shift ($stack)) && $d != NULL)
- {
- $dir = scandir ($d);
-
- foreach ($dir as $node)
- {
- if (strcmp ($node, '.') == 0 || strcmp ($node, '..') == 0)
- continue;
-
- if (is_dir ($d . $node))
- {
- /* Push new found directory. */
- $stack[] = $d . $node . '/';
- }
- elseif (is_file ($d . $node) && preg_match ('/\_infos/i', "$node"))
- {
- /* Read block informations. */
- $f = file ($d . $node);
- $date = trim ($f[0]);
- $block_size = trim ($f[1]);
- if (date ('U') - $date >= JIRAFEAU_MONTH)
- {
- jirafeau_block_delete_ (substr($node, 0, -6));
- $count++;
- }
- }
- }
- }
- return $count;
-}
-
function
jirafeau_crypt_create_iv($base, $size)
{
jirafeau_encrypt_file ($fp_src, $fp_dst)
{
$fs = filesize ($fp_src);
- if ($fs === false || $fs == 0 || !extension_loaded('mcrypt'))
+ if ($fs === false || $fs == 0 || !(extension_loaded('mcrypt') == true))
return '';
/* Prepare module. */
jirafeau_decrypt_file ($fp_src, $fp_dst, $k)
{
$fs = filesize ($fp_src);
- if ($fs === false || $fs == 0 || !extension_loaded('mcrypt'))
+ if ($fs === false || $fs == 0 || !(extension_loaded('mcrypt') == true))
return false;
/* Init module */
return true;
}
-?>
+/**
+ * Check if Jirafeau is password protected for visitors.
+ * @return true if Jirafeau is password protected, false otherwise.
+ */
+function jirafeau_has_upload_password ($cfg)
+{
+ return count ($cfg['upload_password']) > 0;
+}
+
+/**
+ * Challenge password for a visitor.
+ * @param $password password to be challenged
+ * @return true if password is valid, false otherwise.
+ */
+function jirafeau_challenge_upload_password ($cfg, $password)
+{
+ if (!jirafeau_has_upload_password($cfg))
+ return false;
+ forEach ($cfg['upload_password'] as $p)
+ if ($password == $p)
+ return true;
+ return false;
+}
+
+/**
+ * Test if visitor's IP is authorized to upload.
+ * @param $ip IP to be challenged
+ * @return true if IP is authorized, false otherwise.
+ */
+function jirafeau_challenge_upload_ip ($cfg, $ip)
+{
+ if (count ($cfg['upload_ip']) == 0)
+ return true;
+ forEach ($cfg['upload_ip'] as $i)
+ {
+ if ($i == $ip)
+ return true;
+ // CIDR test for IPv4 only.
+ if (strpos ($i, '/') !== false)
+ {
+ list ($subnet, $mask) = explode('/', $i);
+ if ((ip2long ($ip) & ~((1 << (32 - $mask)) - 1) ) == ip2long ($subnet))
+ return true;
+ }
+ }
+ return false;
+}
+
+/** Tell if we have some HTTP headers generated by a proxy */
+function has_http_forwarded()
+{
+ if (!empty ($_SERVER['HTTP_X_FORWARDED_FOR']))
+ return true;
+ if (!empty ($_SERVER['http_X_forwarded_for']))
+ return true;
+ return false;
+}
+
+/**
+ * Generate IP list from HTTP headers generated by a proxy
+ * @return array of IP strings
+ */
+function get_ip_list_http_forwarded()
+{
+ $ip_list = array();
+ if (!empty ($_SERVER['HTTP_X_FORWARDED_FOR']))
+ {
+ $l = explode (',', $_SERVER['HTTP_X_FORWARDED_FOR']);
+ foreach ($l as $ip)
+ array_push ($ip_list, preg_replace ('/\s+/', '', $ip));
+ }
+ if (!empty ($_SERVER['http_X_forwarded_for']))
+ {
+ $l = explode (',', $_SERVER['http_X_forwarded_for']);
+ foreach ($l as $ip)
+ {
+ // Separate IP from port
+ $ip = explode (':', $ip)[0];
+ array_push ($ip_list, preg_replace ('/\s+/', '', $ip));
+ }
+ }
+ return $ip_list;
+}
+
+/**
+ * Get the ip address of the client from REMOTE_ADDR
+ * or from HTTP_X_FORWARDED_FOR if behind a proxy
+ * @returns the client ip address
+ */
+function get_ip_address($cfg) {
+ $remote = $_SERVER['REMOTE_ADDR'];
+ if (count ($cfg['proxy_ip']) == 0 || !has_http_forwarded ())
+ return $remote;
+
+ $ip_list = get_ip_list_http_forwarded ();
+ if (count ($ip_list) == 0)
+ return $remote;
+
+ foreach ($cfg['proxy_ip'] as $proxy_ip)
+ {
+ if ($remote != $proxy_ip)
+ continue;
+ // Take the last IP (the one which has been set by the defined proxy).
+ return end ($ip_list);
+ }
+ return $remote;
+}
+
+/**
+ * Convert hexadecimal string to base64
+ */
+function hex_to_base64($hex)
+{
+ $b = '';
+ foreach (str_split ($hex, 2) as $pair)
+ $b .= chr (hexdec ($pair));
+ return base64_encode ($b);
+}
+
+/**
+ * Read alias informations
+ * @return array containing informations.
+ */
+function
+jirafeau_get_alias ($hash)
+{
+ $out = array ();
+ $link = VAR_ALIAS . s2p ("$hash") . $hash;
+
+ if (!file_exists ($link))
+ return $out;
+
+ $c = file ($link);
+ $out['md5_password'] = trim ($c[0]);
+ $out['ip'] = trim ($c[1]);
+ $out['update_date'] = trim ($c[2]);
+ $out['destination'] = trim ($c[3], NL);
+
+ return $out;
+}
+
+/** Create an alias to a jirafeau's link.
+ * @param $alias alias name
+ * @param $destination reference of the destination
+ * @param $password password to protect alias
+ * @param $ip client's IP
+ * @return a string containing the edit code of the alias or the string "Error"
+ */
+function jirafeau_alias_create ($alias, $destination, $password, $ip)
+{
+ /* Check that alias and password are long enough. */
+ if (strlen ($alias) < 8 ||
+ strlen ($alias) > 32 ||
+ strlen ($password) < 8 ||
+ strlen ($password) > 32)
+ return "Error";
+
+ /* Check that destination exists. */
+ $l = jirafeau_get_link ($destination);
+ if (!count ($l))
+ return "Error";
+
+ /* Check that alias does not already exists. */
+ $alias = md5 ($alias);
+ $p = VAR_ALIAS . s2p ($alias);
+ if (file_exists ($p))
+ return "Error";
+
+ /* Create alias folder. */
+ @mkdir ($p, 0755, true);
+ if (!file_exists ($p))
+ return "Error";
+
+ /* Generate password. */
+ $md5_password = md5 ($password);
+
+ /* Store informations. */
+ $p .= $alias;
+ $handle = fopen ($p, 'w');
+ fwrite ($handle,
+ $md5_password . NL .
+ $ip . NL .
+ date ('U') . NL .
+ $destination . NL);
+ fclose ($handle);
+
+ return "Ok";
+}
+
+/** Update an alias.
+ * @param $alias alias to update
+ * @param $destination reference of the new destination
+ * @param $password password to protect alias
+ * @param $new_password optional new password to protect alias
+ * @param $ip client's IP
+ * @return "Ok" or "Error" string
+ */
+function jirafeau_alias_update ($alias, $destination, $password,
+ $new_password, $ip)
+{
+ $alias = md5 ($alias);
+ /* Check that alias exits. */
+ $a = jirafeau_get_alias ($alias);
+ if (!count ($a))
+ return "Error";
+
+ /* Check that destination exists. */
+ $l = jirafeau_get_link ($a["destination"]);
+ if (!count ($l))
+ return "Error";
+
+ /* Check password. */
+ if ($a["md5_password"] != md5 ($password))
+ return "Error";
+
+ $p = $a["md5_password"];
+ if (strlen ($new_password) >= 8 &&
+ strlen ($new_password) <= 32)
+ $p = md5 ($new_password);
+ else if (strlen ($new_password) > 0)
+ return "Error";
+
+ /* Rewrite informations. */
+ $p = VAR_ALIAS . s2p ($alias) . $alias;
+ $handle = fopen ($p, 'w');
+ fwrite ($handle,
+ $p . NL .
+ $ip . NL .
+ date ('U') . NL .
+ $destination . NL);
+ fclose ($handle);
+ return "Ok";
+}
+
+/** Get an alias.
+ * @param $alias alias to get
+ * @return alias destination or "Error" string
+ */
+function jirafeau_alias_get ($alias)
+{
+ $alias = md5 ($alias);
+ /* Check that alias exits. */
+ $a = jirafeau_get_alias ($alias);
+ if (!count ($a))
+ return "Error";
+
+ return $a["destination"];
+}
+
+function jirafeau_clean_rm_alias ($alias)
+{
+ $p = s2p ("$alias");
+ if (file_exists (VAR_ALIAS . $p . $alias))
+ unlink (VAR_ALIAS . $p . $alias);
+ $parse = VAR_ALIAS . $p;
+ $scan = array();
+ while (file_exists ($parse)
+ && ($scan = scandir ($parse))
+ && count ($scan) == 2 // '.' and '..' folders => empty.
+ && basename ($parse) != basename (VAR_ALIAS))
+ {
+ rmdir ($parse);
+ $parse = substr ($parse, 0, strlen($parse) - strlen(basename ($parse)) - 1);
+ }
+}
+
+/** Delete an alias.
+ * @param $alias alias to delete
+ * @param $password password to protect alias
+ * @return "Ok" or "Error" string
+ */
+function jirafeau_alias_delete ($alias, $password)
+{
+ $alias = md5 ($alias);
+ /* Check that alias exits. */
+ $a = jirafeau_get_alias ($alias);
+ if (!count ($a))
+ return "Error";
+
+ /* Check password. */
+ if ($a["md5_password"] != md5 ($password))
+ return "Error";
+
+ jirafeau_clean_rm_alias ($alias);
+ return "Ok";
+}
+