Exemple #1
0
 public function downloadTheme($name, $url, $signature)
 {
     $model = Model::instance();
     //download theme
     $net = new \Ip\Internal\NetHelper();
     $themeTempFilename = $net->downloadFile($url, ipFile('file/secure/tmp/'), $name . '.zip');
     if (!$themeTempFilename) {
         throw new \Ip\Exception('Theme file download failed.');
     }
     $archivePath = ipFile('file/secure/tmp/' . $themeTempFilename);
     //check signature
     $fileMd5 = md5_file($archivePath);
     $rsa = new \Crypt_RSA();
     $rsa->loadKey($this->publicKey);
     $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
     $verified = $rsa->verify($fileMd5, base64_decode($signature));
     if (!$verified) {
         throw new \Ip\Exception('Theme signature verification failed.');
     }
     //extract
     $helper = Helper::instance();
     $secureTmpDir = ipFile('file/secure/tmp/');
     $tmpExtractedDir = \Ip\Internal\File\Functions::genUnoccupiedName($name, $secureTmpDir);
     \Ip\Internal\Helper\Zip::extract($secureTmpDir . $themeTempFilename, $secureTmpDir . $tmpExtractedDir);
     unlink($archivePath);
     //install
     $extractedDir = $helper->getFirstDir($secureTmpDir . $tmpExtractedDir);
     $installDir = $model->getThemeInstallDir();
     $newThemeDir = \Ip\Internal\File\Functions::genUnoccupiedName($name, $installDir);
     rename($secureTmpDir . $tmpExtractedDir . '/' . $extractedDir, $installDir . $newThemeDir);
 }
 public function downloadPlugin($name, $url, $signature)
 {
     if (is_dir(ipFile("Plugin/{$name}/"))) {
         Service::deactivatePlugin($name);
         Helper::removeDir(ipFile("Plugin/{$name}/"));
     }
     //download plugin
     $net = new \Ip\Internal\NetHelper();
     $pluginTempFilename = $net->downloadFile($url, ipFile('file/secure/tmp/'), $name . '.zip');
     if (!$pluginTempFilename) {
         throw new \Ip\Exception('Plugin file download failed.');
     }
     $archivePath = ipFile('file/secure/tmp/' . $pluginTempFilename);
     //check signature
     $fileMd5 = md5_file($archivePath);
     $rsa = new \Crypt_RSA();
     $rsa->loadKey($this->publicKey);
     $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
     $verified = $rsa->verify($fileMd5, base64_decode($signature));
     if (!$verified) {
         throw new \Ip\Exception('Plugin signature verification failed.');
     }
     //extract
     $secureTmpDir = ipFile('file/secure/tmp/');
     $tmpExtractedDir = \Ip\Internal\File\Functions::genUnoccupiedName($name, $secureTmpDir);
     \Ip\Internal\Helper\Zip::extract($secureTmpDir . $pluginTempFilename, $secureTmpDir . $tmpExtractedDir);
     unlink($archivePath);
     //install
     $extractedDir = $this->getFirstDir($secureTmpDir . $tmpExtractedDir);
     $installDir = Model::pluginInstallDir();
     $newPluginDir = \Ip\Internal\File\Functions::genUnoccupiedName($name, $installDir);
     rename($secureTmpDir . $tmpExtractedDir . '/' . $extractedDir, $installDir . $newPluginDir);
     Service::activatePlugin($name);
 }
 public static function download()
 {
     $requestFile = ipFile('') . ipRequest()->getRelativePath();
     $fileDir = ipFile('file/');
     if (mb_strpos($requestFile, $fileDir) !== 0) {
         return null;
     }
     $file = mb_substr($requestFile, mb_strlen($fileDir));
     $file = urldecode($file);
     if (empty($file)) {
         throw new \Ip\Exception('Required parameter is missing');
     }
     $absoluteSource = realpath(ipFile('file/' . $file));
     if (!$absoluteSource || !is_file($absoluteSource)) {
         throw new \Ip\Exception\Repository\Transform("File doesn't exist", array('filename' => $absoluteSource));
     }
     if (strpos($absoluteSource, realpath(ipFile('file/'))) !== 0 || strpos($absoluteSource, realpath(ipFile('file/secure'))) === 0) {
         throw new \Exception("Requested file (" . $file . ") is outside of public dir");
     }
     $mime = \Ip\Internal\File\Functions::getMimeType($absoluteSource);
     $fsize = filesize($absoluteSource);
     // set headers
     header("Pragma: public");
     header("Expires: 0");
     header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
     header("Cache-Control: public");
     header('Content-type: ' . $mime);
     header("Content-Transfer-Encoding: binary");
     header("Content-Length: " . $fsize);
     // download
     // @readfile($file_path);
     $file = @fopen($absoluteSource, "rb");
     if ($file) {
         while (!feof($file)) {
             print fread($file, 1024 * 8);
             flush();
             if (connection_status() != 0) {
                 @fclose($file);
                 die;
             }
         }
         @fclose($file);
     }
     //TODO provide method to stop any output by ImpressPages
     ipDb()->disconnect();
     exit;
 }
Exemple #4
0
 public function downloadFile($url, $destinationDir, $desiredFilename, $forceFilename = false)
 {
     if (!$forceFilename) {
         $desiredFilename = \Ip\Internal\File\Functions::genUnoccupiedName($desiredFilename, $destinationDir);
     }
     if (!function_exists('curl_init')) {
         throw new \Exception('CURL is not installed. Cannot download file from URL.');
     }
     $ch = curl_init();
     $fh = fopen($destinationDir . $desiredFilename, 'w');
     $options = array(CURLOPT_FILE => $fh, CURLOPT_TIMEOUT => 1800, CURLOPT_URL => $url);
     curl_setopt_array($ch, $options);
     if (curl_exec($ch)) {
         return $desiredFilename;
     } else {
         $this->lastError = curl_error($ch);
         return false;
     }
 }
Exemple #5
0
 /**
  * Adds email to the queue
  *
  * Even if there is a big amount of emails, there is always reserved 20% of traffic for immediate emails.
  * Such emails are: registration cofirmation, contact form data and other.
  * Newsletters, greetings always can wait a litle. So they are not immediate and will not be send if is less than 20% of traffic left.
  *
  * @param string $from email address from whish an email should be send
  * @param $fromName
  * @param string $to email address where an email should be send
  * @param $toName
  * @param $subject
  * @param string $email email html text
  * @param bool $immediate indicate hurry of an email.
  * @param bool $html true if email message should be send as html
  * @param array $files files that should be attached to the email. Files should be accessible for php at this moment. They will be cached until send time.
  * @internal param $string @fromName
  * @internal param $string @toName
  */
 function addEmail($from, $fromName, $to, $toName, $subject, $email, $immediate, $html, $files = null)
 {
     $cached_files = array();
     $cached_fileNames = array();
     $cached_fileMimeTypes = array();
     if ($files) {
         if (is_string($files)) {
             $files = array($files);
         }
         foreach ($files as $fileSetting) {
             $file = array();
             if (is_array($fileSetting)) {
                 $file['real_name'] = $fileSetting[0];
                 $file['required_name'] = basename($fileSetting[1]);
             } else {
                 $file['real_name'] = $fileSetting;
                 $file['required_name'] = $fileSetting;
             }
             $new_name = 'contact_form_' . rand();
             $new_name = \Ip\Internal\File\Functions::genUnoccupiedName($new_name, ipFile('file/tmp/'));
             if (copy($file['real_name'], ipFile('file/tmp/' . $new_name))) {
                 $cached_files[] = ipFile('file/tmp/' . $new_name);
                 $cached_fileNames[] = $file['required_name'];
                 $tmpMimeType = \Ip\Internal\File\Functions::getMimeType($file['real_name']);
                 if ($tmpMimeType == null) {
                     $tmpMimeType = 'Application/octet-stream';
                 }
                 $cached_fileMimeTypes[] = $tmpMimeType;
             } else {
                 trigger_error('File caching failed');
             }
         }
     }
     $cachedFilesStr = implode("\n", $cached_files);
     $cachedFileNamesStr = implode("\n", $cached_fileNames);
     $cachedFileMimeTypesStr = implode("\n", $cached_fileMimeTypes);
     $email = str_replace('src="' . ipConfig()->baseUrl(), 'src="', $email);
     Db::addEmail($from, $fromName, $to, $toName, $subject, $email, $immediate, $html, $cachedFilesStr, $cachedFileNamesStr, $cachedFileMimeTypesStr);
 }
Exemple #6
0
 /**
  * Add file to the repository.
  * @param string $file absolute path to file in tmp directory
  * @param null|string $desiredName desired file name in repository.
  * @return string relative file name in repository
  * @throws \Ip\Exception
  */
 public function addFile($file, $desiredName)
 {
     if (!is_file($file)) {
         throw new \Ip\Exception("File doesn't exist");
     }
     if (strpos(realpath($file), realpath(ipFile('file/repository/'))) === 0) {
         throw new \Ip\Exception("Requested file (" . $file . ") is already in the repository");
     }
     $destination = ipFile('file/repository/');
     if ($desiredName === null) {
         $desiredName = basename($file);
         //to avoid any tricks with relative paths, etc.
     }
     $newName = \Ip\Internal\File\Functions::genUnoccupiedName($desiredName, $destination);
     copy($file, $destination . $newName);
     return $newName;
 }
Exemple #7
0
 /**
  * @param string $url
  * @return string
  */
 protected function downloadFile($url, $title)
 {
     //download image to TMP dir and get $resultFilename
     $net = new \Ip\Internal\NetHelper();
     $tmpFilename = $net->downloadFile($url, ipFile('file/tmp/'), 'bigstock_' . time());
     if (!$tmpFilename) {
         return null;
     }
     //find out file mime type to know required extension
     try {
         $mime = \Ip\Internal\File\Functions::getMimeType(ipFile('file/tmp/' . $tmpFilename));
         switch ($mime) {
             case 'image/png':
                 $ext = '.jpg';
                 break;
             case 'image/gif':
                 $ext = '.gif';
                 break;
             case 'image/bmp':
                 $ext = '.bmp';
                 break;
             case 'image/pjpeg':
             case 'image/jpeg':
             default:
                 $ext = '.jpg';
                 break;
         }
     } catch (\Ip\PhpException $e) {
         $ext = '.jpg';
     }
     //get real nice new file name
     $title = \Ip\Internal\File\Functions::cleanupFileName($title);
     $words = explode(' ', $title);
     $cleanTitle = '';
     foreach ($words as $word) {
         //limit file name to 30 symbols
         if (strlen($cleanTitle . '_' . $word) > 30) {
             break;
         }
         if ($cleanTitle != '') {
             $cleanTitle .= '_';
         }
         $cleanTitle .= $word;
     }
     if ($cleanTitle == '') {
         $cleanTitle = 'file';
     }
     $niceFileName = $cleanTitle . $ext;
     $destinationDir = ipFile('file/repository/');
     $destinationFileName = \Ip\Internal\File\Functions::genUnoccupiedName($niceFileName, $destinationDir);
     copy(ipFile('file/tmp/' . $tmpFilename), $destinationDir . $destinationFileName);
     unlink(ipFile('file/tmp/' . $tmpFilename));
     $browserModel = \Ip\Internal\Repository\BrowserModel::instance();
     $file = $browserModel->getFile($destinationFileName);
     return $file;
 }
Exemple #8
0
 private function createReflectionRecord($source, $options, $desiredName)
 {
     $absoluteSource = realpath(ipFile('file/repository/' . $source));
     if (!$absoluteSource || !is_file($absoluteSource)) {
         throw new \Ip\Exception\Repository\Transform("File doesn't exist", array('filename' => $absoluteSource));
     }
     if (strpos($absoluteSource, realpath(ipFile('file/repository/'))) !== 0) {
         throw new \Exception("Requested file (" . $source . ") is outside repository dir");
     }
     //if desired name ends with .jpg, .gif, etc., remove extension
     $desiredPathInfo = pathinfo($desiredName);
     if (!empty($desiredPathInfo['filename']) && isset($desiredPathInfo['extension']) && strlen($desiredPathInfo['extension']) <= 4) {
         $desiredName = $desiredPathInfo['filename'];
     }
     //update destination file extension
     $pathInfo = pathinfo($absoluteSource);
     if (isset($pathInfo['extension'])) {
         $ext = $pathInfo['extension'];
     } else {
         $ext = '';
     }
     $ext = ipFilter('ipReflectionExtension', $ext, array('source' => $absoluteSource, 'options' => $options));
     if ($desiredName == '') {
         $pathInfo = pathinfo($absoluteSource);
         $desiredName = $pathInfo['filename'];
     }
     if ($ext != '') {
         $desiredName = $desiredName . '.' . $ext;
     }
     $desiredName = \Ip\Internal\File\Functions::cleanupFileName($desiredName);
     //remove double dots if file name. For security reasons.
     $relativeDestinationPath = date('Y/m/d/');
     $relativeDestinationPath = ipFilter('ipRepositoryNewReflectionFileName', $relativeDestinationPath, array('originalFile' => $source, 'options' => $options, 'desiredName' => $desiredName));
     $destinationFileName = $this->getUnocupiedName($desiredName, $relativeDestinationPath);
     $reflection = $relativeDestinationPath . $destinationFileName;
     $this->storeReflectionRecord($source, $reflection, $options);
     return $reflection;
 }
Exemple #9
0
 /**
  * Handle uploads made using PlUpload library
  * @param bool $secureFolder
  * @throws \Ip\Exception\Repository\Upload
  */
 public function handlePlupload($secureFolder)
 {
     if (!$secureFolder && !ipAdminId()) {
         throw new \Ip\Exception\Repository\Upload("Trying to upload image to temporary directory without permission.");
     }
     if ($secureFolder) {
         $targetDir = ipFile('file/secure/tmp/');
     } else {
         $targetDir = ipFile('file/tmp/');
     }
     if ($secureFolder) {
         $sizeLimit = ipGetOption('Repository.publicUploadLimit', 4000);
         if ($this->folderSize($targetDir) > $sizeLimit * 1000000) {
             //4000 Mb by default
             ipLog()->error("Repository.publicUploadLimitReached: IP: `{ip}`. CurrentLimit `{limit}Mb`. Please update Repository.publicUploadLimit option to increase the limits.", array('ip' => $_SERVER['REMOTE_ADDR'], 'limit' => $sizeLimit));
             throw new \Ip\Exception("Upload limit reached");
         }
     }
     // Get parameters
     $chunk = isset($_REQUEST["chunk"]) ? $_REQUEST["chunk"] : 0;
     $chunks = isset($_REQUEST["chunks"]) ? $_REQUEST["chunks"] : 0;
     $fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : '';
     // Clean the fileName for security reasons
     $fileName = \Ip\Internal\File\Functions::cleanupFileName($fileName);
     // Make sure the fileName is unique but only if chunking is disabled
     if ($chunks < 2 && file_exists($targetDir . $fileName)) {
         $fileName = \Ip\Internal\File\Functions::genUnoccupiedName($fileName, $targetDir);
     }
     //security check
     $fileExtension = strtolower(substr($fileName, strrpos($fileName, '.') + 1));
     $whiteListExtensions = array('jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'asf', 'asx', 'wmv', 'wmx', 'wm', 'avi', 'divx', 'flv', 'mov', 'qt', 'mpeg', 'mpg', 'mpe', 'mp4', 'm4v', 'ogv', 'webm', 'mkv', 'txt', 'asc', 'c', 'cc', 'h', 'csv', 'tsv', 'ics', 'rtx', 'css', 'htm', 'html', 'vtt', 'mp3', 'm4a', 'm4b', 'ra', 'ram', 'wav', 'ogg', 'oga', 'mid', 'midi', 'wma', 'wax', 'mka', 'rtf', 'js', 'pdf', 'class', 'tar', 'zip', 'gz', 'gzip', 'rar', '7z', 'doc', 'pot', 'pps', 'ppt', 'wri', 'xla', 'xls', 'xlt', 'xlw', 'mdb', 'mpp', 'docx', 'docm', 'dotx', 'dotm', 'eps', 'xlsx', 'xlsm', 'xlsb', 'xltx', 'xltm', 'xlam', 'pptx', 'pptm', 'ppsx', 'ppsm', 'potx', 'potm', 'ppam', 'sldx', 'sldm', 'onetoc', 'onetoc2', 'onetmp', 'onepkg', 'odt', 'odp', 'ods', 'odg', 'odc', 'odb', 'odf', 'wp', 'wpd', 'key', 'numbers', 'pages', 'xml', 'json', 'iso', 'aac', 'img', 'psd', 'ai', 'sql', 'swf', 'svg');
     $whiteListExtensions = ipFilter('ipWhiteListExtensions', $whiteListExtensions);
     if (!empty($fileExtension) && !in_array($fileExtension, $whiteListExtensions)) {
         //security risk
         throw new \Ip\Exception\Repository\Upload\ForbiddenFileExtension("Files with extension (." . esc($fileExtension) . ") are not permitted for security reasons.", array('extension' => $fileExtension, 'filename' => $fileName));
     }
     //end security check
     // Look for the content type header
     $contentType = null;
     if (isset($_SERVER["HTTP_CONTENT_TYPE"])) {
         $contentType = $_SERVER["HTTP_CONTENT_TYPE"];
     }
     if (isset($_SERVER["CONTENT_TYPE"])) {
         $contentType = $_SERVER["CONTENT_TYPE"];
     }
     // Handle non multipart uploads older WebKit versions didn't support multipart in HTML5
     if (strpos($contentType, "multipart") !== false) {
         if (!isset($_FILES['file']['tmp_name']) || !is_uploaded_file($_FILES['file']['tmp_name'])) {
             throw new \Ip\Exception\Repository\Upload("Failed to move uploaded file.");
         }
         // Open temp file
         $out = fopen($targetDir . $fileName, $chunk == 0 ? "wb" : "ab");
         if (!$out) {
             throw new \Ip\Exception\Repository\Upload("Failed to open output stream.");
         }
         //mark this file as uploaded by current user
         $this->setFileUploadedByThisUser($targetDir . $fileName);
         // Read binary input stream and append it to temp file
         $in = fopen($_FILES['file']['tmp_name'], "rb");
         if (!$in) {
             throw new \Ip\Exception\Repository\Upload("Failed to open input stream.");
         }
         while ($buff = fread($in, 4096)) {
             fwrite($out, $buff);
         }
         fclose($in);
         fclose($out);
         @unlink($_FILES['file']['tmp_name']);
     } else {
         // Open temp file
         $out = fopen($targetDir . '/' . $fileName, $chunk == 0 ? "wb" : "ab");
         if (!$out) {
             throw new \Ip\Exception\Repository\Upload("Failed to open output stream.");
         }
         // Read binary input stream and append it to temp file
         $in = fopen("php://input", "rb");
         if (!$in) {
             throw new \Ip\Exception\Repository\Upload("Failed to open input stream.");
         }
         while ($buff = fread($in, 4096)) {
             if (function_exists('set_time_limit')) {
                 set_time_limit(30);
             }
             fwrite($out, $buff);
         }
         fclose($in);
         fclose($out);
     }
     $this->uploadedFileName = $fileName;
     $this->uploadedFile = $targetDir . $fileName;
     $this->targetDir = $targetDir;
 }
Exemple #10
0
/**
 * Get unocupied file name in directory. Very useful when storing uploaded files.
 *
 * @param string $dir
 * @param string $desiredName
 * @param bool $sanitize clean up supicious symbols from file name
 * @return string
 */
function ipUnoccupiedFileName($dir, $desiredName, $sanitize = true)
{
    $availableFileName = \Ip\Internal\File\Functions::genUnoccupiedName($desiredName, $dir, '', $sanitize);
    return $availableFileName;
}
Exemple #11
0
 /**
  * @param string $imageFile
  * @param string $destDir
  * @param int $x1
  * @param int $y1
  * @param int $x2
  * @param int $y2
  * @param int $quality
  * @param int $widthDest
  * @param int $heightDest
  * @return string
  * @throws \Ip\Exception
  * @throws \Exception
  */
 public static function crop($imageFile, $destDir, $x1, $y1, $x2, $y2, $quality, $widthDest, $heightDest)
 {
     if ($widthDest === null) {
         $widthDest = $x2 - $x1;
     }
     if ($heightDest === null) {
         $heightDest = $y2 - $y1;
     }
     $imageInfo = getimagesize($imageFile);
     if ($imageInfo[0] == $widthDest && $imageInfo[1] == $heightDest && $x1 == 0 && $y1 == 0) {
         // Don't need to crop or resize.
         $newName = \Ip\Internal\File\Functions::genUnoccupiedName($imageFile, $destDir);
         copy($imageFile, $destDir . $newName);
         return $newName;
     }
     if (!self::getMemoryNeeded($imageFile)) {
         throw new \Ip\Exception("Can't get memory needed", self::ERROR_MEMORY);
     }
     try {
         $image = self::createImageImage($imageFile);
     } catch (\Exception $e) {
         throw new \Ip\Exception($e->getMessage(), $e->getCode(), $e);
     }
     if ($x2 - $x1 > imagesx($image) || $y2 - $y1 > imagesy($image) || $x1 < 0 || $y1 < 0) {
         // Cropping area goes out of image edge. Fill transparent.
         /**
          * Negative coordinates x1, y1 are possible.
          * This part of code just adds tarnsparent edges in this way making $image required proportions.
          * We don't care about the size in this step.
          */
         $tmpImage = imagecreatetruecolor($x2 - $x1, $y2 - $y1);
         imagealphablending($tmpImage, false);
         imagesavealpha($tmpImage, true);
         $color = imagecolorallocatealpha($tmpImage, 255, 255, 255, 127);
         imagefilledrectangle($tmpImage, 0, 0, $x2 - $x1, $y2 - $y1, $color);
         if ($x1 >= 0) {
             $sx1 = $x1;
             $dx1 = 0;
         } else {
             $sx1 = 0;
             $dx1 = -$x1;
         }
         if ($y1 >= 0) {
             $sy1 = $y1;
             $dy1 = 0;
         } else {
             $sy1 = 0;
             $dy1 = -$y1;
         }
         if ($x2 - $x1 > imagesx($image)) {
             $sx2 = imagesx($image);
             // $dx2 = $x2 - $x1;
             $dx2 = $dx1 + imagesx($image);
         } else {
             $sx2 = $x2;
             $dx2 = imagesx($tmpImage);
         }
         if ($y2 - $y1 > imagesy($image)) {
             $sy2 = imagesy($image);
             $dy2 = $dy1 + imagesy($image);
         } else {
             $sy2 = $y2;
             $dy2 = imagesy($tmpImage);
         }
         imagecopyresampled($tmpImage, $image, $dx1, $dy1, $sx1, $sy1, $dx2 - $dx1, $dy2 - $dy1, $sx2 - $sx1, $sy2 - $sy1);
         $image = $tmpImage;
         $sx1 = 0;
         $sy1 = 0;
         $sx2 = imagesx($image);
         $sy2 = imagesy($image);
         /* Transparency required. Transform to png. */
         $mime = IMAGETYPE_PNG;
         $path_parts = pathinfo($imageFile);
         if ($path_parts['extension'] != 'png') {
             $tmpImageName = $path_parts['filename'] . '.png';
         } else {
             $tmpImageName = $imageFile;
         }
         $newName = \Ip\Internal\File\Functions::genUnoccupiedName($tmpImageName, $destDir);
     } else {
         $sx1 = $x1;
         $sx2 = $x2;
         $sy1 = $y1;
         $sy2 = $y2;
         $mime = self::getMimeType($imageFile);
         $newName = \Ip\Internal\File\Functions::genUnoccupiedName($imageFile, $destDir);
     }
     /**
      * Our $image is required proportions. The only thing we need to do is to scale the image and save.
      */
     $imageNew = imagecreatetruecolor($widthDest, $heightDest);
     imagealphablending($imageNew, false);
     imagesavealpha($imageNew, true);
     $color = imagecolorallocatealpha($imageNew, 255, 255, 255, 127);
     imagefilledrectangle($imageNew, 0, 0, $widthDest, $heightDest, $color);
     imagecopyresampled($imageNew, $image, 0, 0, $sx1, $sy1, $widthDest, $heightDest, $sx2 - $sx1, $sy2 - $sy1);
     $newFile = $destDir . $newName;
     try {
         self::saveImage($imageNew, $newFile, $quality, $mime);
     } catch (\Exception $e) {
         throw new \Exception($e->getMessage(), $e->getCode(), $e);
     }
     return $newName;
 }
Exemple #12
0
 /**
  * @param string $relativePath
  * @param string $destinationDir
  * @return string
  */
 public static function copyTemporaryFile($relativePath, $destinationDir)
 {
     $newBasename = \Ip\Internal\File\Functions::genUnoccupiedName($relativePath, $destinationDir);
     if (!copy(ipFile('file/tmp/' . $relativePath), $destinationDir . $newBasename)) {
         trigger_error("Can't copy file from " . htmlspecialchars(ipThemeFile('') . $relativePath) . ' to ' . htmlspecialchars($destinationDir . $newBasename));
     }
     return $newBasename;
 }