<?php

if (!function_exists('myLog')) {
	/**
	 * myLog(__CLASS__, __FUNCTION__, __LINE__, "");
	 */
	function myLog($cls, $fun, $line, $msg = '') {
		trigger_error(":U({$_SERVER['PHP_AUTH_USER']}): {$cls}::{$fun}():{$line}: {$msg}", E_USER_NOTICE);
	}
}


Lib::loadClass('FoldersConfig');


/**
 * Webdav Helper class.
 * 
 * Directory structure (samba - filesystem root path):
 * 
 * samba/DRUKI/
 * 
 * samba/DRUKI/1..../*  pliki fizycznie w katalogu
 * samba/DRUKI/1..../PROJEKT.2/ -> to samo co w samba/PROJEKTY/2..../
 * samba/DRUKI/1..../PROJEKT.3/ -> to samo co w samba/PROJEKTY/3..../
 * 
 * samba/PROJEKTY/
 * 
 * samba/PROJEKTY/2..../ * pliki fizycznie w katalogu
 * samba/PROJEKTY/2..../DRUK.1/ -> to samo co w samba/DRUKI/1..../
 * RM: samba/PROJEKTY/2..../PISMO.10/ * plik skan/pdf - pismo
 * RM: samba/PROJEKTY/2..../PISMO.11/ * plik skan/pdf - pismo
 * samba/PROJEKTY/2..../PISMA/{ID}.*.* * plik skan/pdf - pismo
 * 
 * samba/PROJEKTY/3..../ * pliki fizycznie w katalogu
 * 
 * Database folder ID names:
 *     PROJEKTY - IN7_MK_BAZA_DYSTRYBUCJI
 *     DRUKI    - CRM_LISTA_ZASOBOW
 *     PISMA    - IN7_DZIENNIK_KORESP
 * 
 * PROJEKTY <--> DRUKI : IN7_MK_BAZA_DYSTRYBUCJI.ID_ZASOB_REVITALIZE <--> CRM_LISTA_ZASOBOW.ID
 * PROJEKTY <--> PISMA : IN7_DZIENNIK_KORESP.ID_PROJECT <--> IN7_MK_BAZA_DYSTRYBUCJI.ID
 * 
 */
class WebdavHelper {

	function getRootDir() {
		return FoldersConfig::getRootPoint('mount_point');
	}

	function getTrashDir() {
		return FoldersConfig::getRootPoint('mount_point') . '/Trash';
	}

	function getBaseDir($col_name) {
		return FoldersConfig::get($col_name, 'mount_point');
	}

	/**
	 * @param string $davpath eg.: SE/DRUKI/1379.UMOWA.2011-06-03.umowa_abonencka.2011-06-03
	 * 
	 * @returns array $list [realname => type(dir,file,virtualdir?)]
	 */
	function getChildren($davpath = '', $user = null) {
		$list = array();
		$file = self::getRealFile($davpath, $user);
		if ($file) {
			if ($file->is_dir === true) {
				if ($davpath == 'SE') {
					$list['PROJEKTY'] = 'dir';
					$list['DRUKI'] = 'dir';
					return $list;
				}
				if (!empty($file->virtual_dirs)) {
					foreach ($file->virtual_dirs as $name) {
						$list[$name] = 'dir';
					}
				}
				if (!empty($file->virtual_files)) {
					foreach ($file->virtual_files as $name) {
						$list[$name] = 'file';
					}
				}
				if (empty($file->prevent_scandir)) {
					foreach (scandir($file->realpath) as $name) {
						if (substr($name, 0, 1) == '.') continue;

						//$id = reset(explode('.', $name));
						//if (is_numeric($id)) {// TODO: check name format
						if (is_dir($file->realpath . '/' . $name)) {
							$list[$name] = 'dir';
						} else {
							$list[$name] = 'file';
						}
						//}
					}
				}
			}
		}
		return $list;
	}

	/**
	 * 
	 * @returns string $type ('dir', 'file', '' - not exists)
	 * 
	 * webdav> ls -> TODO: getItem(SE)
	 * webdav> ls DRUKI -> TODO: getItem(SE/DRUKI)
	 */
	function getChildType($davpath = '', $user = null) {
		$file = self::getRealFile($davpath, $user);
		//myLog(__CLASS__, __FUNCTION__, __LINE__, "TODO: ({$davpath}) file: " . json_encode($file));
		if ($file) {
			if ($file->is_dir === true) {
				return 'dir';
			} else if ($file->is_dir === false) {
				return 'file';
			}
		}
		return '';
	}

	/**
	 * Convert WebDAV path to real path, and add additional info.
	 * 
	 */
	function getRealFile($davpath, $user = null) {
		$file = new stdClass();
		$file->realpath = WebdavHelper::getRootDir();
		$file->is_dir = null;
		$file->virtual_dirs = null;
		$file->virtual_files = null;
		$file->perms = 'R';
		$pathObj = new stdClass();
		$pathObj->_main_dir = null;
		$pathObj->_projekt_id = null;
		$pathObj->_druk_id = null;
		$pathObj->_pismo_id = null;
		$pathObj->_proj_pisma = false;

		$dav_parts = explode('/', $davpath);

		{// 1st part: SE
			$part = array_shift($dav_parts);
			if ($part == 'SE') {
				if (empty($dav_parts)) {
					$file->is_dir = true;
					return $file;
				}
			} else {
				return null;
			}
		}

		{// 2nd part: SE/ PROJEKTY, DRUKI
			$part = array_shift($dav_parts);
			if (in_array($part, array('PROJEKTY', 'DRUKI'))) {
				$file->realpath .= '/' . $part;
				if (empty($dav_parts)) {
					$file->is_dir = true;
					if ($part == 'PROJEKTY') {
						Lib::loadClass('User');
						$user_name = User::getName();
						$projekty_ids = self::getProjektyIds($user_name);
						$file->prevent_scandir = true;
						foreach (scandir($file->realpath) as $name) {
							if (substr($name, 0, 1) == '.') continue;

							$id = reset(explode('.', $name));
							if (!is_numeric($id)) continue;
							if (!in_array($id, $projekty_ids)) {
								continue;
							}

							if (is_dir($file->realpath . '/' . $name)) {
								$file->virtual_dirs[] = $name;
							} else {
								$file->virtual_files[] = $name;
							}
						}
					}
					else if ($part == 'DRUKI') {
						// TODO: only druki with access by procesy
					}
					return $file;
				}
			}
			else {
				return null;
			}
		}

		{// 3rd part: SE/[PROJEKTY,DRUKI]/ files, directories
			$pathObj->_main_dir = $part;
			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) 3rd part: {$pathObj->_main_dir}/...");
			$part = array_shift($dav_parts);
			if ($pathObj->_main_dir == 'DRUKI') {
				$druk_id = (int)reset(explode('.', $part));
				if ($druk_id > 0) {
					$pathObj->_druk_id = $druk_id;
				} else {
					// TODO: error wrong file name?
				}

				// TODO: change realpath if PROJEKT.{$ID}/
				$file->realpath .= '/' . $part;
				if (empty($dav_parts)) {
					$file->is_dir = is_dir($file->realpath);

					if ($druk_id > 0) {
						// TODO: add $file->virtual_dirs - PROJEKT.{$ID}/ <-- get creator projekt ID from DB
						$projektyIds = WebdavHelper::getProjektyIdsByDruk($druk_id, $user);
						foreach ($projektyIds as $projektID) {
							$file->virtual_dirs []= "PROJEKT.{$projektID}";
						}
					}

					return $file;
				}
			}
			else if ($pathObj->_main_dir == 'PROJEKTY') {
				$projekt_id = (int)reset(explode('.', $part));
				if ($projekt_id > 0) {
					$pathObj->_projekt_id = $projekt_id;
				} else {
					// TODO: error wrong file name?
				}

				$file->realpath .= '/' . $part;
				if (!file_exists($file->realpath)) {
					return null;
				}
				if (empty($dav_parts)) {
					$file->is_dir = is_dir($file->realpath);

					if ($projekt_id > 0) {
						$file->perms = 'RWXC';
						$file->virtual_dirs []= "PISMA";

						$druki = self::getDrukiIdsByProjekt($pathObj->_projekt_id);
						foreach ($druki as $druk_id) {
							$file->virtual_dirs []= "DRUK.{$druk_id}";
						}
					}

					return $file;
				}
			}
			else {
				return null;
			}
		}

		{// 4rd part: SE/[PROJEKTY,DRUKI]/{item_folder:$ID.*}/ files, directories, virtual
			$item_folder = $part;
			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) 4rd part: {$pathObj->_main_dir}/{$item_folder}/...");
			$part = array_shift($dav_parts);
			if ($pathObj->_main_dir == 'DRUKI') {
				$file->realpath .= '/' . $part;

				// $pathObj->_druk_id
				$cur_parts = explode('.', $part);
				if (count($cur_parts) == 2) {
					if ($cur_parts[0] == 'PROJEKT') {
						$id = (int)$cur_parts[1];
						if ($id > 0) {
							// TODO: find realpath for PROJEKT
							$file->realpath = self::findRealPath('PROJEKT', $id);
							if (empty($dav_parts)) {
								$file->is_dir = true;//is_dir($file->realpath);
								return $file;
							}
						}
					}
				}

				if (empty($dav_parts)) {
					$file->is_dir = is_dir($file->realpath);
					return $file;
				}
			}
			else if ($pathObj->_main_dir == 'PROJEKTY') {
				// $pathObj->_projekt_id
				$virtual_type = null;
				$virtual_id = null;
				$cur_parts = explode('.', $part);
				if ($part == 'PISMA') {
					$virtual_type = 'PISMA';
				}
				else if (count($cur_parts) == 2) {
					if ($cur_parts[0] == 'PROJEKT') {// alias do innego projektu?
						$virtual_id = (int)$cur_parts[1];
						$virtual_type = 'PROJEKT';
					} else if ($cur_parts[0] == 'DRUK') {
						$virtual_id = (int)$cur_parts[1];
						$virtual_type = 'DRUK';
					} else if ($cur_parts[0] == 'PISMO') {
						$virtual_id = (int)$cur_parts[1];
						$virtual_type = 'PISMO';
					}
				}

				if ($virtual_type) {
					if ($virtual_type == 'PISMA') {
						$pathObj->_proj_pisma = true;
						$pismaIds = self::getPismaIdsByProjekt($pathObj->_projekt_id);
						if (!empty($pismaIds)) {
							$pismaFiles = self::findRealPaths('PISMA', $pismaIds);
							foreach ($pismaFiles as $pismoFile) {
								$file->virtual_files []= end(explode('/', $pismoFile));
							}
							$file->is_dir = true;
							$file->prevent_scandir = true;
						}
					}
					else if ($virtual_type == 'PISMO') {
						$pathObj->_pismo_id = $virtual_id;
						if (empty($dav_parts)) {
							$pismo_file = self::findRealPath('PISMO', $pathObj->_pismo_id);
							//myLog(__CLASS__, __FUNCTION__, __LINE__, "PISMO {$pismo_file}...");
							//$file->realpath = $pismo_file;
							$file->virtual_files []= end(explode('/', $pismo_file));
							$file->prevent_scandir = true;
						}
					}
					else if ($virtual_type == 'DRUK') {
						$pathObj->_druk_id = $virtual_id;
						$druk_dir = self::findRealPath('DRUK', $pathObj->_druk_id);
						$file->realpath = $druk_dir;
						$file->is_dir = is_dir($file->realpath);
						$file->prevent_scandir = false;
						//myLog(__CLASS__, __FUNCTION__, __LINE__, "DRUK dir({$file->is_dir}) {$druk_dir}...");
					}
				}
				else {
					$file->perms = 'RWXC';
					$file->realpath .= '/' . $part;
				}
			}

			if (empty($dav_parts)) {
				if (!file_exists($file->realpath)) {
					return null;
				}
				$file->is_dir = is_dir($file->realpath);
				return $file;
			}
		}

		{// 5rd part: SE/[PROJEKTY,DRUKI]/{item_folder[:$ID.*]}/[files,directories,virtual]/ files, directories, virtual
			$part = array_shift($dav_parts);
			if ($pathObj->_main_dir == 'DRUKI') {
				myLog(__CLASS__, __FUNCTION__, __LINE__, "TODO: ({$davpath}) 5rd part: main({$pathObj->_main_dir}) ...");
			}
			else if ($pathObj->_main_dir == 'PROJEKTY') {
				if ($pathObj->_proj_pisma) {// virtual PISMA/{pismo_file}
					$pismo_id = intval(reset(explode('.', $part)));
					if ($pismo_id <= 0) {
						return null;
					}
					$file->realpath = self::findRealPath('PISMO', $pismo_id);
					if (!$file->realpath) {
						myLog(__CLASS__, __FUNCTION__, __LINE__, "TODO: ({$davpath}) 5rd part: 404 ".json_encode($file));
						return null;
					}
					$file->is_dir = false;
					return $file;
				}
				else if ($pathObj->_pismo_id > 0) {// virtual PISMO.{$pathObj->_pismo_id}
					$file->realpath = self::findRealPath('PISMO', $pathObj->_pismo_id);
					if (!$file->realpath) {
						myLog(__CLASS__, __FUNCTION__, __LINE__, "TODO: ({$davpath}) 5rd part: 404 ".json_encode($file));
						return null;
					}
					$file->is_dir = false;
					return $file;
				}
				else if ($pathObj->_druk_id > 0) {// virtual DRUK.{$pathObj->_druk_id}
					$file->realpath .= '/' . $part;
				}
				else {
					$file->realpath .= '/' . $part;
					myLog(__CLASS__, __FUNCTION__, __LINE__, "TODO: ({$davpath}) 5rd part: main({$pathObj->_main_dir}) ...");
				}
			}
		}

		while (!empty($dav_parts)) {
			$part = array_shift($dav_parts);
			$file->realpath .= '/' . $part;
		}
		if (!file_exists($file->realpath)) {
			return null;
		}
		$file->is_dir = is_dir($file->realpath);

		return $file;
	}

	function findRealPath($type, $id) {
		$file = null;
		if ($type == 'PROJEKT') {
			$basePath = WebdavHelper::getBaseDir('IN7_MK_BAZA_DYSTRYBUCJI_COLUMN');
			$files = glob("{$basePath}/{$id}.*", GLOB_NOSORT);
			if (empty($files)) {
				return false;
			}
			$file = reset($files);
			return $file;
		}
		else if ($type == 'DRUK') {
			$basePath = WebdavHelper::getBaseDir('CRM_LISTA_ZASOBOW_COLUMN');
			$files = glob("{$basePath}/{$id}.*", GLOB_NOSORT);
			if (empty($files)) {
				return false;
			}
			$file = reset($files);
			return $file;
		}
		else if ($type == 'PISMO') {
			$basePath = WebdavHelper::getBaseDir('IN7_DZIENNIK_KORESP_COLUMN');
			$pismoType = WebdavHelper::getPismoType($id);
			if ($pismoType) {
				$files = glob("{$basePath}/{$pismoType}/{$id}.*", GLOB_NOSORT);
				if (empty($files)) {
					return false;
				}
				$file = reset($files);
			}
			return $file;
		}
	}

	function findRealPaths($type, $ids) {
		$foundFiles = null;
		if ($type == 'PISMA') {
			$basePath = WebdavHelper::getBaseDir('IN7_DZIENNIK_KORESP_COLUMN');
			foreach ($ids as $id) {
				$files = glob("{$basePath}/*/{$id}.*", GLOB_NOSORT);
				if (empty($files)) {
					continue;
				}
				$file = reset($files);
				$foundFiles []= $file;
			}
		}
		return $foundFiles;
	}

	function getFileSize($davpath, $user = null) {
		$file = self::getRealFile($davpath, $user);
		if ($file) {
			if ($file->is_dir === false) {
				if ($file->realpath && file_exists($file->realpath)) {
					return filesize($file->realpath);
				}
			}
		}
		return 0;
	}

	function getFileETag($davpath, $user = null) {
		$file = self::getRealFile($davpath, $user);
		if ($file) {
			if ($file->is_dir === false) {
				return '"' . md5_file($file->realpath) . '"';
			}
		}
		return '"0"';
	}

	function getFileContent($davpath, $user = null) {
		$file = self::getRealFile($davpath, $user);
		if ($file) {
			if ($file->is_dir === false) {
				return fopen($file->realpath, 'r');
			}
		}
		return null;
	}

	function file_exists($davpath) {
		$file = self::getRealFile($davpath);
		if (!$file) {
			return false;
		}
		return file_exists($file->realpath);
	}

	function setName($davpath, $newName) {
		$parts = explode('/', $davpath);
		$oldName = array_pop($parts);
		$davpath = implode('/', $parts);

		$file = self::getRealFile($davpath);
		if (!$file) {
			return 403;
		}

		if (false === strpos($file->perms, 'C')) {
			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$newName}) oldName({$oldName}) file not renamed - 403");
			return 403;
		}

		@rename($file->realpath . '/' . $oldName, $file->realpath . '/' . $newName);
		if (!file_exists($file->realpath . '/' . $newName)) {
			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$newName}) file not renamed - return 403");
			return 403;
		}
		myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$newName}) file renamed - return 0");
		return 0;
	}

	function getMimeType($davpath, $user = null) {
		$file = self::getRealFile($davpath, $user);

		if ($file->is_dir) {
			return 'httpd/unix-directory';
		}

		$mimeType = self::getMimeTypeByExt($file->realpath);

		if ($mimeType == 'application/octet-stream') {
			// PHP >= 5.3.0, PECL fileinfo >= 0.1.0
			if (function_exists('finfo_open')) {
				if ($finfo = finfo_open(FILEINFO_MIME)) {
					$info = @strtolower(finfo_file($finfo, $file->realpath));
					if ($info) {
						$mimeType = substr($info, 0, strpos($info, ';'));
					}
					finfo_close($finfo);
				}
			} else if (function_exists('mime_content_type')) {
				$mimeType = mime_content_type($file->realpath);
			}
		}
		myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) mimeType({$mimeType})");
		return $mimeType;
	}

	function getMimeTypeByExt($realPath) {
		$mimeTypesByExt = array(
			'css'=>'text/css',
			'flac'=>'audio/flac',
			'gif'=>'image/gif',
			'gzip'=>'application/x-gzip',
			'gz'=>'application/x-gzip',
			'html'=>'text/html',
			'htm'=>'text/html',
			'ics'=>'text/calendar',
			'ical'=>'text/calendar',
			'jpeg'=>'image/jpeg',
			'jpg'=>'image/jpeg',
			'js'=>'application/javascript',
			'oga'=>'audio/ogg',
			'ogg'=>'audio/ogg',
			'ogv'=>'video/ogg',
			'pdf'=>'application/pdf',
			'png'=>'image/png',
			'svg'=>'image/svg+xml',
			'tar'=>'application/x-tar',
			'tgz'=>'application/x-compressed',
			'tar.gz'=>'application/x-compressed',
			'tif'=>'image/tiff',
			'tiff'=>'image/tiff',
			'txt'=>'text/plain',
			'zip'=>'application/zip',
			'wav'=>'audio/wav',
			'odt'=>'application/vnd.oasis.opendocument.text',
			'ods'=>'application/vnd.oasis.opendocument.spreadsheet',
			'odg'=>'application/vnd.oasis.opendocument.graphics',
			'odp'=>'application/vnd.oasis.opendocument.presentation',
			'pages'=>'application/x-iwork-pages-sffpages',
			'numbers'=>'application/x-iwork-numbers-sffnumbers',
			'keynote'=>'application/x-iwork-keynote-sffkey',
			'kra'=>'application/x-krita',
			'mp3'=>'audio/mpeg',
			'doc'=>'application/msword',
			'docx'=>'application/msword',
			'xls'=>'application/msexcel',
			'xlsx'=>'application/msexcel',
			'php'=>'application/x-php',
			'exe'=>'application/x-ms-dos-executable',
			'pl'=>'application/x-pearl',
			'py'=>'application/x-python',
			'blend'=>'application/x-blender',
			'xcf'=>'application/x-gimp',
			'psd'=>'application/x-photoshop',
			'xml'=>'application/xml',
			'avi'=>'video/x-msvideo',
			'dv'=>'video/dv',
			'm2t'=>'video/mp2t',
			'mp4'=>'video/mp4',
			'm4v'=>'video/mp4',
			'mpg'=>'video/mpeg',
			'mpeg'=>'video/mpeg',
			'mov'=>'video/quicktime',
			'webm'=>'video/webm',
			'wmv'=>'video/x-ms-asf',
			'py'=>'text/x-script.phyton',
			'vcf' => 'text/vcard',
			'vcard' => 'text/vcard',
			'doc'=>'application/msword',
			'docx'=>'application/msword',
			'xls'=>'application/msexcel',
			'xlsx'=>'application/msexcel',
			'ppt'=>'application/mspowerpoint',
			'pptx'=>'application/mspowerpoint',
			'sgf' => 'application/sgf',
			'cdr' => 'application/coreldraw',
			'impress' => 'text/impress',
			'ai' => 'application/illustrator',
			'epub' => 'application/epub+zip',
			'mobi' => 'application/x-mobipocket-ebook',
			'exe' => 'application',
			'msi' => 'application'
		);

		if (strpos($realPath, '.')) {
			$extension = substr(strtolower(strrchr(basename($realPath), ".")), 1);
			$mimeType = (isset($mimeTypesByExt[$extension]))? $mimeTypesByExt[$extension] : 'application/octet-stream';
		} else {
			$mimeType = 'application/octet-stream';
		}
		return $mimeType;
	}

	function createDirectory($davpath, $name, $user = null) {
		$file = self::getRealFile($davpath);
		if (!$file) {
			return 403;
		}

		// TODO: check perms RWXC by user
		if (false === strpos($file->perms, 'C')) {
			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$name}) file not created - 403");
			return 403;
		}

		// TODO: check $name - remove special chars
		//$name = 'TODO-newdir';

		//myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$name}) FILE:" . json_encode($file));

		@mkdir($file->realpath . '/' . $name, 0777);
		if (!file_exists($file->realpath . '/' . $name)) {
			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$name}) file not created - return 403");
			return 403;
		}
		myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$name}) file created - return 0");
		return 0;
	}

	function createFile($davpath, $name, $data = null) {
		/*
Creates a new file in the directory

Data will either be supplied as a stream resource, or in certain cases as a string. Keep in mind that you may have to support either.

After successful creation of the file, you may choose to return the ETag of the new file here.

The returned ETag must be surrounded by double-quotes (The quotes should be part of the actual string).

If you cannot accurately determine the ETag, you should not return it. If you don't store the file exactly as-is (you're transforming it somehow) you should also not return an ETag.

This means that if a subsequent GET to this new file does not exactly return the same contents of what was submitted here, you are strongly recommended to omit the ETag.
		 */

		$file = self::getRealFile($davpath);
		if (!$file) {
			return 403;
		}

		if (false === strpos($file->perms, 'C')) {
			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$name}) file not created - 403");
			return 403;
		}

		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) name({$name}) HTTP_OC_CHUNKED(".isset($_SERVER['HTTP_OC_CHUNKED']).")");
			return 501;
/*
			$info = OC_FileChunking::decodeName($name);
			if (empty($info)) {
				throw new Sabre_DAV_Exception_NotImplemented();
			}
			$chunk_handler = new OC_FileChunking($info);
			$chunk_handler->store($info['index'], $data);
			if ($chunk_handler->isComplete()) {
				$newPath = $this->path . '/' . $info['name'];
				$chunk_handler->file_assemble($newPath);
				return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
			}
*/
		}
		else {
			// TODO: $name - remove special chars
			$newPath = $file->realpath . '/' . $name;

			// mark file as partial while uploading
			$partPath = $newPath . '.part';

			file_put_contents($partPath, $data);

			if (isset ($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PUT' ) {
				if (isset($_SERVER['CONTENT_LENGTH'])) {
					$expected = $_SERVER['CONTENT_LENGTH'];
					$actual = filesize($partPath);
					if ($actual != $expected) {
						unlink($partPath);
						return 400;
					}
				}
			}

			rename($partPath, $newPath);

			return '"' . md5_file($newPath) . '"';
		}
		return null;
	}

	function delete($davpath, $user = null) {
		$file = self::getRealFile($davpath);
		if (!$file) {
			return 403;
		}

//		if (false === strpos($file->perms, 'X')) {
//			myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) file not deleted - 403");
//			return 403;
//		}

		$rootDir = self::getRootDir();
		$trashDir = self::getTrashDir();
		$trashPath = str_replace($rootDir, $trashDir, $file->realpath);
		$trashPath = explode('/', $trashPath);
		$oldName = array_pop($trashPath);
		$oldName .= '.' . time();
		$trashPath = implode('/', $trashPath);
		myLog(__CLASS__, __FUNCTION__, __LINE__, "({$davpath}) trashPath({$trashPath}/{$oldName})");
		@mkdir($trashPath, 0777, true);
		@rename($file->realpath, $trashPath . '/' . $oldName);

		return 0;
	}

	function getPismoType($id) {
		$db = DB::getDB();
		$sql = "select kor.`K_TYP_KORESP` from `IN7_DZIENNIK_KORESP` as kor where kor.`ID`='{$id}' ";
		$res = $db->query($sql);
		if ($r = $db->fetch($res)) {
			return $r->K_TYP_KORESP;
		}
		return null;
	}

	function getProjektyIds($user_name = null) {
		$projektyIds = array();
		$db = DB::getDB();
		$sql = "select proj.`ID` from `IN7_MK_BAZA_DYSTRYBUCJI` as proj where proj.`L_APPOITMENT_USER`='{$user_name}' ";
		$res = $db->query($sql);
		while ($r = $db->fetch($res)) {
			$projektyIds[] = $r->ID;
		}
		return $projektyIds;
	}

	/*
	 * PROJEKTY <--> DRUKI : IN7_MK_BAZA_DYSTRYBUCJI.ID_ZASOB_REVITALIZE <--> CRM_LISTA_ZASOBOW.ID
	 */
	function getProjektyIdsByDruk($druk_id, $user = null) {
		$projektyIds = array();

		//$projektyIds[] = 1756;// TODO: test - read from DB by PROJEKTY <--> DRUKI : IN7_MK_BAZA_DYSTRYBUCJI.ID_ZASOB_REVITALIZE <--> CRM_LISTA_ZASOBOW.ID

		return $projektyIds;
	}

	/*
	 * PROJEKTY <--> DRUKI : IN7_MK_BAZA_DYSTRYBUCJI.ID_ZASOB_REVITALIZE <--> CRM_LISTA_ZASOBOW.ID
	 */
	function getDrukiIdsByProjekt($projekt_id, $user = null) {
		$drukiIds = array();
		$db = DB::getDB();
		$sql = "select proj.`ID_ZASOB_REVITALIZE`
			from `IN7_MK_BAZA_DYSTRYBUCJI` as proj
			where proj.`ID`='{$projekt_id}' and proj.`ID_ZASOB_REVITALIZE`>0
		";
		$res = $db->query($sql);
		while ($r = $db->fetch($res)) {
			$drukiIds[] = $r->ID_ZASOB_REVITALIZE;
		}
		return $drukiIds;
	}

	/*
	 * PROJEKTY <--> PISMA : IN7_DZIENNIK_KORESP.ID_PROJECT <--> IN7_MK_BAZA_DYSTRYBUCJI.ID
	 */
	function getPismaIdsByProjekt($projekt_id, $user = null) {
		$pismaIds = array();
		$db = DB::getDB();
		$sql = "select kor.`ID` from `IN7_DZIENNIK_KORESP` as kor where kor.`ID_PROJECT`='{$projekt_id}' ";
		$res = $db->query($sql);
		while ($r = $db->fetch($res)) {
			$pismaIds[] = $r->ID;
		}
		return $pismaIds;
	}

	function auth() {
		Lib::loadClass('User');
		if ( ! User::logged() ) {
			//myLog(__CLASS__, __FUNCTION__, __LINE__, "not logged in...");
			if (!isset($_SERVER['PHP_AUTH_USER'])) {
				header('WWW-Authenticate: Basic realm="SE"');
				header('HTTP/1.0 401 Unauthorized');
				die('Access denied');
			} else {
				$ADM_ACCOUNT = V::get('PHP_AUTH_USER', '', $_SERVER);
				$ADM_PASSWD = V::get('PHP_AUTH_PW', '', $_SERVER);

				global $LDAP_SERVER_AUTH;
				if (isset($LDAP_SERVER_AUTH)) {
					SEF('AUTH_LDAP_CLIENT');
					ob_start();
					AUTH_LDAP_CLIENT($ADM_ACCOUNT, $ADM_PASSWD);
					ob_end_clean();// - Clean (erase) the output buffer and turn off output buffering
				} else {
					SEF('AUTHORIZE_USER_LOGIN');
					ob_start();
//					AUTHORIZE_USER_LOGIN( $ADM_ACCOUNT, $ADM_PASSWD );
					$db = DB::getDB();
					$sql = "SELECT u.*
						from `ADMIN_USERS` as u
						where
							u.`ADM_ACCOUNT`='{$ADM_ACCOUNT}'
							and ( u.`ADM_PASSWD`='{$ADM_PASSWD}' or u.`ADM_PASSWD`=md5('{$ADM_PASSWD}') )
							and u.`A_STATUS` in('WAITING','NORMAL')
						LIMIT 0,1;
					";
					$res = $db->query($sql);
					if (!$res) die("Blad zapytania - auth!");
					$num_rows = $db->num_rows($res);
					if ($num_rows == 0) {
						echo "Podales zlego uzytkownika lub/i haslo()" ;
					}
					else if ($num_rows == 1) {
						if ($h = $db->fetch($res)) {
							// if(( $ADM_ACCOUNT = $h->ADM_ACCOUNT ) && ( $ADM_ACCOUNT = $h->ADM_ACCOUNT )) {
							$_SESSION['ADM_PASSWD']=$ADM_PASSWD;
							$_SESSION['AUTHORIZE_USER']="$h->ADM_ACCOUNT";
							$_SESSION['ADM_ACCOUNT']="$h->ADM_ACCOUNT";
							$_SESSION['ADM_AREA']="$h->ADM_AREA";
							$_SESSION['ADM_NAME']="$h->ADM_NAME";
							$_SESSION['ADM_TECH_WORKER']="$h->ADM_TECH_WORKER";
							$_SESSION['ADM_COMPANY']="$h->ADM_COMPANY";
							$_SESSION['ADM_ADMIN_LEVEL']="$h->ADM_ADMIN_LEVEL";
							$_SESSION['ADM_PHONE']="$h->ADM_PHONE";
							$_SESSION['ADM_ADMIN_EXPIRE']="$h->ADM_ADMIN_EXPIRE";
							$_SESSION['ADM_ADMIN_DESC']="$h->ADM_ADMIN_DESC";
							$_SESSION['ADM_ID']="$h->ID";
						}
					}
					ob_end_clean();// - Clean (erase) the output buffer and turn off output buffering
				}

				if ( ! User::logged() ) {
					header('WWW-Authenticate: Basic realm="SE"');
					header('HTTP/1.0 401 Unauthorized');
					die('Access denied');
				}
			}
		} else {
			//myLog(__CLASS__, __FUNCTION__, __LINE__, "logged in...");
		}
	}

	/**
	 * BUG: nie zadziała, bo wymaga albo hasła jawnym tekstem, albo dodatkowej kolumny z hashem do autoryzacji Digest
	 */
	function auth_TODO_DIGEST() {
		$realm = 'Restricted area';

		if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
			header('HTTP/1.1 401 Unauthorized');
			header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
			die('Access denied');
		}

		// analyze the PHP_AUTH_DIGEST variable
		$data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST']);
		myLog(__CLASS__, __FUNCTION__, __LINE__, " data: ".json_encode($data));
		if (!$data) {// || !isset($users[$data['username']])
			die('Wrong Credentials!');
		} else {
			//user => password
			$users = array('admin' => 'mypass', 'pl' => 'pl');

			// TODO: fetch $data['username'] password from DB
			// TODO: if user not exists die('User not exists!');

			Lib::loadClass('User');
			if ( ! User::logged() ) {
				$ADM_ACCOUNT = V::get('ADM_ACCOUNT', '', $_REQUEST);
				$ADM_PASSWD = V::get('ADM_PASSWD', '', $_REQUEST);

				global $LDAP_SERVER_AUTH;
				if (isset($LDAP_SERVER_AUTH)) {
					SEF('AUTH_LDAP_CLIENT');
					ob_start();
					AUTH_LDAP_CLIENT($ADM_ACCOUNT, $ADM_PASSWD);
					ob_end_clean();// - Clean (erase) the output buffer and turn off output buffering
				} else {
					SEF('AUTHORIZE_USER_LOGIN');
					ob_start();
					AUTHORIZE_USER_LOGIN( $ADM_ACCOUNT, $ADM_PASSWD );
					ob_end_clean();// - Clean (erase) the output buffer and turn off output buffering
				}
			}
		}

		// generate the valid response
		$A1 = $data['username'] . ':' . $realm . ':' . $users[$data['username']];
		$A2 = $_SERVER['REQUEST_METHOD'].':'.$data['uri'];
		$valid_response = md5(implode(':', array(md5($A1), $data['nonce'], $data['nc'], $data['cnonce'], $data['qop'], md5($A2))));

		if ($data['response'] != $valid_response) {
			die('Wrong Credentials!');
		}
		// ok, valid username & password
		//echo 'You are logged in as: ' . $data['username'];
	}

	/**
	 * Function to parse the http auth header
	 */
	function http_digest_parse($txt) {
		// protect against missing data
		$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
		$data = array();
		$keys = implode('|', array_keys($needed_parts));

		preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);

		foreach ($matches as $m) {
			$data[$m[1]] = $m[3] ? $m[3] : $m[4];
			unset($needed_parts[$m[1]]);
		}

		return $needed_parts ? false : $data;
	}

}
