<?php
  /**************************************************************************\
  * eGroupWare API - VFS                                                     *
  * This file written by Vinicius Cubas Brand, strongly based on vfs_sql of  *
  *		Jason Wies (Zone) <zone@phpgroupware.org>                            *
  * This class handles file/dir access for eGroupWare                        *
  * Copyright (C) 2001 Jason Wies, (C) 2004 Vinicius Cubas Brand             *
  *   and (c) 2006 Benjamin Donnachie					     *
  * -------------------------------------------------------------------------*
  * This library is part of the eGroupWare API                               *
  * http://www.egroupware.org/api                                            * 
  * ------------------------------------------------------------------------ *
  * This library is free software; you can redistribute it and/or modify it  *
  * under the terms of the GNU Lesser General Public License as published by *
  * the Free Software Foundation; either version 2.1 of the License,         *
  * or any later version.                                                    *
  * This library is distributed in the hope that it will be useful, but      *
  * WITHOUT ANY WARRANTY; without even the implied warranty of               *
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     *
  * See the GNU Lesser General Public License for more details.              *
  * You should have received a copy of the GNU Lesser General Public License *
  * along with this library; if not, write to the Free Software Foundation,  *
  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA            *
  \**************************************************************************/

	/*!
	@class vfs
	@abstract Virtual File System with SQL backend - version 2
	@description Authors: Zone, viniciuscb
	*/

	# viniciuscb 2004-09-06 Upgraded this class to the sql implementation v2.

	/* These are used in calls to extra_sql () */
	define ('DEFAULT_LOW_PERMS',0600);

	#upd viniciuscb 2004-09-06 Updated to the new database tables (the ones that
	#  match phpgw_vfs2*). I have created a class to handle mime types and one
	#  to handle the versioning system. 

	#upd viniciuscb 2005-03-11 Deleted some garbage

	#upd Benjamin Donnachie <benjamin@pythagoras.no-ip.org> 2006-01-09
	#  Fixed function in_docroot so that it works with asyncservices

	/**
	 * Class: vfs
	 *
	 *	Virtual File System class - SQL v.2 implementation
	 */
	class vfs extends vfs_shared
	{
		var $working_id;
		var $working_lid;
		var $meta_types;
		var $now;
		var $file_actions;
		var $vfs_mimetypes;
		var $vfs_versionsystem;
		var $vfs_customfields;
		var $vfs_sharing;
		var $db;
		var $db_hl;
		var $appfiles_root;

		var $Debug;

		//other attributes may be in the custom fields....

		var $attribute_tables = array(
			'file_id'       => 'phpgw_vfs2_files',	
			'owner_id'      => 'phpgw_vfs2_files',
			'createdby_id'  => 'phpgw_vfs2_files',
			'modifiedby_id' => 'phpgw_vfs2_files',
			'created'       => 'phpgw_vfs2_files',
			'modified'      => 'phpgw_vfs2_files',
			'size'			=> 'phpgw_vfs2_files',
			'mime_type'     => 'phpgw_vfs2_mimetypes',
			'comment'       => 'phpgw_vfs2_files',
			'app'           => 'phpgw_vfs2_files',
			'directory'     => 'phpgw_vfs2_files',
			'name'          => 'phpgw_vfs2_files',
			'link_directory'=> 'phpgw_vfs2_files',
			'link_name'     => 'phpgw_vfs2_files',
			'version'       => 'phpgw_vfs2_files'
		);

		//to external use.
		//if $custom_field_support is set, then this class have support to
		//custom fields.
		var $custom_field_support = 1;

		//if $search_support is set, then this class have support to
		//searching in files for a particular value in a particular property.
		var $search_support = 1;

		var $compress_support = 1;
		var $extract_support = 1;


		/*!
		@function vfs
		@abstract constructor, sets up variables
		*/
		function vfs ()
		{

			//just in case... this must change soon.
			if (@$GLOBALS['phpgw_info']['flags']['currentapp']=='filemanager')
			{
				echo "FILEMANAGER UNTESTED WITH VFS2. ABORTED.";
				$GLOBALS['phpgw']->common->phpgw_exit();
			}

			$this->db = clone($GLOBALS['phpgw']->db);
			$this->db_hl =& $GLOBALS['phpgw']->db_hl;
	
			$this->vfs_shared ();
			$this->basedir = $GLOBALS['phpgw_info']['server']['files_dir'];
			$this->working_id = $GLOBALS['phpgw_info']['user']['account_id'];
			$this->working_lid = $GLOBALS['phpgw']->accounts->id2name($this->working_id);
			$this->now = date ('Y-m-d H:i:s');


			/*
			   File/dir attributes, each corresponding to a database field.
			   Useful for use in loops If an attribute was added to the table,
			   add it here and possibly add it to set_attributes ()

			   set_attributes now uses this array().   07-Dec-01 skeeter
			*/

			$this->attributes[] = 'deleteable';
			$this->attributes[] = 'content';
			$this->attributes[] = 'is_backup';
			$this->attributes[] = 'shared';
			$this->attributes[] = 'proper_id';

			$this->attribute_tables['deleteable'] = 'phpgw_vfs2_files';
			$this->attribute_tables['content'] = 'phpgw_vfs2_files';
			$this->attribute_tables['is_backup'] = 'phpgw_vfs2_files';
			$this->attribute_tables['shared'] = 'phpgw_vfs2_files';
			$this->attribute_tables['proper_id'] = 'phpgw_vfs2_files';

			/*
			   Decide whether to use any actual filesystem calls (fopen(),
			   fread(), unlink(), rmdir(), touch(), etc.).  If not, then we're
			   working completely in the database.
			*/
			$this->file_actions = $GLOBALS['phpgw_info']['server']['file_store_contents'] == 'filesystem' ||
				!$GLOBALS['phpgw_info']['server']['file_store_contents'];

			// test if the files-dir is inside the document-root, and refuse
			// working if so
			if ($this->file_actions && $this->in_docroot($this->basedir))
			{
				$GLOBALS['phpgw']->common->phpgw_header();
				if ($GLOBALS['phpgw_info']['flags']['noheader']) 
				{
					echo parse_navbar();
				}
				echo '<p align="center"><font color="red"><b>'.lang('Path to user and group files HAS TO BE OUTSIDE of the webservers document-root!!!')."</b></font></p>\n";
				$GLOBALS['phpgw']->common->phpgw_exit();
			}

			/* We store the linked directories in an array now, so we don't
			 * have to make the SQL call again */




			$this->linked_dirs = array ();
			while ($GLOBALS['phpgw']->db->next_record ())
			{
				$this->linked_dirs[] = $GLOBALS['phpgw']->db->Record;
			}

			//set_vfs to use this very object (pass by reference)
			$this->vfs_mimetypes =& CreateObject('phpgwapi.vfs_mimetypes',false);
			$this->vfs_mimetypes->set_vfs($this);

			$this->vfs_versionsystem =& CreateObject('phpgwapi.vfs_versionsystem',false);
			$this->vfs_versionsystem->set_vfs($this);

			$this->vfs_customfields =& CreateObject('phpgwapi.vfs_customfields');

			$this->vfs_sharing =& CreateObject('phpgwapi.vfs_sharing');
		}

		/*!
		@function in_docroot
		@abstract test if $path lies within the webservers document-root
		*/
		function in_docroot($path)
		{
			$docroots = array(PHPGW_SERVER_ROOT,$_SERVER['DOCUMENT_ROOT']);

			foreach ($docroots as $docroot)
			{
				$len = strlen($docroot);

				if ($docroot != "" && $docroot == substr($path,0,$len))
				{
					$rest = substr($path,$len);

					if (!strlen($rest) || $rest[0] == DIRECTORY_SEPARATOR)
					{
						return True;
					}
				}
			}
			return False;
		}

		/*!
		@function get_id_from_path
		@abstract Given a Directory and file name, finds the correspondent
		   file_id
		@param $directory string
		@param $name string
		@result int the file_id in repository
		*/
		function get_id_from_path($directory,$name)
		{
			$where = array(
				'directory' => $directory,
				'name' => $name
			);

			$this->db->select('phpgw_vfs2_files','file_id',$where,
				__LINE__,__FILE__);
	
			if ($this->db->next_record())
			{
				return $this->db->Record['file_id'];
			}

			return false;
		}
		
		//the inverse way of $this->get_id_from_path
		function get_path_from_id($id)
		{
			$where = array(
				'file_id' => $id
			);

			$this->db->select('phpgw_vfs2_files','directory,name',$where,
				__LINE__,__FILE__);
	
			if ($this->db->next_record())
			{
				return $this->db->Record['directory'].'/'.$this->db->Record['name'];
			}

			return false;
		}

		/*
		 * See vfs_shared
		 */
		function get_journal ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT),
					'type'	=> False
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (!$this->acl_check (array(
					'string' => $p->fake_full_path,
					'relatives' => array ($p->mask)
				)))
			{
				return False;
			}

			//find the file_id from a file based on directory and name
			$file_id = $this->get_id_from_path($p->fake_leading_dirs_clean,$p->fake_name_clean);


			if ($file_id === false)
			{
				return false;
			}
			return $this->vfs_versionsystem->get_journal($file_id);
		}

		/*
		 * See vfs_shared
		 */
		function acl_check ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT),
					'operation'	=> PHPGW_ACL_READ,
					'must_exist'	=> False
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			/* Accommodate special situations */
			if ($this->override_acl || $data['relatives'][0] == RELATIVE_USER_APP || $GLOBALS['phpgw_info']['user']['apps']['admin'])
			{
				return True;
			}

			if (!$data['owner_id'])
			{
				$p = $this->path_parts (array(
						'string'	=> $data['string'],
						'relatives'	=> array ($data['relatives'][0])
					)
				);

				/* Temporary, until we get symlink type files set up */
				if ($p->outside)
				{
					return True;
				}

				/* Read access is always allowed here, but nothing else is */
				if ($data['string'] == '/' || $data['string'] == $this->fakebase)
				{
					if ($data['operation'] == PHPGW_ACL_READ)
					{
						return True;
					}
					else
					{
						return False;
					}
				}

				/* If the file doesn't exist, we get ownership from the parent
				 * directory */
				if (!$this->file_exists (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask)
					))
				)
				{
					if ($data['must_exist'])
					{
						return False;
					}

					$data['string'] = $p->fake_leading_dirs;
					$p2 = $this->path_parts (array(
							'string'	=> $data['string'],
							'relatives'	=> array ($p->mask)
						)
					);

					if (!$this->file_exists (array(
							'string'	=> $data['string'],
							'relatives'	=> array ($p->mask)
						))
					)
					{
						return False;
					}
				}
				else
				{
					$p2 = $p;
				}
				/*
				   We don't use ls () to get owner_id as we normally would,
				   because ls () calls acl_check (), which would create an infinite loop
				*/
				$query = $GLOBALS['phpgw']->db->query ("SELECT owner_id FROM phpgw_vfs2_files WHERE directory='".
					$GLOBALS['phpgw']->db->db_addslashes($p2->fake_leading_dirs_clean)."' AND name='".
					$GLOBALS['phpgw']->db->db_addslashes($p2->fake_name_clean)."'", __LINE__, __FILE__);
				$GLOBALS['phpgw']->db->next_record ();

				$owner_id = $GLOBALS['phpgw']->db->Record['owner_id'];
			}
			else
			{
				$owner_id = $data['owner_id'];
			}

			/* This is correct.  The ACL currently doesn't handle undefined values correctly */
			if (!$owner_id)
			{
				$owner_id = 0;
			}

			$user_id = $GLOBALS['phpgw_info']['user']['account_id'];

			/* They always have access to their own files */
			if ($owner_id == $user_id)
			{
				return True;
			}

				
			#viniciuscb: Check if the file is inside a dir which is an application dir. 
			#            If so, see if the dir has permissions in vfs_sharing. If not, 
			#            inherit permissions given by the source application

			if ($this->is_appfolder($p2->fake_full_path))
			{
				$file_id = $this->get_file_id(array(
					'string'    => $p2->fake_full_path,
					'relatives' => array($p2->mask)));
	
				$rights = $this->vfs_sharing->get_file_permissions($GLOBALS['phpgw_info']['user']['account_id'],$file_id);

				if ($rights & $data['operation'])
				{
					return true;
				}
				
				$exp_path = explode('/',$p2->fake_full_path);
				$appname = $exp_path[2];
				$id = $exp_path[3];

				//get hooks to know node permission
				$resp = $GLOBALS['phpgw']->hooks->process(array(
					'location' => 'files_info',
					'account_id' => $GLOBALS['phpgw_info']['user']['account_id']
					));

				return ($resp[$appname][$id]['permissions'] & $data['operation']);
			}

			#viniciuscb: rethink the group files role and working schema
			/* Check if they're in the group */
			$memberships = $GLOBALS['phpgw']->accounts->membership ($user_id);

			if (is_array ($memberships))
			{
				foreach ($memberships as $group_array)
				{
					if ($owner_id == $group_array['account_id'])
					{
						$group_ok = 1;
						break;
					}
				}
			}

			$acl = CreateObject ('phpgwapi.acl', $owner_id);
			$acl->account_id = $owner_id;
			$acl->read_repository ();

			$file_id = $this->get_file_id(array(
				'string'    => $p2->fake_full_path,
				'relatives' => array($p2->mask)));

			$rights = $this->vfs_sharing->get_file_permissions($user_id,$file_id);

			if ($this->Debug)
			{
				echo "<br>\nRIGHTS OF THE FILE ".$p2->fake_full_path." ARE=$rights<br>\n";
			}
			
			if ($rights & $data['operation'])
			{
				return True;
			}
			elseif (!$rights && $group_ok)
			{
				$conf =& CreateObject('phpgwapi.config', 'phpgwapi');
				$conf->read_repository();
				if ($conf->config_data['acl_default'] == 'grant')
				{
					return True;
				}
				else
				{
					return False;
				}
			}
			else
			{
				return False;
			}
		}

		/*
		 * See vfs_shared
		 */
		function read ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (!$this->acl_check (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'operation'	=> PHPGW_ACL_READ
				))
			)
			{
				return False;
			}

			$conf =& CreateObject('phpgwapi.config', 'phpgwapi');
			$conf->read_repository();
			if ($this->file_actions || $p->outside)
			{
				if ($fp = fopen ($p->real_full_path, 'rb'))
				{
					$contents = fread ($fp, filesize ($p->real_full_path));
					fclose ($fp);
				}
				else
				{
					$contents = False;
				}
			}
			else
			{
				$ls_array = $this->ls (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask),
					)
				);

				$contents = $ls_array[0]['content'];
			}

			return $contents;
		}

		/*
		 * See vfs_shared
		 */
		function write ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT),
					'content'	=> ''
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if ($this->file_exists (array (
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask)
				))
			)
			{
				$acl_operation = PHPGW_ACL_EDIT;
				$journal_operation = VFS_OPERATION_EDITED;
			}
			else
			{
				$acl_operation = PHPGW_ACL_ADD;
			}

			if (!$this->acl_check (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'operation'	=> $acl_operation
				))
			)
			{
				return False;
			}

			umask(0177);

			/*
			 * If 'string' doesn't exist, touch () creates both the file and
			 * the database entry If 'string' does exist, touch () sets the
			 * modification time and modified by
			*/
			$this->touch (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask)
				)
			);

			$conf =& CreateObject('phpgwapi.config', 'phpgwapi');
			$conf->read_repository();


			$file_id = $this->get_id_from_path($p->fake_leading_dirs_clean,$p->fake_name_clean);

			//Saves a snapshot in the journal
			if ($journal_operation)
			{
				$this->vfs_versionsystem->save_snapshot($file_id,$journal_operation);
			}

			if ($this->file_actions)
			{
				if ($fp = fopen ($p->real_full_path, 'wb'))
				{
					fwrite ($fp, $data['content'], strlen ($data['content']));
					fclose ($fp);
					$write_ok = 1;
				}
			}

			if ($write_ok || !$this->file_actions)
			{

				
				if ($this->file_actions)
				{
					$set_attributes_array = array(
						'size' => filesize ($p->real_full_path)
					);
				}
				else
				{
					$set_attributes_array = array (
						'size'	=> strlen ($data['content']),
						'content'	=> $data['content']
					);
				}

				if ($journal_operation)
				{
					$this->vfs_versionsystem->commit($file_id);
				}



				$this->set_attributes (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'attributes'	=> $set_attributes_array
				));

				return True;
			}
			else
			{
				return False;
			}
		}

		/*
		 * See vfs_shared
		 */
		function touch ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array(
				'relatives'	=> array (RELATIVE_CURRENT)
			);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$account_id = $GLOBALS['phpgw_info']['user']['account_id'];
			$currentapp = $GLOBALS['phpgw_info']['flags']['currentapp'];

			$p = $this->path_parts (array(
				'string'	=> $data['string'],
				'relatives'	=> array ($data['relatives'][0])
			));

			umask (0177);

			if ($this->file_actions)
			{
				/*
				   PHP's touch function will automatically decide whether to
				   create the file or set the modification time
				*/
				$rr = @touch ($p->real_full_path);

				if ($p->outside)
				{
					return $rr;
				}
			}

			/* We, however, have to decide this ourselves */
			if ($this->file_exists (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask)
				))
			)
			{
				if (!$this->acl_check (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask),
						'operation'	=> PHPGW_ACL_EDIT
					)))
				{
					return False;
				}

				$vr = $this->set_attributes (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask),
						'attributes'	=> array(
									'modifiedby_id' => $account_id,
									'modified' => $this->now
								)
						)
					);
			}
			else
			{
				if (!$this->acl_check (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask),
						'operation'	=> PHPGW_ACL_ADD
					))
				)
				{
					return False;
				}

				//TODO VCB insert other fields

				$insert_data = array(
					'owner_id' => $this->working_id,
					'directory' => $p->fake_leading_dirs_clean,
					'name' => $p->fake_name_clean
				);

				$query = $this->db->insert('phpgw_vfs2_files',$insert_data,$insert_data,__LINE__,__FILE__);

				$file_id = $this->get_id_from_path($p->fake_leading_dirs_clean,$p->fake_name_clean);

				$this->vfs_versionsystem->create_initial_version($file_id);

				$this->set_attributes(array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'attributes'	=> array (
								'createdby_id' => $account_id,
								'created' => $this->now,
								'size' => 0,
								'deleteable' => 'Y',
								'app' => $currentapp
							)
					)
				);

				$this->correct_attributes (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask)
					)
				);
			}

			if ($rr || $vr || $query)
			{
				return True;
			}
			else
			{
				return False;
			}
		}

		/*
		 * See vfs_shared
		 * If $data['symlink'] the file is symlinked instead of copied
		 */
		function cp ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT, RELATIVE_CURRENT),
					'journal' => true
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$account_id = $GLOBALS['phpgw_info']['user']['account_id'];

			$f = $this->path_parts (array(
					'string'	=> $data['from'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			$t = $this->path_parts (array(
					'string'	=> $data['to'],
					'relatives'	=> array ($data['relatives'][1])
				)
			);

			if (!$this->acl_check (array(
					'string'	=> $f->fake_full_path,
					'relatives'	=> array ($f->mask),
					'operation'	=> PHPGW_ACL_READ
				))
			)
			{
				trigger_error('vfs->cp: could not copy file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Without permission to read from source location. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
				return False;
			}

			if ($exists = $this->file_exists (array(
					'string'	=> $t->fake_full_path,
					'relatives'	=> array ($t->mask)
				))
			)
			{
				if (!$this->acl_check (array(
						'string'	=> $t->fake_full_path,
						'relatives'	=> array ($t->mask),
						'operation'	=> PHPGW_ACL_EDIT
					))
				)
				{
					trigger_error('vfs->cp: could not copy file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Without permission to edit destination. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
					return False;
				}
			}
			else
			{
				if (!$this->acl_check (array(
						'string'	=> $t->fake_full_path,
						'relatives'	=> array ($t->mask),
						'operation'	=> PHPGW_ACL_ADD
					))
				)
				{

					trigger_error('vfs->cp: could not copy file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Without permission to create new file. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
					return False;
				}
			}

			umask(0177);

			if ($this->file_type (array(
					'string'	=> $f->fake_full_path,
					'relatives'	=> array ($f->mask)
				)) != 'Directory'
			)
			{
				if ($this->file_actions)
				{
					if (@$data['symlink'])
					{
						if ($exists)
						{
							@unlink($t->real_full_path);
						}
						if (!symlink($f->real_full_path, $t->real_full_path))
						{
							trigger_error('vfs->cp: could not copy file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Without permission to create symbolic link. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
							return False;
						}
					}
					elseif (!copy ($f->real_full_path, $t->real_full_path))
					{
						trigger_error('vfs->cp: could not copy file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
						return False;
					}

					$size = filesize ($t->real_full_path);
				}
				else
				{
					$content = $this->read (array(
							'string'	=> $f->fake_full_path,
							'relatives'	=> array ($f->mask)
						)
					);


					$size = strlen ($content);
				}

				if ($t->outside)
				{
					return True;
				}

				$ls_array = $this->ls (array(
						'string'	=> $f->fake_full_path,
						'relatives'	=> array ($f->mask),
						'checksubdirs'	=> False,
						'mime_type'	=> False,
						'nofiles'	=> True
					)
				);
				$record = $ls_array[0];

				if ($this->file_exists (array(
						'string'	=> $data['to'],
						'relatives'	=> array ($data['relatives'][1])
					))
				)
				{ //Overwrite

/*
					$query = $GLOBALS['phpgw']->db->query ("UPDATE phpgw_vfs SET owner_id='$this->working_id', directory='".
						$GLOBALS['phpgw']->db->db_addslashes($t->fake_leading_dirs_clean)."', name='".
						$GLOBALS['phpgw']->db->db_addslashes($t->fake_name_clean)."' WHERE owner_id='$this->working_id' AND directory='".
						$GLOBALS['phpgw']->db->db_addslashes($t->fake_leading_dirs_clean)."' AND name='".
						$GLOBALS['phpgw']->db->db_addslashes($t->fake_name_clean)."'", __LINE__, __FILE__);*/

					$ls_array_dest = $this->ls (array(
							'string'	=> $t->fake_full_path,
							'relatives'	=> array ($t->mask),
							'checksubdirs'	=> False,
							'mime_type'	=> False,
							'nofiles'	=> True
						)
					);
					$record_dest = $ls_array_dest[0];

					$this->vfs_versionsystem->save_snapshot($record_dest['file_id'],VFS_OPERATION_EDITED,'Overwritten by copy of '.$f->fake_full_path_clean);
		
					$set_attributes_array = array (
						'createdby_id' => $account_id,
						'created' => $this->now,
						'size' => $size,
						'mime_type' => $record['mime_type'],
						'deleteable' => $record['deleteable'],
						'comment' => $record['comment'],
						'app' => $record['app']
					);

					if (!$this->file_actions)
					{
						$set_attributes_array['content'] = $content;
					}

					$res = $this->set_attributes(array(
						'string'	=> $t->fake_full_path,
						'relatives'	=> array ($t->mask),
						'attributes'	=> $set_attributes_array
						)
					);

					if ($res)
						$this->vfs_versionsystem->commit($record_dest['file_id']);
				}
				else //Create a new file
				{
					$this->touch (array(
							'string'	=> $t->fake_full_path,
							'relatives'	=> array ($t->mask)
						)
					);

					$set_attributes_array = array (
						'createdby_id' => $account_id,
						'created' => $this->now,
						'size' => $size,
						'mime_type' => $record['mime_type'],
						'deleteable' => $record['deleteable'],
						'comment' => $record['comment'],
						'app' => $record['app']
					);

					if (!$this->file_actions)
					{
						$set_attributes_array['content'] = $content;
					}

					$this->set_attributes(array(
							'string'	=> $t->fake_full_path,
							'relatives'	=> array ($t->mask),
							'attributes'	=> $set_attributes_array
						)
					);
				}
				$this->correct_attributes (array(
						'string'	=> $t->fake_full_path,
						'relatives'	=> array ($t->mask)
					)
				);
			}
			else	/* It's a directory */
			{
				/* First, make the initial directory */
				$this->mkdir (array(
						'string'	=> $data['to'],
						'relatives'	=> array ($data['relatives'][1])
					)
				);

				/* Next, we create all the directories below the initial directory */
				foreach($this->ls (array(
						'string'	=> $f->fake_full_path,
						'relatives'	=> array ($f->mask),
						'checksubdirs'	=> True,
						'mime_type'	=> 'Directory'
					)) as $entry)
				{
					$newdir = ereg_replace ("^$f->fake_full_path", "$t->fake_full_path", $entry['directory']);
					$this->mkdir (array(
							'string'	=> $newdir.'/'.$entry['name'],
							'relatives'	=> array ($t->mask)
						)
					);
				}

				/* Lastly, we copy the files over */
				foreach($this->ls (array(
						'string'	=> $f->fake_full_path,
						'relatives'	=> array ($f->mask)
					)) as $entry)
				{
					if ($entry['mime_type'] == 'Directory')
					{
						continue;
					}

					$newdir = ereg_replace ("^$f->fake_full_path", "$t->fake_full_path", $entry['directory']);
					$this->cp (array(
							'from'	=> "$entry[directory]/$entry[name]",
							'to'	=> "$newdir/$entry[name]",
							'relatives'	=> array ($f->mask, $t->mask)
						)
					);
				}
			}

			if (!$f->outside)
			{

				$ls_array = $this->ls(array(
					'string'    => $f->fake_full_path,
					'relatives' => $f->mask
				));

				$file = $ls_array[0];

				$this->vfs_versionsystem->save_snapshot($file['file_id'],VFS_OPERATION_COPIED,'',array('dest' =>$t->fake_full_path));

				$this->vfs_versionsystem->commit();

			}

			return True;
		}


		/*
		 * See vfs_shared
		 */
		function mv ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT, RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$account_id = $GLOBALS['phpgw_info']['user']['account_id'];

			$f = $this->path_parts (array(
					'string'	=> $data['from'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			$t = $this->path_parts (array(
					'string'	=> $data['to'],
					'relatives'	=> array ($data['relatives'][1])
				)
			);

			if ($f->fake_full_path == $t->fake_full_path)
			{
				return true;
			}

			if (!$this->acl_check (array(
					'string'	=> $f->fake_full_path,
					'relatives'	=> array ($f->mask),
					'operation'	=> PHPGW_ACL_READ
				))
				|| !$this->acl_check (array(
					'string'	=> $f->fake_full_path,
					'relatives'	=> array ($f->mask),
					'operation'	=> PHPGW_ACL_DELETE
				))
			)
			{
				trigger_error('vfs->mv: could not move file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Not allowed to delete the file from its source location. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
				return False;
			}

			if (!$this->acl_check (array(
					'string'	=> $t->fake_full_path,
					'relatives'	=> array ($t->mask),
					'operation'	=> PHPGW_ACL_ADD
				))
			)
			{
				trigger_error('vfs->mv: could not move file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Not allowed to add a file in destination location. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
				return False;
			}

			if ($this->file_exists (array(
					'string'	=> $t->fake_full_path,
					'relatives'	=> array ($t->mask)
				))
			)
			{
				if (!$this->acl_check (array(
						'string'	=> $t->fake_full_path,
						'relatives'	=> array ($t->mask),
						'operation'	=> PHPGW_ACL_EDIT
					))
				)
				{
					trigger_error('vfs->mv: could not move file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Not allowed to edit existent file in destination location. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
					return False;
				}
			}

			umask (0177);

			/* We can't move directories into themselves */
			if (($this->file_type (array(
					'string'	=> $f->fake_full_path,
					'relatives'	=> array ($f->mask)
				) == 'Directory'))
				&& ereg ("^$f->fake_full_path", $t->fake_full_path)
			)
			{
				if (($t->fake_full_path == $f->fake_full_path) || substr ($t->fake_full_path, strlen ($f->fake_full_path), 1) == '/')
				{
					trigger_error('vfs->mv: could not move file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Trying to write in invalid location. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
					return False;
				}
			}

			if ($this->file_exists (array(
					'string'	=> $f->fake_full_path,
					'relatives'	=> array ($f->mask)
				))
			)
			{

				/* We get the listing now, because it will change after we update the database */
				$ls = $this->ls (array(
						'string'	=> $f->fake_full_path,
						'relatives'	=> array ($f->mask)
					)
				);

				//to the new version system.
				$ls_fileonly = $this->ls(array(
					'string'      => $f->fake_full_path,
					'relatives'   => array ($f->mask),
					'checksudirs' => false,
					'nofiles'     => true
				));


				$this->vfs_versionsystem->save_snapshot(
					$ls_fileonly[0]['file_id'],VFS_OPERATION_MOVED,
					'Moved from '.$f->fake_full_path.' to '.$t->fake_full_path,
					array('dest' => $t->fake_full_path));

				if ($this->file_exists (array(
						'string'	=> $t->fake_full_path,
						'relatives'	=> array ($t->mask)
					))
				)
				{
					$this->rm (array(
							'string'	=> $t->fake_full_path,
							'relatives'	=> array ($t->mask)
						)
					);
				}

				/*
				 *  We add the journal entry now, before we delete.  This way
				 *  the mime_type field will be updated to 'journal-deleted'
				 *  when the file is actually deleted
				 */
				if (!$f->outside)
				{
					//add_journal was here			
				}

				/*
				   If the from file is outside, it won't have a database entry,
				   so we have to touch it and find the size
				*/
				if ($f->outside)
				{
					$size = filesize ($f->real_full_path);

					$this->touch (array(
							'string'	=> $t->fake_full_path,
							'relatives'	=> array ($t->mask)
						)
					);

					$query = $GLOBALS['phpgw']->db->query ("UPDATE phpgw_vfs2_files SET size=$size WHERE directory='".
						$GLOBALS['phpgw']->db->db_addslashes($t->fake_leading_dirs_clean)."' AND name='".
						$GLOBALS['phpgw']->db->db_addslashes($t->fake_name_clean)."'", __LINE__, __FILE__);
				}
				elseif (!$t->outside)
				{

					$query = $GLOBALS['phpgw']->db->query ("UPDATE phpgw_vfs2_files SET name='".
						$GLOBALS['phpgw']->db->db_addslashes($t->fake_name_clean)."', directory='".
						$GLOBALS['phpgw']->db->db_addslashes($t->fake_leading_dirs_clean)."' WHERE directory='".
						$GLOBALS['phpgw']->db->db_addslashes($f->fake_leading_dirs_clean)."' AND name='".
						$GLOBALS['phpgw']->db->db_addslashes($f->fake_name_clean)."'", __LINE__, __FILE__);
				}


/*				$this->set_attributes(array(
						'string'	=> $t->fake_full_path,
						'relatives'	=> array ($t->mask),
							'attributes'	=> array (
									'modifiedby_id' => $account_id,
									'modified' => $this->now
								)
					)
				);*/

/*				$this->correct_attributes (array(
						'string'	=> $t->fake_full_path,
						'relatives'	=> array ($t->mask)
					)
				);*/

				if ($this->file_actions)
				{
					if(file_exists($t->real_full_path)) 
					{
						unlink($t->real_full_path);
						$ok = rename($f->real_full_path, $t->real_full_path);
					} 
					else 
					{
						$ok = rename($f->real_full_path, $t->real_full_path); 
					}

					if (!$ok)
					{
						return false;
					}

					if (is_dir($t->real_full_path) && $f->outside)
					{
						$this->update_real(array(
							'string' => $t->fake_full_path,
							'relatives' => array($t->mask)
							));

					}
				
					//$rr = rename ($f->real_full_path, $t->real_full_path);
				}

				/*
				   This removes the original entry from the database The actual
				   file is already deleted because of the rename () above
				*/
				if ($t->outside)
				{
					$this->rm (array(
							'string'	=> $f->fake_full_path,
							'relatives'	=> array($f->mask)
						)
					);
				}
				
			}
			else
			{
				trigger_error('vfs->mv: could not move file from '.$f->fake_full_path.' to '.$t->fake_full_path.'. Source file not found. Line '.__LINE__.', File '.__FILE__,E_USER_NOTICE);
				return False;
			}
			
			if ($this->file_type (array(
					'string'	=> $t->fake_full_path,
					'relatives'	=> array ($t->mask)
				)) == 'Directory'
			)
			{
				/* We got $ls from above, before we renamed the directory */
				foreach ($ls as $entry)
				{
					$newdir = ereg_replace ("^$f->fake_full_path", $t->fake_full_path, $entry['directory']);
					$newdir_clean = $this->clean_string (array ('string' => $newdir));

					$query = $GLOBALS['phpgw']->db->query ("UPDATE phpgw_vfs2_files SET directory='".
						$GLOBALS['phpgw']->db->db_addslashes($newdir_clean)."' WHERE file_id='$entry[file_id]'",
						__LINE__, __FILE__);
					$this->correct_attributes (array(
							'string'	=> "$newdir/$entry[name]",
							'relatives'	=> array ($t->mask)
						)
					);
				}

			}


			$this->vfs_versionsystem->commit($ls_fileonly[0]['file_id']);

/*			$this->add_journal (array(
					'string'	=> $t->fake_full_path,
					'relatives'	=> array ($t->mask),
					'operation'	=> VFS_OPERATION_MOVED,
					'state_one'	=> $f->fake_full_path,
					'state_two'	=> $t->fake_full_path
				)
			);*/
			return True;
		}

		/*
		 * See vfs_shared
		 */
		function rm ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (!$this->acl_check (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'operation'	=> PHPGW_ACL_DELETE
				))
			)
			{
				return False;
			}

			if (!$this->file_exists (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				))
			)
			{
				if ($this->file_actions)
				{
					$rr = unlink ($p->real_full_path);
				}
				else
				{
					$rr = True;
				}

				if ($rr)
				{
					return True;
				}
				else
				{
					return False;
				}
			}

			if ($this->file_type (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)) != 'Directory'
			)
			{
				$ls_array = $this->ls(array(
					'string'    => $p->fake_full_path_clean,
					'relatives' => $p->mask
				));

				$file = $ls_array[0];
	
				$this->vfs_versionsystem->save_snapshot($file['file_id'],VFS_OPERATION_DELETED);

				$query = $GLOBALS['phpgw']->db->query ("DELETE FROM phpgw_vfs2_files WHERE directory='".
					$GLOBALS['phpgw']->db->db_addslashes($p->fake_leading_dirs_clean)."' AND name='".
					$GLOBALS['phpgw']->db->db_addslashes($p->fake_name_clean)."'", __LINE__, __FILE__);

				if ($query)
					$this->vfs_versionsystem->commit($file['file_id']);

				if ($this->file_actions)
				{
					$rr = unlink ($p->real_full_path);
				}
				else
				{
					$rr = True;
				}

				if ($query || $rr)
				{
					return True;
				}
				else
				{
					return False;
				}
			}
			else
			{
				$ls = $this->ls (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask)
					)
				);

				/* First, we cycle through the entries and delete the files */
				foreach($ls as $entry)
				{
					if ($entry['mime_type'] == 'Directory')
					{
						continue;
					}

					$this->rm (array(
							'string'	=> "$entry[directory]/$entry[name]",
							'relatives'	=> array ($p->mask)
						)
					);
				}

				/* Now we cycle through again and delete the directories */
				foreach ($ls as $entry)
				{
					if ($entry['mime_type'] != 'Directory')
					{
						continue;
					}

					/* Only the best in confusing recursion */
					$this->rm (array(
							'string'	=> "$entry[directory]/$entry[name]",
							'relatives'	=> array ($p->mask)
						)
					);
				}

				/* If the directory is linked, we delete the placeholder directory */
				$ls_array = $this->ls (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask),
						'checksubdirs'	=> False,
						'mime_type'	=> False,
						'nofiles'	=> True
					)
				);
				$link_info = $ls_array[0];

				if ($link_info['link_directory'] && $link_info['link_name'])
				{
					$path = $this->path_parts (array(
							'string'	=> $link_info['directory'] . '/' . $link_info['name'],
							'relatives'	=> array ($p->mask),
							'nolinks'	=> True
						)
					);

					if ($this->file_actions)
					{
						rmdir ($path->real_full_path);
					}
				}

				$file = $link_info;
	
				$this->vfs_versionsystem->save_snapshot($file['file_id'],VFS_OPERATION_DELETED);

				$query = $GLOBALS['phpgw']->db->query ("DELETE FROM phpgw_vfs2_files WHERE directory='".
					$GLOBALS['phpgw']->db->db_addslashes($p->fake_leading_dirs_clean)."' AND name='".
					$GLOBALS['phpgw']->db->db_addslashes($p->fake_name_clean)."'",
					__LINE__, __FILE__);

				if ($query)
					$this->vfs_versionsystem->commit();

				if ($this->file_actions)
				{
					rmdir ($p->real_full_path);
				}

				return True;
			}
		}

		/*
		 * See vfs_shared
		 */
		function mkdir ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$account_id = $GLOBALS['phpgw_info']['user']['account_id'];
			$currentapp = $GLOBALS['phpgw_info']['flags']['currentapp'];

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (!$this->acl_check (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'operation'	=> PHPGW_ACL_ADD)
				)
			)
			{
				echo "can't create dir ".$p->fake_full_path." due to permissions.";
				return False;
			}

			/* We don't allow /'s in dir names, of course */
			if (ereg ("/", $p->fake_name))
			{
				return False;
			}

			umask (077);

			if ($this->file_actions)
			{
				if (!@is_dir($p->real_leading_dirs_clean))	// eg. /home or /group does not exist
				{
					if (!ereg_replace('^/','',$p->fake_leading_dirs))
					{
						return false;
					}
					if (!@$this->mkdir(array(
						'string' => $p->fake_leading_dirs,
						'relatives' => array(RELATIVE_NONE) )))	// ==> create it
					{
						return False;
					}
				}	

				if (@is_dir($p->real_full_path))	// directory already exists
				{
					//WITH Serious BUG when registrys are in database, but not
					//in filesystem. Correct this ASAP.
					//$this->update_real($data,True);		// update its contents
				}
				elseif (!@mkdir ($p->real_full_path, 0770))
				{
					return False;
				}
			}

			if (!$this->file_exists (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask)
				))
			)
			{
				$query = $GLOBALS['phpgw']->db->query ("INSERT INTO phpgw_vfs2_files (owner_id, name, directory) VALUES ($this->working_id, '".
					$GLOBALS['phpgw']->db->db_addslashes($p->fake_name_clean)."', '".
					$GLOBALS['phpgw']->db->db_addslashes($p->fake_leading_dirs_clean)."')", __LINE__, __FILE__);
	
				$this->set_attributes(array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'attributes'	=> array (
								'createdby_id' => $account_id,
								'size' => 4096,
								'mime_type' => 'Directory',
								'created' => $this->now,
								'deleteable' => 'Y',
								'app' => $currentapp
							)
					)
				);

				$this->correct_attributes (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask)
					)
				);

				//Get info to the versionsystem
				$res = $this->ls(array(
					'string'    => $p->fake_full_path_clean,
					'relatives' => $p->mask
				));

				$file = $res[0];

				$this->vfs_versionsystem->create_initial_version($file['file_id']);


			}
			else
			{
				return False;
			}

			return True;
		}

		/*
		 * See vfs_shared
		 */
		function make_link ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT, RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$account_id = $GLOBALS['phpgw_info']['user']['account_id'];
			$currentapp = $GLOBALS['phpgw_info']['flags']['currentapp'];

			$vp = $this->path_parts (array(
					'string'	=> $data['vdir'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			$rp = $this->path_parts (array(
					'string'	=> $data['rdir'],
					'relatives'	=> array ($data['relatives'][1])
				)
			);

			if (!$this->acl_check (array(
					'string'	=> $vp->fake_full_path,
					'relatives'	=> array ($vp->mask),
					'operation'	=> PHPGW_ACL_ADD
				))
			)
			{
				return False;
			}


			if ((!$this->file_exists (array(
					'string'	=> $rp->real_full_path,
					'relatives'	=> array ($rp->mask)
				)))
				&& !mkdir ($rp->real_full_path, 0770))
			{
				return False;
			}

			if (!$this->mkdir (array(
					'string'	=> $vp->fake_full_path,
					'relatives'	=> array ($vp->mask)
				))
			)
			{
				return False;
			}

			$size = $this->get_size (array(
					'string'	=> $rp->real_full_path,
					'relatives'	=> array ($rp->mask)
				)
			);

			$this->set_attributes(array(
					'string'	=> $vp->fake_full_path,
					'relatives'	=> array ($vp->mask),
					'attributes'	=> array (
								'link_directory' => $rp->real_leading_dirs,
								'link_name' => $rp->real_name,
								'size' => $size
							)
				)
			);

			$this->correct_attributes (array(
					'string'	=> $vp->fake_full_path,
					'relatives'	=> array ($vp->mask)
				)
			);

			return True;
		}

		/*
		 * See vfs_shared
		 */
		//upd 2004-09-16 viniciuscb: custom fields support
		//upd 2004-10-11 viniciuscb: proper_id for file: accepts:
		//               $data['proper_id'], for an all-ready proper id, else
		//               $data['prefix'] for a string with the prefix
		//                  (this can only be used if no proper_id 
		//                   specified now or before)
		//               $data['ptype'] For a specification of file type
		//               $data['prefix_type'] (FUTURE)
		//               if none specified, prefix will be user_lid
		function set_attributes ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT),
					'attributes'	=> array ()
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			/*
			 * This is kind of trivial, given that set_attributes () can change
			 * owner_id, size, etc.
			*/
			if (!$this->acl_check (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'operation'	=> PHPGW_ACL_EDIT
				))
			)
			{
				return False;
			}

			if (!$this->file_exists (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				))
			)
			{
				return False;
			}

			/*
			 * All this voodoo just decides which attributes to update
			 * depending on if the attribute was supplied in the 'attributes'
			 * array
			*/

			$ls_array = $this->ls (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'checksubdirs'	=> False,
					'nofiles'	=> True
				)
			);
			$record = $ls_array[0];

			//handles mime type

			/* FIXME is mime_type is application/octet-stream, don't believe
			 * in passed mime_type and try to find a mime type based in 
			 * extension */
			/* TODO use mime magic capabilities */
			$mime_id = '';
			if ($data['attributes']['mime_type'] && $data['attributes']['mime_type'] != 'application/octet-stream')
			{
				$mime_data = array (
					'mime' => $data['attributes']['mime_type'],
					'extension' => @$this->get_file_extension($data['string'])
				);

				if (!$type = $this->vfs_mimetypes->get_type($mime_data))
				{
					$type = $this->vfs_mimetypes->add_filetype($mime_data);
				}
			}
			//try to find a compatible mime/type based in file extension
			else
			{
				$mime_data = array (
					'extension' => @$this->get_file_extension($data['string'])
				);

				if (!$type = $this->vfs_mimetypes->get_type($mime_data))
				{
					$type = $this->vfs_mimetypes->add_filetype($mime_data);

				}

				/* Finally if the file has no extension and no mime (or mime
				 * defined as application/octet-stream, will consider file type
				 * as application/octet-stream 
				 */
				if (!$type)
				{
					$type = $this->vfs_mimetypes->get_type(array(
						'mime' => 'application/octet-stream'
					));
				}

			}

			//will only change mime_type if mime_type was specified in attribts
			if ($data['attributes']['mime_type'])
			{
				unset($data['attributes']['mime_type']);
				$data['attributes']['mime_id'] = $type['mime_id'];
			}

			/*
			   Indicate that the EDITED_COMMENT operation needs to be journaled,
			   but only if the comment changed
			*/
			if (array_key_exists('comment',$data['attributes']) &&
				 $data['attributes']['comment'] != $record['comment'])
			{
				$edited_comment = 1;
			}

			#** proper id treating **
			
			#1.User does not specified proper_id, and file had not any proper id
			#generates a new proper_id
			if(!$data['attributes']['proper_id'] && $data['attributes']['prefix'])
			{
				$prefix = $data['attributes']['prefix'];

//				$data['attributes']['proper_id'] = $this->generate_proper_id($data['attributes']['prefix'],$type['proper_id']);
				$data['attributes']['proper_id'] = $this->generate_proper_id($data['attributes']['prefix'],$data['attributes']['ptype']);
			}
			#2.User specified proper_id
			#check if this id is not being used. If it is, do not change.
			elseif ($data['attributes']['proper_id'])
			{
				$this->db->select('phpgw_vfs2_files','proper_id',array('proper_id'=>$data['attributes']['proper_id']));

				if ($this->db->next_record())
				{
					unset($data['attributes']['proper_id']);
				}

			}

			//To be sure that olny fields from phpgw_vfs2_files will be inserted
			$update_data = array();
			foreach ($data['attributes'] as $key => $val)
			{
				if ($this->attribute_tables[$key] == 'phpgw_vfs2_files' || $key == 'mime_id')
					$update_data[$key] = $val;
			}

			$where = array(
				'file_id' => $record['file_id']
			);

			if ($edited_comment)
			{
				$this->vfs_versionsystem->save_snapshot($record['file_id'],
					VFS_OPERATION_EDITED_COMMENT);
			}

			if (count($update_data)) //if false, there is nothing to do
			{
				$res = $this->db->update('phpgw_vfs2_files',$update_data,$where,
					__LINE__,__FILE__);

				if ($res) 
				{
					//custom fields storing
					$customfields = $this->vfs_customfields->get_customfields('customfield_name');
					foreach ($customfields as $custom_name => $custom_val)
					{
						if (array_key_exists($custom_name,$data['attributes']))
						{
							$store_array[$record['file_id']][$custom_name] = $data['attributes'][$custom_name];

						}
					}

					if ($store_array)
					{
						$this->vfs_customfields->store_fields($store_array);
					}
				
					if ($edited_comment)
					{
						$this->vfs_versionsystem->commit($record['file_id']);
					}
					return True;
				}
			}
			return false;
		}

		/*!
		@function correct_attributes
		@abstract Set the correct attributes for 'string' (e.g. owner)
		@param string File/directory to correct attributes of
		@param relatives Relativity array
		@result Boolean True/False
		*/
		function correct_attributes ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if ($p->fake_leading_dirs != $this->fakebase && $p->fake_leading_dirs != '/')
			{
				$ls_array = $this->ls (array(
						'string'	=> $p->fake_leading_dirs,
						'relatives'	=> array ($p->mask),
						'checksubdirs'	=> False,
						'nofiles'	=> True
					)
				);
				$set_attributes_array = Array(
					'owner_id' => $ls_array[0]['owner_id']
				);
			}
			elseif (preg_match ("+^$this->fakebase\/(.*)$+U", $p->fake_full_path, $matches))
			{
				$set_attributes_array = Array(
					'owner_id' => $GLOBALS['phpgw']->accounts->name2id ($matches[1])
				);
			}
			else
			{
				$set_attributes_array = Array(
					'owner_id' => 0
				);
			}

			$this->set_attributes (array(
					'string'	=> $p->fake_full_name,
					'relatives'	=> array ($p->mask),
					'attributes'	=> $set_attributes_array
				)
			);

			return True;
		}

		/*
		 * See vfs_shared
		 */
		function file_type ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (!$this->acl_check (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'operation'	=> PHPGW_ACL_READ,
					'must_exist'	=> True
				))
			)
			{
				return False;
			}

			if ($p->outside)
			{
				if (is_dir ($p->real_full_path))
				{
					return ('Directory');
				}

				/*
				   We don't return an empty string here, because it may still
				   match with a database query because of linked directories
				*/
			}

			/*
			   We don't use ls () because it calls file_type () to determine if
			   it has been passed a directory
			*/

			//TODO VCB change this also with filetypes class

			$db2 = $GLOBALS['phpgw']->db;

			$db2->query ("SELECT mime_id 
			              FROM   phpgw_vfs2_files
			              WHERE  directory='".$db2->db_addslashes($p->fake_leading_dirs_clean)."' 
			               AND   name='".$db2->db_addslashes($p->fake_name_clean)."'", __LINE__, __FILE__); 
			$db2->next_record ();

			$file_record = $db2->Record;

			$mime = $this->vfs_mimetypes->get_type(array(
				'mime_id' => $file_record['mime_id']
			));

			$mime_type = $mime['mime'];

			if(!$mime_type)
			{
				$extension = $this->get_file_extension($p->fake_name_clean);
				
				if (!$res = $this->vfs_mimetypes->get_type(array(
						'extension' => $extension)) )
				{
					$res = $this->vfs_mimetypes->add_filetype(array(
						'extension' => $extension)); 

					if ($res)
					{
						$this->db->update('phpgw_vfs2_files',
							array('mime_id'   => $res['mime_id']),
							array('directory' => $p->fake_leading_dirs_clean,
								  'name'      => $p->fake_name_clean
							),__LINE__,__FILE__);
					}
							
				}
				$mime_type = $res['mime'];

			}

			return $mime_type;
		}

		/*
		 * See vfs_shared
		 */
		function file_exists ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT),
					'allow_outside' => true
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if ($p->outside)
			{
				if (!$data['allow_outside'])
				{
					return false;
				}
				$rr = file_exists ($p->real_full_path);
				
				return $rr;
			}

			//TODO id: primary field
			$db2 =& $GLOBALS['phpgw']->db;
			$db2->query ("SELECT name FROM phpgw_vfs2_files WHERE directory='".
				$GLOBALS['phpgw']->db->db_addslashes($p->fake_leading_dirs_clean)."' AND name='".
				$GLOBALS['phpgw']->db->db_addslashes($p->fake_name_clean)."'", __LINE__, __FILE__);
			
			if ($db2->num_rows())
			{
				return True;
			}
			else
			{
				return False;
			}
		}

		/*
		 * See vfs_shared
		 */
		function get_size ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT),
					'checksubdirs'	=> True
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (!$this->acl_check (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'operation'	=> PHPGW_ACL_READ,
					'must_exist'	=> True
				))
			)
			{
				return False;
			}

			/*
			   WIP - this should run through all of the subfiles/directories in the directory and tally up
			   their sizes.  Should modify ls () to be able to return a list for files outside the virtual root
			*/
			if ($p->outside)
			{
				$size = filesize ($p->real_full_path);

				return $size;
			}

			foreach($this->ls (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'checksubdirs'	=> $data['checksubdirs'],
					'nofiles'	=> !$data['checksubdirs']
				)) as $file_array)
			{
				/*
				   Make sure the file is in the directory we want, and not
				   some deeper nested directory with a similar name
				*/
/*
				if (@!ereg ('^' . $file_array['directory'], $p->fake_full_path))
				{
					continue;
				}
*/

				$size += $file_array['size'];
			}

			//TODO VCB update this when id be primary key
			if ($data['checksubdirs'])
			{
				$query = $GLOBALS['phpgw']->db->query ("SELECT size FROM phpgw_vfs2_files WHERE directory='".
					$GLOBALS['phpgw']->db->db_addslashes($p->fake_leading_dirs_clean)."' AND name='".
					$GLOBALS['phpgw']->db->db_addslashes($p->fake_name_clean)."'",__LINE__,__FILE__);
				$GLOBALS['phpgw']->db->next_record ();
				$size += $GLOBALS['phpgw']->db->Record[0];
			}

			return $size;
		}

		/*!
		@function checkperms
		@abstract Check if $this->working_id has write access to create files in $dir
		@discussion Simple call to acl_check
		@param string Directory to check access of
		@param relatives Relativity array
		@result Boolean True/False
		*/
		function checkperms ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (!$this->acl_check (array(
					'string'	=> $p->fake_full_path,
					'relatives'	=> array ($p->mask),
					'operation'	=> PHPGW_ACL_ADD
				))
			)
			{
				return False;
			}
			else
			{
				return True;
			}
		}

		/*
		 * See vfs_shared
		 * If $data['readlink'] then a readlink is tryed on the real file
		 * If $data['file_id'] then the file_id is used instead of a path
		 */
		//upd 2004-09-16 viniciuscb: Support for custom fields
		function ls ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT),
					'checksubdirs'	=> True,
					'mime_type'	=> False,
					'nofiles'	=> False,
					'orderby'	=> 'directory,name',
					'backups'   => false, /* show or hide backups */
					'files_specified' => array(),
					'allow_outside' => true,
					'modifiedby_information' => false
				);

			//check if orderby is a valid field (or is composed by valid fields)
			//this prevents a sql error that happens when sort field is invalid.
			if ($data['orderby'])
			{
				$fields = explode(',',$data['orderby']);
				foreach ($fields as $field_name)
				{
					if ($this->attribute_tables[trim($field_name)] != 'phpgw_vfs2_files')
					{
						unset($data['orderby']);
						break;
					}
				}
			}

			$data = array_merge ($this->default_values ($data, $default_values), $data);


			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);
			$dir = $p->fake_full_path;

			//Abiliy to show or not backup files -> put it in sql queries
			$sql_backups = ($data['backups']) ? '' : ' AND is_backup=\'N\' ';


			/* If they pass us a file or 'nofiles' is set, return the info for $dir only */
			if (@$data['file_id']
				|| ((($type = $this->file_type (array(
					'string'	=> $dir,
					'relatives'	=> array ($p->mask)
				)) != 'Directory'))
				|| ($data['nofiles'])) && !$p->outside
			)
			{

				/* SELECT all, the, attributes */
				$sql = 'SELECT ';

				foreach ($this->attributes as $num => $attribute)
				{
					if ($this->attribute_tables[$attribute] == 'phpgw_vfs2_files')
					{
						if ($num)
						{
							$sql .= ', ';
						}

						$sql .= $attribute;
					}
				}

				$sql .= ",mime_id,is_backup FROM phpgw_vfs2_files WHERE ";


				if (@$data['file_id'])
				{
					$sql .= 'file_id='.(int)$data['file_id'].$sql_backups;
				}
				else
				{
					$sql .= "directory='".$GLOBALS['phpgw']->db->db_addslashes($p->fake_leading_dirs_clean).
						"' AND name='".$GLOBALS['phpgw']->db->db_addslashes($p->fake_name_clean)."'".$sql_backups;
				}

//				echo " select1: dir=".$p->fake_leading_dirs_clean." name=".$p->fake_name_clean." <br>\n";
				$query = $GLOBALS['phpgw']->db->query ($sql, __LINE__, __FILE__);

				$GLOBALS['phpgw']->db->next_record ();
				$record = $GLOBALS['phpgw']->db->Record;

				if (!$record)
				{
					return array();
				}

				/* We return an array of one array to maintain the standard */
				$rarray = array ();
				foreach($this->attributes as $attribute)
				{
					switch ($attribute)
					{

						case 'mime_type':
							if (!is_numeric($record['mime_id'])) 
							{
								//no mime type registered for file, must find one and if not exist add one.
								$extension = $this->get_file_extension($record['name']);

								if (!$res = $this->vfs_mimetypes->get_type(array('extension' => $extension)))
								{
									$res = $this->vfs_mimetypes->add_filetype(array('extension' => $extension));
								}

								if ($res)
								{
									$this->db->update('phpgw_vfs2_files',
										array('mime_id'   => $res['mime_id']),
										array('directory' => $p->fake_leading_dirs_clean,
											  'name'      => $p->fake_name_clean
										),__LINE__,__FILE__);

								}
							}
							else
							{
								$res = $this->vfs_mimetypes->get_type(array(
									'mime_id' => $record['mime_id']
								));
							}

							$record['mime_type'] = $res['mime'];
							$record['mime_friendly'] = $res['friendly'];
							break;
						case 'created':
						case 'modified':
							$record[$attribute] = $this->db->from_timestamp($record[$attribute]);
							break;
					}


					$rarray[0][$attribute] = $record[$attribute];
				}
				if ($this->file_actions && @$data['readlink'])	// test if file is a symlink and get it's target
				{
					$rarray[0]['symlink'] = @readlink($p->real_full_path);
				}

				//handle custom fields
				reset($rarray);
				while(list($key,$val) = each($rarray))
				{
					$custom =& $this->vfs_customfields->get_fields_by_fileid($val['file_id']);

					if ($custom)
					{
						$rarray[$key] = array_merge($val,$custom);
					}
				}

				return $rarray;
			}

			//WIP - this should recurse using the same options the virtual part of ls () does
			/* If $dir is outside the virutal root, we have to check the file system manually */
			if ($p->outside)
			{
				if (!$data['allow_outside'])
				{
					return false;
				}

				if ($this->file_type (array(
						'string'	=> $p->fake_full_path,
						'relatives'	=> array ($p->mask)
					)) == 'Directory'
					&& !$data['nofiles']
				)
				{
					$dir_handle = opendir ($p->real_full_path);
					while ($filename = readdir ($dir_handle))
					{
						if ($filename == '.' || $filename == '..')
						{
							continue;
						}

						$rarray[] = $this->get_real_info (array(
								'string'	=> $p->real_full_path . SEP . $filename,
								'relatives'	=> array ($p->mask)
							)
						);
					}
				}
				else
				{
					$rarray[] = $this->get_real_info (array(
							'string'	=> $p->real_full_path,
							'relatives'	=> array ($p->mask)
						)
					);
				}

				//handle custom fields
				reset($rarray);
				while(list($key,$val) = each($rarray))
				{
					$custom =& $this->vfs_customfields->get_fields_by_fileid($val['file_id']);

					if ($custom)
					{
						$rarray[$key] = array_merge($val,$custom);
					}
				}

				return $rarray;
			}

			/* $dir's not a file, is inside the virtual root, and they want to check subdirs */
			/* SELECT all, the, attributes FROM phpgw_vfs WHERE file=$dir */

//			echo "DIR_CLEAN: ".$dir_clean."<BR>";

			$dir_clean = $this->clean_string (array ('string' => $dir));


			//This query must be fast and only bring things with the correct
			//permissions. So, I'll get the list of all groups the current user
			//belongs, get the list of the current dir and all parents over it
			//and pass all these things in one query.

			//gets the id of all groups $account_id belongs to
			$groups = $GLOBALS['phpgw']->accounts->membership($GLOBALS['phpgw_info']['user']['account_id']);

			foreach($groups as $group)
			{
				$accounts[] = $group['account_id'];
			}

			$accounts[] = $GLOBALS['phpgw_info']['user']['account_id'];
			$accounts[] = 0; //default permission for all users

			$paths = array();
			
			$dir_exploded = explode('/',$dir_clean);
			$dir_parts = count($dir_parts);

			for ($i=$dir_parts-1;$i>=0;$i--)
			{	
				$res = implode('/',array_slice($dir_explode,0,$i));
				$paths[] = "'".($res) ? $res : '/'."'";
			}

			if (!$data['backups'])
			{
				$append .= " AND fls.is_backup = 'N'";
			}


			if ($data['mime_type'])
			{
				$other_where .= " AND mime.mime = '".$data['mime_type']."'";
				$mime_join = ' INNER JOIN phpgw_vfs2_mimetypes mime ON fls.mime_id = mime.mime_id';
			}

			//orderby
			$orderby = explode(',',$data['orderby']);

			foreach($orderby as $key =>$orderby_instance)
			{
				$orderby[$key] = 'fls.'.$orderby_instance;
			}


			$append .= ' ORDER BY '.implode(',',$orderby);

			$dir_rule = $data['checksubdirs'] ? "fls.directory LIKE '".$dir_clean."%'" : "fls.directory = '".$dir_clean."'";
			//SQL to get all files in current dir, except those ones that user
			//does not have permission to see
			$sql = "SELECT	DISTINCT fls.*
					FROM	phpgw_vfs2_files fls
					$mime_join
					LEFT JOIN phpgw_vfs2_shares sh ON fls.file_id = sh.file_id
					WHERE	".$dir_rule." AND
							(   (sh.account_id in (".implode(',',$accounts).") AND fls.shared = 'Y') OR 
								(sh.account_id is NULL AND fls.shared = 'N') OR
								fls.owner_id = ".$GLOBALS['phpgw_info']['user']['account_id']." )
							$other_where $append";

			
//			echo "<xmp>\n$sql\n</xmp>";

			$query = $GLOBALS['phpgw']->db->query ($sql, __LINE__, __FILE__);

			$rarray = array ();
			for ($i = 0; $GLOBALS['phpgw']->db->next_record (); $i++)
			{
				$record = $GLOBALS['phpgw']->db->Record;


				/* Further checking on the directory.  This makes sure /home/user/test won't match /home/user/test22 */
				if (@!ereg ("^$dir(/|$)", $record['directory']))
				{
					continue;
				}

				/* If they want only this directory, then $dir should end without a trailing / */
				if (!$data['checksubdirs'] && ereg ("^$dir/", $record['directory']))
				{
					continue;
				}

				foreach($this->attributes as $attribute)
				{
					switch($attribute)
					{
						case 'mime_type':
							if (!is_numeric($record['mime_id']))
							{
								$extension = $this->get_file_extension($record['name']);
								if(!$res = $this->vfs_mimetypes->get_type(array(
										'extension' => $extension)) )
								{
									$res = $this->vfs_mimetypes->add_filetype(array(
										'extension'
									));

									if ($res)
									{
										$this->db->update('phpgw_vfs2_files',
											array('mime_id'   => $res['mime_id']),
											array('directory' => $p->fake_leading_dirs_clean,
												  'name'      => $p->fake_name_clean
											),__LINE__,__FILE__);
									}

								}
							}
							else
							{
								$res = $this->vfs_mimetypes->get_type(array(
									'mime_id' => $record['mime_id']
								));
							}

							$record['mime_type'] = $res['mime'];
							$rarray[$i]['mime_friendly'] = $res['friendly'];
							break;
						case 'created':
						case 'modified':
							$record[$attribute] = $this->db->from_timestamp($record[$attribute]);
							break;
					}

					$rarray[$i][$attribute] = $record[$attribute];
				}
			}

			//handle custom fields
			reset($rarray);
			while(list($key,$val) = each($rarray))
			{
				$custom =& $this->vfs_customfields->get_fields_by_fileid($val['file_id']);

				if ($custom)
				{
					$rarray[$key] = array_merge($val,$custom);
				}
			}

			return $rarray;
		}

		/*
		 * See vfs_shared
		 */
		function update_real ($data,$recursive = False)
		{
			//FIXME this method does not work when there are registrys in
			//database, but not in filesystem. It starts corromping the
			//database by putting wrong things that are in the partition root.
			//return false;
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (file_exists ($p->real_full_path))
			{
				if (is_dir ($p->real_full_path))
				{
					$dir_handle = opendir ($p->real_full_path);
					while ($filename = readdir ($dir_handle))
					{
						if ($filename == '.' || $filename == '..')
						{
							continue;
						}

						$rarray[] = $this->get_real_info (array(
								'string'	=> $p->fake_full_path . '/' . $filename,
								'relatives'	=> array (RELATIVE_NONE)
							)
						);
					}
				}
				else
				{
					$rarray[] = $this->get_real_info (array(
							'string'	=> $p->fake_full_path,
							'relatives'	=> array (RELATIVE_NONE)
						)
					);
				}

				if (!is_array ($rarray))
				{
					$rarray = array ();
				}

				foreach($rarray as $num => $file_array)
				{
					$p2 = $this->path_parts (array(
							'string'	=> $file_array['directory'] . '/' . $file_array['name'],
							'relatives'	=> array (RELATIVE_NONE)
						)
					);

					/* Note the mime_type.  This can be "Directory", which is
					 * how we create directories */
					$set_attributes_array = Array(
						'size' => $file_array['size'],
						'mime_type' => $file_array['mime_type']
					);

					if (!$this->file_exists (array(
							'string'	=> $p2->fake_full_path,
							'relatives'	=> array (RELATIVE_NONE)
						))
					)
					{
						$this->touch (array(
								'string'	=> $p2->fake_full_path,
								'relatives'	=> array (RELATIVE_NONE)
							)
						);
					}
					$this->set_attributes (array(
							'string'	=> $p2->fake_full_path,
							'relatives'	=> array (RELATIVE_NONE),
							'attributes'	=> $set_attributes_array
						)
					);
					if ($recursive && $file_array['mime_type'] == 'Directory')
					{
						$dir_data = $data;
						$dir_data['string'] = $file_array['directory'] . '/' . $file_array['name'];
						$this->update_real($dir_data,$recursive);
					}
				}
			}
		}

		/*!
		 * @function regenerate_database 
		 * @abstract This function regenerates the full database. It is like
		 *  the function update_real, but instead, it works for all files in a
		 *  folder, fixing all broken regs in a database (if it exists), or
		 *  recreating it.  
		 * @author Vinicius Cubas Brand
		 */
		function regenerate_database()
		{

		}

		/* Helper functions */

		/* This fetchs all available file system information for string (not using the database) */
		function get_real_info ($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT)
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
					'string'	=> $data['string'],
					'relatives'	=> array ($data['relatives'][0])
				)
			);

			if (is_dir ($p->real_full_path))
			{
				$mime_type = 'Directory';
			}
			else
			{
				$mime_type = $this->get_ext_mime_type (array(
						'string'	=> $p->fake_name
					)
				);

/*				if($mime_type)
				{
					$GLOBALS['phpgw']->db->query ("UPDATE phpgw_vfs2_files as file, phpgw_vfs2_mimetypes as mime SET mime.mime_id=file.mime_id WHERE mime.mime='".$mime_type."' AND file.directory='".
						$GLOBALS['phpgw']->db->db_addslashes($p->fake_leading_dirs_clean)."' AND file.name='".
						$GLOBALS['phpgw']->db->db_addslashes($p->fake_name_clean)."'", 
						__LINE__, __FILE__);
				}*/
			}

			$size = filesize ($p->real_full_path);
			$rarray = array(
				'directory' => $p->fake_leading_dirs,
				'name' => $p->fake_name,
				'size' => $size,
				'mime_type' => $mime_type
			);

			return ($rarray);
		}


		/*!
		 * @function search
		 * @abstract This function returns the files that have a field with
		 *           keyword
		 *
		 * @author Vinicius Cubas Brand
		 */
		function search($keyword,$fields=null,$is_backup='N')
		{
			//the fields in which the keyword will be searched
			$searchable_fields = array(
				'created',
				'comment',
				'app',
				'directory',
				'name',
				'link_directory',
				'link_name',
				'version',
				'proper_id'
			);

			if (is_array($fields))
			{
				$tmp = array_diff($searchable_fields,$fields);
				$searchable_fields = array_diff($searchable_fields,$tmp);
			}

			foreach ($searchable_fields as $val)
			{
				$sf[] = $val." LIKE '%$keyword%'";
			}
			
			$where = implode(' OR ',$sf);
			
			$sql = "SELECT file_id
			        FROM   phpgw_vfs2_files
					WHERE  $where
					  AND  is_backup = '$is_backup'";
			
			$this->db->query($sql,__LINE__,__FILE__);

			$res = array();
			while ($this->db->next_record())
			{
				$res[] = $this->db->Record['file_id'];
			}

			//search in the custom fields
			$res = array_unique(array_merge($res,$this->vfs_customfields->search_files($keyword,$fields)));

			sort($res);

			return $res;
		}

		/*!
		 * @function get_file_extension
		 * @abstract This function returns the file extension for a file.
		 *
		 * @author Vinicius Cubas Brand
		 */
		function get_file_extension($filename)
		{
			$ext = explode('.',$filename);
			
			$bla = array_pop($ext);

			if (count($ext))
			{
				return $bla;
			}

			return '';
		}

		/*!
		 * @function get_file_id
		 * @abstract This function returns the file id for a file, false on eror
		 * @param string, relatives
		 *
		 * @author Vinicius Cubas Brand
		 */
		function get_file_id($data)
		{
			if (!is_array ($data))
			{
				$data = array ();
			}

			$default_values = array
				(
					'relatives'	=> array (RELATIVE_CURRENT),
				);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			$p = $this->path_parts (array(
						'string'	=> $data['string'],
						'relatives'	=> array ($data['relatives'][0])
					)
			);

			$sql = "SELECT file_id
			        FROM phpgw_vfs2_files
					WHERE directory='".$this->db->db_addslashes($p->fake_leading_dirs_clean)."' 
				      AND name='".$this->db->db_addslashes($p->fake_name_clean)."'";
				
			$query = $this->db->query ($sql , __LINE__, __FILE__);
			$this->db->next_record ();

			return $this->db->Record['file_id'];
		}

		/*!
		 * @function id2name
		 * @abstract This function returns an array with all information about
		 * a file
		 * @param string, relatives
		 *
		 * @author Vinicius Cubas Brand
		 */
		function id2name($id)
		{
			$res = $this->db->select('phpgw_vfs2_files','*',
				array('file_id'=>$id),__LINE__,__FILE__);

			if($this->db->next_record())
			{
				$result = $this->db->Record;

				$res = $this->vfs_mimetypes->get_type(array(
									'mime_id' => $result['mime_id']
								));

					$result['mime_type'] = $res['mime'];
					$result['mime_friendly'] = $res['friendly'];

					return $result;
			}

			return array();
		}

		//TODO should generate a new name if archive exists, or have some other
		//sort of smart error handling.
		function compress($data)
		{
			$compression_handlers = array(
//				'gz'  => 'gzip',
//				'bz2' => 'bzip',
//				'tar' => 'tar',
				'zip' => 'zip'
				);

			if (!is_array($data['files']) || !$data['name'])
			{
				return false;
			}

			$default_values = array
			(
				'type'          => 'zip',
				'relatives'     => array(RELATIVE_CURRENT),
				'prefix'        => $GLOBALS['phpgw_info']['user']['account_lid'],
				'ptype'         => ''
			);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			if (!$data['type'] || !array_key_exists($data['type'],$compression_handlers))
			{
				//error: inexistent type given
				return false;
			}


			//put extension in archive name, if not exists
			if ($this->get_file_extension($data['name']) != $data['type'])
			{
				if ($data['type'] == 'zip' || $data['type'] == 'tar')
				{
					$data['name'] .= '.' . $data['type'];
				}
				else
				{
					$data['name'] .= '.tar.' . $data['type'];
				}
			}

			//last element in array $data['relatives'] is the id of destiny
			$dest_relativity = (count($data['relatives']))?array_pop($data['relatives']):RELATIVE_CURRENT;

			$dest = $this->path_parts(array(
				'string' => $data['name'],
				'relatives' => $dest_relativity
			));

			if ($this->file_exists(array(
				'string' => $dest->fake_full_path,
				'relatives' => $dest->mask	)))
			{
				//overwrite handling

				//check if acl allow overwriting
				if (!$this->acl_check(array(
					'string' => $dest->fake_full_path,
					'relatives' => $dest->mask,
					'operation' => PHPGW_ACL_EDIT))
					)
				{
					//error: cannot overwrite (HANDLE THIS)
					return false;
				}
			}
			else
			{	
				//Add handling

				//check acl
				if (!$this->acl_check(array(
					'string' => $dest->fake_leading_dirs,
					'relatives' => $dest->mask,
					'operation' => PHPGW_ACL_ADD))
					)
				{
					//error: cannot add in dir
					return false;
				}
			}

			$count_rels = count($data['relatives']);

			reset($data['relatives']);
			foreach($data['files'] as $filename)
			{
				if (!($relative = current($data['relatives'])))
				{
					$relative = RELATIVE_CURRENT;
				}
			
				$p = $this->path_parts(array(
					'string' => $filename,
					'relatives' => array($relative)
				));

				if ($this->acl_check(array(
					'string' => $p->fake_full_path,
					'relatives' => $p->mask,
					'operation' => PHPGW_ACL_READ,
					'must_exist' => true )) )
				{
					$filenames[] = $p->real_full_path;
				}
				else
				{
					//catch error: file not exists or no permission
				}

				next($data['relatives']);
			}

			//Save file in tmp folder, then move it to its definitive place.
			//This will handle file overwrites and journalling entries.

			$tmp_dir =  $GLOBALS['phpgw_info']['server']['temp_dir'];
			$tmp_filename = $tmp_dir.'/'.$this->random_filename();

			$archive_opts = array(
				'basedir' =>  $dest->real_leading_dirs,
				'inmemory' => 0,
				'overwrite' => 1, //provisory, todo error handling
				'recurse' => 1,
				'storepaths' => 1,
				'level' => 9//compression level, 9 is max, 0 is none, 3 ok
			);

/*			#Didn't work - class.archive.inc.php
			$compression_class = $compression_handlers[$data['type']].'_file';

			require_once(PHPGW_API_INC.'/class.archive.inc.php');	

			$arch = new $compression_class($tmp_filename);
			$arch->set_options($archive_opts);
			$arch->add_files($filenames);
			$arch->create_archive();

			$type_array = $this->vfs_mimetypes->get_type(array('extension'=>$data['type']));
			$arch_mime = $type_array['mime'];*/


			switch($data['type'])
			{
				case 'zip':
					$zip =& CreateObject('phpgwapi.PclZip',$tmp_filename);

					//FIXME not $dest->real_leading_dirs, but the path to be
					//removed from files
					$zip->create($filenames,PCLZIP_OPT_REMOVE_PATH,$dest->real_leading_dirs);
					$arch_mime = 'application/x-zip-compressed';
					break;

				default:
				/*
					$tar =& CreateObject('phpgwapi.Archive_Tar',$tmp_filename,$data['type']);
					//FIXME not $dest->real_leading_dirs, but the path to be
					//removed from files
					if (!$tar->createModify($filenames,'',$dest->real_leading_dirs.'/'))
					{
						//TODO throw error
					}
					$arch_mime = 'application/x-gzip';
					break;
				*/
			}

			/* VOILA! now the archive is created!!! but it is yet in /tmp and
			 * have no entry in the database and is not in its correct dir.
			 * The next and final step is then to make these tasks. */

			$dest_relativity = $data['relatives'][count($data['relatives'])-1];

			# ----------------------------------------------------------- #
			# All code under here copied from filescenter->bo->fileUpload #
			# ----------------------------------------------------------- #

			$file_comment = "Archive contents:\n\n".implode("\n",$data['files']);
			$file_comment = substr(0,255,$file_comment);

			# Check to see if the file exists in the database, and get
			# its info at the same time
			$ls_array = $this->ls(array(
				'string'=> $data['name'],
				'relatives'	=> array($dest_relativity),
				'checksubdirs'	=> False,
				'nofiles'	=> True
			));

			$fileinfo = $ls_array[0];

			if($fileinfo['name'])
			{
				if($fileinfo['mime_type'] == 'Directory')
				{
					$messages[]= $GLOBALS['phpgw']->common->error_list(array(lang('Cannot replace %1 because it is a directory', $fileinfo['name'])));
					return false;
				}
			}

			#overwriting
			if($fileinfo['name'] && $fileinfo['deleteable'] != 'N')
			{
				$tmp_arr=array(
					'string'=> $data['name'],
					'relatives'	=> array($dest_relativity),
					'attributes'	=> array(
						'owner_id' => $GLOBALS['phpgw_info']['user']['account_id'],
						'modifiedby_id' => $GLOBALS['phpgw_info']['user']['account_id'], 
						'modified' => date('Y-m-d'),
						'mime_type' => $arch_mime,
						'size' => filesize($tmp_filename),
						'deleteable' => 'Y',
						'comment' => $file_comment,
						'prefix' => $data['prefix'],
						'ptype' => $data['ptype']
						#if overwriting, do not change.
						#TODO rethink/decide policy for that
						#'prefix' => $otherelms['prefix'.$file_number])
						
					)
				);
				$this->set_attributes($tmp_arr);

				$tmp_arr=array(
					'from'	=> $tmp_filename,
					'to'	=> $data['name'],
					'relatives'	=> array(RELATIVE_NONE|VFS_REAL, $dest_relativity)
				);
				$this->cp($tmp_arr);

			}
			else #creating a new file
			{
				$this->cp(array(
					'from'=> $tmp_filename,
					'to'=> $data['name'],
					'relatives'	=> array(RELATIVE_NONE|VFS_REAL, $dest_relativity)
				));

				$this->set_attributes(array(
					'string'=> $data['name'],
					'relatives'	=> array($dest_relativity),
					'attributes'=> array(
						'comment' => $file_comment,
						'mime_type' => $arch_mime,
						'prefix' => $data['prefix'],
						'ptype' => $data['ptype']
						
					)
				));

			}
		}


		#two parts: 1: extract files in tmp_dir.  2: move files to dir, adding
		# them in db
		function extract($data)
		{
			$compression_handlers = array(
				'gz'  => 'gzip',
				'bz2' => 'bzip',
				'tar' => 'tar',
				'zip' => 'zip'
				);


			if (!$data['name'] || !$data['dest'])
			{
				return false;
			}

			$default_values = array
			(
				'relatives'	=> array(RELATIVE_CURRENT,RELATIVE_CURRENT),
				'prefix'    => $GLOBALS['phpgw_info']['user']['account_lid'],
				'ptype'     => ''
				
			);

			$data = array_merge ($this->default_values ($data, $default_values), $data);

			if (!$data['type'])
			{
				$data['type'] = strtolower($this->get_file_extension($data['name']));
			}

			if (!$data['type'] || !array_key_exists($data['type'],$compression_handlers))
			{
				//error: inexistent type given
				return false;
			}


			$arch = $this->path_parts (array(
				'string' => $data['name'],
				'relatives' => array ($data['relatives'][0])
			));

			$dest = $this->path_parts (array(
				'string' => $data['dest'],
				'relatives' => array_pop($data['relatives'])
			));

			//Extract files in tmp folder, then move it to its definitive place.
			//This will handle file overwrites and journalling entries.

			$tmp_dir =  $GLOBALS['phpgw_info']['server']['temp_dir'];
			$tmp_dirname = $tmp_dir.'/'.$this->random_filename();


			$tmp_dest = $this->path_parts(array(
				'string' => $tmp_dirname,
				'relatives' => array(RELATIVE_NONE|VFS_REAL)
				));

			if ($this->file_exists(array(
				'string'=> $tmp_dest->fake_full_path,
				'relatives' => $tmp_dest->mask )))
			{
				$this->rm(array(
					'string' => $tmp_dest->fake_full_path,
					'relatives' => $tmp_dest->mask 
				));
			}

			$this->mkdir(array(
				'string' => $tmp_dest->fake_full_path,
				'relatives' => array($tmp_dest->mask)
			));


			# see if user has add permission in destination folder
			if (!$this->acl_check(array(
				'string'     => $dest->fake_full_path,
				'relatives'  => $dest->mask,
				'operation'  => PHPGW_ACL_ADD,
				'must_exist' => true ))
				)
			{
				return false; //TODO error handling
			}

			#extract files
			switch ($data['type'])
			{
				case 'zip':
					$zip =& CreateObject('phpgwapi.PclZip',$arch->real_full_path);
					if (!$zip->extract(PCLZIP_OPT_PATH,$tmp_dest->real_full_path,PCLZIP_OPT_SET_CHMOD,DEFAULT_LOW_PERMS))
					{
						return false; //TODO handle error
					}
					break;
				default:
					return false;
				/*

					$archive_opts = array(
						'basedir' =>  $tmp_dest->real_full_path,
						'overwrite' => 1 //provisory, todo error handling
					);

					$compression_class = $compression_handlers[$data['type']].'_file';
					$archive_obj = new $compression_class($arch->real_full_path);
					$archive_obj->set_options($archive_opts);
					$archive_obj->extract_files();
					*/

	/*				$tar =& CreateObject('phpgwapi.Archive_Tar',$arch->real_full_path);
					if (!$tar->extract($tmp_dest->real_full_path))
					{
						return false; //TODO handle error
					}
					break;*/
				default:
					return false; //TODO handle error
			}

			#refresh db
			$filelist = $this->ls(array(
				'string'    => $tmp_dest->fake_full_path,
				'relatives' => array($tmp_dest->mask),
				'checksubdirs' => true,
				'nofiles' => false
			));

			foreach ($filelist as $file)
			{
				$res = $this->mv(array(
					'from' => $file['directory'].'/'.$file['name'],
					'to'   => $dest->fake_full_path.'/'.$file['name'],
					'relatives' => array(RELATIVE_NONE|VFS_REAL,$dest->mask)
				));

				$this->set_attributes(array(
					'string' => $dest->fake_full_path.'/'.$file['name'],
					'relatives' => array($dest->mask),
					'attributes' => array(
						'prefix' => $data['prefix'],
						'ptype'  => $data['ptype'],
						'mime_type' => $file['mime_type'],
						'size' => $file['size']
					)
				));
			}

			return true;
		}

		/*!
		 * @function random_filename()
		 * @abstract Generates a Random Filename
		 *
		 * @result string
		 */
		function random_filename()
		{
			$filename = '';
			$filename_length = 8;
			while (strlen($filename) < $filename_length) {
				$filename .= chr(rand (97,122));
			}

			return $filename.'.tmp';
		}

		function generate_proper_id($owner_name,$filetype_identifier)
		{
			$prefix = $owner_name.'-'.$filetype_identifier.'-';
			#$prefix = 'P-' . $this->year . '-';

			$prefix = str_replace('--','-',$prefix);

			$qry = "select max(proper_id) from phpgw_vfs2_files where proper_id like ('$prefix%') AND LENGTH(proper_id) <= ".(strlen($prefix)+4);

//			echo $qry;
//			exit();

			$this->db->query("select max(proper_id) from phpgw_vfs2_files where proper_id like ('$prefix%') AND LENGTH(proper_id) <= ".(strlen($prefix)+4));
			$this->db->next_record();
			$max = $this->add_leading_zero(array_pop(explode('-',$this->db->f(0))));
			return $prefix . $max;
		}

		function add_leading_zero($num)  
		{
/*			if ($id_type == "hex")
			{
				$num = hexdec($num);
				$num++;
				$num = dechex($num);
			}
			else
			{
				$num++;
			} */

			$num++;

			if (is_numeric($num))
			{
				if (strlen($num) == 4)
					$return = $num;
				if (strlen($num) == 3)
					$return = "0$num";
				if (strlen($num) == 2)
					$return = "00$num";
				if (strlen($num) == 1)
					$return = "000$num";
				if (strlen($num) == 0)
					$return = "0001";
			}
			else
			{
				$return = $num;
			}

			return strtoupper($return);
		}

			
		//import function from old vfs
		function import_vfs()
		{
			$filename = PHPGW_API_INC.'/class.vfs_sql.inc.php';
			
			if (function_exists('file_get_contents'))
			{
				$file_vfs_sql = file_get_contents($filename);
			}
			else
			{
				$fp = fopen($filename,'r');
				$file_vfs_sql = fread($fp,filesize($filename));
				fclose($fp);
			}
			
			$file_vfs_sql = str_replace(
				array('class vfs','function vfs','<?php','<?','?>'),
				array('class vfs_sql_old','function vfs_sql_old'),$file_vfs_sql
			);

			eval($file_vfs_sql);

			$obj_vfs_sql = new vfs_sql_old();

			$obj_vfs_sql->override_acl = true;

			$directories = $obj_vfs_sql->ls(array(
				'string' => '/',
				'relatives' => array(RELATIVE_ROOT),
				'checksubdirs' => true,
				'nofiles' => false,
				'mime_type' => 'Directory'
				));

			$global_content = $obj_vfs_sql->ls(array(
				'string' => '/',
				'relatives' => array(RELATIVE_ROOT),
				'checksubdirs' => true,
				'nofiles' => false
				));

			$obj_vfs_sql->override_acl = false;

			$this->override_acl = true;

			foreach($directories as $key => $dir)
			{
				$dirname = str_replace('//','/',$dir['directory'].'/'.$dir['name']);
				$dir_exists_in_vfs2 = $this->file_exists(array(
					'string' => $dirname,
					'relatives' => array(RELATIVE_ROOT),
					'allow_outside' => false
				));


				if ($dir_exists_in_vfs2)
				{
					$dir_is_dir = $this->file_type(array(
						'string' => $dirname,
						'relatives' => array(RELATIVE_ROOT)
					)) == 'Directory';

					if ($dir_is_dir)
					{
						//good - just add permissions to the old owner

						//1. get information about file

						$file_info = $this->ls(array(
							'string' => $dirname,
							'relatives' => array(RELATIVE_ROOT),
							'nofiles' => true,
							'checksubdirs' => false,
							'allow_outside' => false
						));

						if (!$file_info['proper_id'])
						{
							$this->set_attributes(array(
								'string' => $dirname,
								'relatives' => array(RELATIVE_ROOT),
								'attributes' => array(
									'prefix' => $GLOBALS['phpgw']->accounts->id2name($dir['owner_id']),
									'comment' => $dir['comment']
									)
							));
						}

						//if user is not the owner of the file, will
						//add him in the list of authorized personnel
						if (array_key_exists(0,$file_info) && $file_info[0]['owner_id'] != $dir['owner_id'])
						{
							$file_id = $this->get_file_id(array(
								'string' => $dirname,
								'relatives' => array(RELATIVE_ROOT)
							));

							if ($file_id != 0)
							{
								if ($dirname != "/" && $dirname != "/home")
								{
									$perms = $this->vfs_sharing->get_permissions($file_id);
									$perms[$dir['owner_id']] = PHPGW_ACL_READ | PHPGW_ACL_ADD | PHPGW_ACL_EDIT | PHPGW_ACL_DELETE;

									$this->vfs_sharing->set_permissions(array($file_id => $perms));
								}

							}
							else
							{
								//something gone wrong... This should not have
								//happened
								trigger_error('Failed in permission setting of file <b>'.$dirname.'</b> at importing procedure.');
							}
						}
						continue;
					}
					else
					{
						$this->mv(array(
							'from' => $dirname,
							'to'   => $dirname.'_renamed',
							'relatives' => array(RELATIVE_ROOT,RELATIVE_ROOT)
						));
						//error, bacuse wanting to touch a dir that is a
						//file in new vfs
					}
				}
				
				$this->mkdir(array(
					'string' => $dirname,
					'relatives' => array(RELATIVE_ROOT)
				));

				unset($dir['file_id']);

				$dir['prefix'] = $GLOBALS['phpgw']->accounts->id2name($dir['owner_id']);

				$this->set_attributes(array(
					'string' => $dirname,
					'relatives' => array(RELATIVE_ROOT),
					'attributes' => $dir
				));
			}

			foreach($global_content as $file)
			{
				$filename = str_replace('//','/',$file['directory'].'/'.$file['name']);
				$file_exists_in_vfs2 = $this->file_exists(array(
					'string' => $filename,
					'relatives' => array(RELATIVE_ROOT),
					'allow_outside' => false
				));


				if ($file['mime_type'] != 'Directory')
				{
					if ($file_exists_in_vfs2)
					{
						$file_is_dir = $this->file_type(array(
							'string' => $filename,
							'relatives' => array(RELATIVE_ROOT)
						)) == 'Directory';

						if (!$file_is_dir)
						{
							//good - just add permissions to the old owner

							//1. get information about file

							$file_info = $this->ls(array(
								'string' => $filename,
								'relatives' => array(RELATIVE_ROOT),
								'nofiles' => true,
								'checksubdirs' => false,
								'allow_outside' => false
							));

							if (!$file_info['proper_id'])
							{
								$this->set_attributes(array(
									'string' => $filename,
									'relatives' => array(RELATIVE_ROOT),
									'attributes' => array(
										'prefix' => $GLOBALS['phpgw']->accounts->id2name($file['owner_id']),
										'comment' => $file['comment']
										)
								));
							}

							//if user is not the owner of the file, will
							//add him in the list of authorized personnel
							if (array_key_exists(0,$file_info) && $file_info[0]['owner_id'] != $file['owner_id'])
							{
								$file_id = $this->get_file_id(array(
									'string' => $filename,
									'relatives' => array(RELATIVE_ROOT)
								));

								if ($file_id != 0)
								{
									if ($filename != "/" && $filename != "/home")
									{
										$perms = $this->vfs_sharing->get_permissions($file_id);
										$perms[$file['owner_id']] = PHPGW_ACL_READ | PHPGW_ACL_ADD | PHPGW_ACL_EDIT | PHPGW_ACL_DELETE;

										$this->vfs_sharing->set_permissions(array($file_id => $perms));
									}

								}
								else
								{
									//something gone wrong... This should not have
									//happened
									trigger_error('Failed in permission setting of file <b>'.$filename.'</b> at importing procedure.');
								}
							}
							continue;
						}
						else 
						{
							$this->mv(array(
								'from' => $filename,
								'to'   => $filename.'_renamed',
								'relatives' => array(RELATIVE_ROOT,RELATIVE_ROOT)
							));
							//error, bacuse wanting to touch a file that is a
							//dir in new vfs
						}
						
					}

					$this->touch(array(
						'string' => $filename,
						'relatives' => array(RELATIVE_ROOT)
					));

					unset($file['file_id']);

					$file['prefix'] = $GLOBALS['phpgw']->accounts->id2name($file['owner_id']);

					$this->set_attributes(array(
						'string' => $filename,
						'relatives' => array(RELATIVE_ROOT),
						'attributes' => $file
						
					));

				}
			}
			$this->override_acl = false;
		}

		#                                              #
		#  Functions that treat with application dirs  #
		#                                              #

		//gets the root of the application files dir
		function get_appfiles_root()
		{
			if ($this->appfiles_root)
			{
				return $this->appfiles_root;
			}
			
			$appfiles_root = '/'.$GLOBALS['phpgw_info']['server']['file_appfolder'];

			if ($appfiles_root == '/') //folder not set in setup
			{
				//see if there is a /infolog file in vfs. If have, use it. Else use /appfiles.
				$res = $this->db->select('phpgw_vfs2_files','file_id',array('directory'=>'/','name'=>'infolog'),__LINE__,__FILE__);
				if ($this->db->next_record()) //dir exists
				{
					$appfiles_root = '/infolog';
				}
				else
				{
					$appfiles_root = '/appfiles';
				}
			}
			$this->appfiles_root = $appfiles_root;
			return $appfiles_root;
		}

		//is folder belonging to an application (like for instance projects)
		//$path is the virtual path. Pass the real path here seems nonsense now.
		function is_appfolder($path) 
		{
			$app_files_root = $this->get_appfiles_root();
			
			return preg_match('/'.str_replace('/',"\\/",$app_files_root).'/',$path);
		}	
	

		/**
		 * Method: get_external_name
		 *
		 *		Given an application name and an id of this application,
		 *		returns its name.
		 */
		function get_external_name($p_appname,$p_appid)
		{
			if (!$this->external_files_info)
			{
				$this->_consult_external_files_info();
			}
			
			if (!$this->external_files_info[$p_appname][$p_appid]['caption'])
			{
				return $GLOBALS['phpgw']->hooks->single(array(
					'appname' => $p_appname,
					'location' => 'get_name',
					'id' => $p_appid
					));
			}
			else
			{
				return $this->external_files_info[$p_appname][$p_appid]['caption'];
			}
		}

		/**
		 * Method: get_external_files_info
		 *
		 *		Returns all the external files info. This is good for some applications
		 *		that need to show all the external files or all the external folders
		 */
		function &get_external_files_info()
		{
			if (!$this->external_files_info)
			{
				$this->_consult_external_files_info();
			}
			return $this->external_files_info;
		}
		
		/**
		 * Method: _consult_external_files_info
		 *
		 *		Consults the external files info and store it in the vfs 
		 *		object attribute $this->external_files_info
		 */
		function _consult_external_files_info()
		{
			//Gets the application dirs that were set as shared, to include them
			//also in the tree
			$application_shared_files = $this->vfs_sharing->get_shares(array(
				'only_dir' => $this->get_appfiles_root(),
				'account_id' => $GLOBALS['phpgw_info']['user']['account_id']
				));

			foreach($application_shared_files as $shared_file)
			{
				$shared_file = ereg_replace($this->get_appfiles_root().'\/*','',$shared_file['directory'].'/'.$shared_file['name']);
				$shared_file = ereg_replace('\/*$','',$shared_file);
				$shared_file = explode('/',$shared_file);

				$appname = $shared_file[0];
				$appid = $shared_file[1];

				$include_also[$appname][] = $appid;
			}

			$this->external_files_info = $GLOBALS['phpgw']->hooks->process(array(
				'location' => 'files_info',
				'account_id' => $this->userinfo['account_id'],
				'include_also' => $include_also
				));
		}

		/**
		 * Method: set_sharing
		 *
		 *		Sets the file sharing of ONE file to the given ACL
		 *
		 * Parameters:
		 * 
		 * 		string - the full string describing the file
		 *      relatives - the array of relativity
		 *      permissions - an array with the user id as key, and the permissions
		 * 		          (that are a boolean operation of PHPGW_ACLs) as value.
		 *		          If you want to apply a default permission to all users,
		 * 		          use the 0 id as key
		 */
		function set_sharing($params)
		{
			//sets the file attibute to be 'shared=Y'
			$this->set_attributes(array(
				'string' => $params['string'],
				'relatives' => $params['relatives'],
				'attributes' => array('shared' => 'Y')
				));

			$file_id = $this->get_file_id(array(
				'string' => $params['string'],
				'relatives' => $params['relatives']
				));

			$this->vfs_sharing->set_permissions(array($file_id => $params['permissions']));
		}


		/**
		 * Method: unset_sharing
		 *
		 *		Unsets all file sharing and ACL permissions for a file
		 *
		 * Parameters:
		 * 
		 * 		string - the full string describing the file
		 *      relatives - the array of relativity
		 */
		function unset_sharing($params)
		{
			$this->set_attributes(array(
				'string' => $params['string'],
				'relatives' => $params['relatives'],
				'attributes' => array('shared' => 'N')
				));

			$file_id = $this->get_file_id(array(
				'string' => $params['string'],
				'relatives' => $params['relatives']
				));
		
			$this->vfs_sharing->remove_all_permissions($file_id);
		}
	}
?>
