source: trunk/phpgwapi/inc/class.vfs_dav.inc.php @ 2

Revision 2, 74.9 KB checked in by niltonneto, 15 years ago (diff)

Removida todas as tags usadas pelo CVS ($Id, $Source).
Primeira versão no CVS externo.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1<?php
2  /**************************************************************************\
3  * eGroupWare API - VFS                                                     *
4  * This file written by Jason Wies (Zone) <zone@phpgroupware.org>           *
5  * This class handles file/dir access for eGroupWare                        *
6  * Copyright (C) 2001-2003 Jason Wies, Jonathon Sim                         *
7  * -------------------------------------------------------------------------*
8  * This library is part of the eGroupWare API                               *
9  * http://www.egroupware.org/api                                            *
10  * ------------------------------------------------------------------------ *
11  * This library is free software; you can redistribute it and/or modify it  *
12  * under the terms of the GNU Lesser General Public License as published by *
13  * the Free Software Foundation; either version 2.1 of the License,         *
14  * or any later version.                                                    *
15  * This library is distributed in the hope that it will be useful, but      *
16  * WITHOUT ANY WARRANTY; without even the implied warranty of               *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                     *
18  * See the GNU Lesser General Public License for more details.              *
19  * You should have received a copy of the GNU Lesser General Public License *
20  * along with this library; if not, write to the Free Software Foundation,  *
21  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA            *
22  \**************************************************************************/
23
24
25        /*Different aspects of debugging. DEBUG enables debug output for this class
26        DEBUG_SQL enables some SQL debugging.  DEBUG_DAV enables (LOTS) of debugging inside
27        the HTTP class
28        */
29        define ('DEBUG', 0);
30        define ('TRACE', 0);//This generates a whole lotta output
31        define ('DEBUG_SQL', 0);
32        define ('DEBUG_DAV', 0);
33
34        class vfs
35        {
36                var $basedir;
37                var $fakebase;
38                var $relative;
39                var $working_id;
40                var $working_lid;
41                var $attributes;
42                var $override_acl;
43                var $linked_dirs;
44                var $meta_types;
45                var $now;
46                var $override_locks;
47
48                //These are DAV-native properties that have different names in VFS
49                var $vfs_property_map = array(
50                        'creationdate' => 'created',
51                        'getlastmodified' => 'modified',
52                        'getcontentlength' => 'size',
53                        'getcontenttype' => 'mime_type',
54                        'description' => 'comment',
55                        'creator_id' => 'createdby_id',
56                        'contributor_id' => 'modifiedby_id',
57                        'publisher_id' => 'owner_id'
58                );
59               
60                /*!
61                @function vfs
62                @abstract constructor, sets up variables
63                */
64
65                function vfs ()
66                {
67                        $this->basedir = $GLOBALS['phpgw_info']['server']['files_dir'];
68                        $this->fakebase = '/home';
69                        $this->working_id = $GLOBALS['phpgw_info']['user']['account_id'];
70                        $this->working_lid = $GLOBALS['phpgw']->accounts->id2name($this->working_id);
71                        $this->now = date ('Y-m-d');
72                        $this->override_acl = 0;
73                        /*
74                           File/dir attributes, each corresponding to a database field.  Useful for use in loops
75                           If an attribute was added to the table, add it here and possibly add it to
76                           set_attributes ()
77
78                           set_attributes now uses this array().   07-Dec-01 skeeter
79                        */
80
81                        $this->attributes = array(
82                                'file_id',
83                                'owner_id',
84                                'createdby_id',
85                                'modifiedby_id',
86                                'created',
87                                'modified',
88                                'size',
89                                'mime_type',
90                                'deleteable',
91                                'comment',
92                                'app',
93                                'directory',
94                                'name',
95                                'link_directory',
96                                'link_name',
97                                'version'
98                        );
99
100                        /*
101                           These are stored in the MIME-type field and should normally be ignored.
102                           Adding a type here will ensure it is normally ignored, but you will have to
103                           explicitly add it to acl_check (), and to any other SELECT's in this file
104                        */
105
106                        $this->meta_types = array ('journal', 'journal-deleted');
107
108                        /* We store the linked directories in an array now, so we don't have to make the SQL call again */
109                        if ($GLOBALS['phpgw_info']['server']['db_type']=='mssql'
110                                || $GLOBALS['phpgw_info']['server']['db_type']=='sybase')
111                        {
112                                $query = $GLOBALS['phpgw']->db->query ("SELECT directory, name, link_directory, link_name FROM phpgw_vfs WHERE CONVERT(varchar,link_directory) != '' AND CONVERT(varchar,link_name) != ''" . $this->extra_sql (array ('query_type' => VFS_SQL_SELECT)), __LINE__,__FILE__);
113                        }
114                        else
115                        {
116                                $query = $GLOBALS['phpgw']->db->query ("SELECT directory, name, link_directory, link_name FROM phpgw_vfs WHERE (link_directory IS NOT NULL or link_directory != '') AND (link_name IS NOT NULL or link_name != '')" . $this->extra_sql (array ('query_type' => VFS_SQL_SELECT)), __LINE__,__FILE__);
117                        }
118
119                        $this->linked_dirs = array ();
120                        while ($GLOBALS['phpgw']->db->next_record ())
121                        {
122                                $this->linked_dirs[] = $GLOBALS['phpgw']->db->Record;
123                        }
124
125                       
126                        $this->repository = $GLOBALS['phpgw_info']['server']['files_dir'];
127                        $this->dav_user=$GLOBALS['phpgw_info']['user']['userid'];
128                        $this->dav_pwd=$GLOBALS['phpgw_info']['user']['passwd'];
129                        $parsed_url = parse_url($this->repository);
130                        $this->dav_host=$parsed_url['host'];
131                        $this->dav_port=@isset($parsed_url['port']) ? $parsed_url['port'] : 80;
132
133                        $this->dav_client = CreateObject('phpgwapi.http_dav_client');
134                        $this->dav_client->set_credentials($this->dav_user,$this->dav_pwd);
135                        $this->dav_client->set_attributes($this->attributes,$this->vfs_property_map);
136                        $result = $this->dav_client->connect($this->dav_host,$this->dav_port);
137                        if (DEBUG_DAV)
138                        {
139                                echo '<b>DAV client debugging enabled!</b>';
140                                $this->dav_client->set_debug(DBGTRACE|DBGINDATA|DBGOUTDATA|DBGSOCK|DBGLOW);
141                        }
142                        if (!$result)
143                        {
144                                echo '<h2>Cannot connect to the file repository server!</h2>';
145                                die($this->dav_client->get_body());
146                        }
147                        //determine the supported DAV features
148/*                      $features = $this->dav_client->dav_features('http://'.$this->dav_host);
149                        if (!$features || ! in_array( '1', $features) )
150                        {
151                                die("Error :: The specified file repository: $this->dav_host doesn't appear to support WebDAV! ");
152                       
153                        }
154*/     
155                        //Reload the overriden_locks
156                        $app = $GLOBALS['phpgw_info']['flags']['currentapp'];
157                        $session_data = base64_decode($GLOBALS['phpgw']->session->appsession ('vfs_dav',$app));
158                        $this->override_locks = array();
159                        if ($session_data)
160                        {
161                                $locks = explode('\n', $session_data); 
162                                foreach ($locks as $lock)
163                                {
164                                        $lockdata = explode(';', $lock);
165                                        $name = $lockdata[0];
166                                        $token = $lockdata[1];
167                                        $this->override_locks[$name] = $token;
168                                }
169                        }
170
171                                register_shutdown_function(array(&$this, 'vfs_umount'));
172                                $this->debug('Constructed with debug enabled');
173                       
174                }
175
176                //TODO:  Get rid of this
177                //A quick, temporary debug output function
178                function debug($info) {
179                        if (DEBUG)
180                        {
181                                echo '<b> vfs_sql_dav debug:<em> ';
182                                if (is_array($info))
183                                {
184                                        print_r($info);
185                                }
186                                else
187                                {
188                                        echo $info;
189                                }
190                                echo '</em></b><br>';
191                        }
192                }
193
194                /*!
195                @function dav_path
196                @abstract Apaches mod_dav in particular requires that the path sent in a dav request NOT be a URI
197                */
198                function dav_path($uri) {
199                        //$this->debug('DAV path');
200                        $parsed = parse_url($uri);
201                        return $parsed['path'];
202                }
203
204                /*!
205                @function glue_url
206                @abstract glues a parsed url (ie parsed using PHP's parse_url) back
207                        together
208                @param $url     The parsed url (its an array)
209                */
210                function glue_url ($url){
211                        if (!is_array($url))
212                        {
213                                return false;
214                        }
215                        // scheme
216                        $uri = (!empty($url['scheme'])) ? $url['scheme'].'://' : '';
217                        // user & pass
218                        if (!empty($url['user']))
219                        {
220                                $uri .= $url['user'];
221                                if (!empty($url['pass']))
222                                {
223                                        $uri .=':'.$url['pass'];
224                                }
225                                $uri .='@';
226                        }
227                        // host
228                        $uri .= $url['host'];
229                        // port
230                        $port = (!empty($url['port'])) ? ':'.$url['port'] : '';
231                        $uri .= $port;
232                        // path
233                        $uri .= $url['path'];
234                        // fragment or query
235                        if (isset($url['fragment']))
236                        {
237                                $uri .= '#'.$url['fragment'];
238                        } elseif (isset($url['query']))
239                        {
240                                $uri .= '?'.$url['query'];
241                        }
242                        return $uri;
243                }
244
245                function dav_host($uri) {
246                        //$this->debug('DAV path');
247                        $parsed = parse_url($uri);
248                        $parsed['path'] = '';
249                        $host = $this->glue_url($parsed);
250                        return $host;
251                }
252
253                function vfs_umount()
254                {
255                        $this->dav_client->disconnect();
256                }
257
258
259                /*!
260                @function set_relative
261                @abstract Set path relativity
262                @param mask Relative bitmask (see RELATIVE_ defines)
263                */
264                function set_relative ($data)
265                {
266                        if (!is_array ($data))
267                        {
268                                $data = array ();
269                        }
270
271                        if (!$data['mask'])
272                        {
273                                unset ($this->relative);
274                        }
275                        else
276                        {
277                                $this->relative = $data['mask'];
278                        }
279                }
280
281                /*!
282                @function get_relative
283                @abstract Return relativity bitmask
284                @discussion Returns relativity bitmask, or the default of "completely relative" if unset
285                */
286                function get_relative ()
287                {
288                        if (isset ($this->relative) && $this->relative)
289                        {
290                                return $this->relative;
291                        }
292                        else
293                        {
294                                return RELATIVE_ALL;
295                        }
296                }
297
298                /*!
299                @function sanitize
300                @abstract Removes leading .'s from 'string'
301                @discussion You should not pass all filenames through sanitize () unless you plan on rejecting
302                                .files.  Instead, pass the name through securitycheck () first, and if it fails,
303                                pass it through sanitize
304                @param string string to sanitize
305                @result $string 'string' without it's leading .'s
306                */
307                function sanitize ($data)
308                {
309                        if (!is_array ($data))
310                        {
311                                $data = array ();
312                        }
313
314                        /* We use path_parts () just to parse the string, not translate paths */
315                        $p = $this->path_parts (array(
316                                        'string' => $data['string'],
317                                        'relatives' => array (RELATIVE_NONE)
318                                )
319                        );
320
321                        return (ereg_replace ('^\.+', '', $p->fake_name));
322                }
323
324                /*!
325                @function securitycheck
326                @abstract Security check function
327                @discussion Checks for basic violations such as ..
328                                If securitycheck () fails, run your string through vfs->sanitize ()
329                @param string string to check security of
330                @result Boolean True/False.  True means secure, False means insecure
331                */
332                function securitycheck ($data)
333                {
334                        if (!is_array ($data))
335                        {
336                                $data = array ();
337                        }
338
339                        if (substr ($data['string'], 0, 1) == "\\" || strstr ($data['string'], "..") || strstr ($data['string'], "\\..") || strstr ($data['string'], ".\\."))
340                        {
341                                return False;
342                        }
343                        else
344                        {
345                                return True;
346                        }
347                }
348
349                /*!
350                @function db_clean
351                @abstract Clean 'string' for use in database queries
352                @param string String to clean
353                @result Cleaned version of 'string'
354                */
355                function db_clean ($data)
356                {
357                        if (!is_array ($data))
358                        {
359                                $data = array ();
360                        }
361
362                        $string = ereg_replace ("'", "\'", $data['string']);
363
364                        return $string;
365                }
366
367                /*!
368                @function extra_sql
369                @abstract Return extra SQL code that should be appended to certain queries
370                @param query_type The type of query to get extra SQL code for, in the form of a VFS_SQL define
371                @result Extra SQL code
372                */
373                function extra_sql ($data)
374                { //This is purely for SQL
375                        return '';
376                }
377
378                /*!
379                @function add_journal
380                @abstract Add a journal entry after (or before) completing an operation,
381                          and increment the version number.  This function should be used internally only
382                @discussion Note that state_one and state_two are ignored for some VFS_OPERATION's, for others
383                            they are required.  They are ignored for any "custom" operation
384                            The two operations that require state_two:
385                            operation                   state_two
386                            VFS_OPERATION_COPIED        fake_full_path of copied to
387                            VFS_OPERATION_MOVED         fake_full_path of moved to
388
389                            If deleting, you must call add_journal () before you delete the entry from the database
390                @param string File or directory to add entry for
391                @param relatives Relativity array
392                @param operation The operation that was performed.  Either a VFS_OPERATION define or
393                                  a non-integer descriptive text string
394                @param state_one The first "state" of the file or directory.  Can be a file name, size,
395                                  location, whatever is appropriate for the specific operation
396                @param state_two The second "state" of the file or directory
397                @param incversion Boolean True/False.  Increment the version for the file?  Note that this is
398                                   handled automatically for the VFS_OPERATION defines.
399                                   i.e. VFS_OPERATION_EDITED would increment the version, VFS_OPERATION_COPIED
400                                   would not
401                @result Boolean True/False
402                */
403                function add_journal ($data) {
404                //The journalling dont work :(  Ideally this will become "versioning"
405                        return True;
406                }
407
408
409                /*!
410                @function flush_journal
411                @abstract Flush journal entries for $string.  Used before adding $string
412                @discussion flush_journal () is an internal function and should be called from add_journal () only
413                @param string File/directory to flush journal entries of
414                @param relatives Realtivity array
415                @param deleteall Delete all types of journal entries, including the active Create entry.
416                                  Normally you only want to delete the Create entry when replacing the file
417                                  Note that this option does not effect $deleteonly
418                @param deletedonly Only flush 'journal-deleted' entries (created when $string was deleted)
419                @result Boolean True/False
420                */
421                function flush_journal ($data)
422                {
423                        return True;
424                }
425
426
427                /*!
428                @function get_journal
429                @abstract Retrieve journal entries for $string
430                @param string File/directory to retrieve journal entries of
431                @param relatives Relativity array
432                @param type 0/False = any, 1 = 'journal', 2 = 'journal-deleted'
433                @result Array of arrays of journal entries
434                */
435                function get_journal ($data)
436                {
437                        return array();
438                }
439
440                /*!
441                @function path_parts
442                @abstract take a real or fake pathname and return an array of its component parts
443                @param string full real or fake path
444                @param relatives Relativity array
445                @param object True returns an object instead of an array
446                @param nolinks Don't check for links (made with make_link ()).  Used internally to prevent recursion
447                @result $rarray/$robject Array or object containing the fake and real component parts of the path
448                @discussion Returned values are:
449                                mask
450                                outside
451                                fake_full_path
452                                fake_leading_dirs
453                                fake_extra_path         BROKEN
454                                fake_name
455                                real_full_path
456                                real_leading_dirs
457                                real_extra_path         BROKEN
458                                real_name
459                                fake_full_path_clean
460                                fake_leading_dirs_clean
461                                fake_extra_path_clean   BROKEN
462                                fake_name_clean
463                                real_full_path_clean
464                                real_leading_dirs_clean
465                                real_extra_path_clean   BROKEN
466                                real_name_clean
467                                real_uri
468                        "clean" values are run through vfs->db_clean () and
469                        are safe for use in SQL queries that use key='value'
470                        They should be used ONLY for SQL queries, so are used
471                        mostly internally
472                        mask is either RELATIVE_NONE or RELATIVE_NONE|VFS_REAL,
473                        and is used internally
474                        outside is boolean, True if 'relatives' contains VFS_REAL
475                */
476                function path_parts ($data)
477                {
478                        $default_values = array
479                                (
480                                        'relatives'     => array (RELATIVE_CURRENT),
481                                        'object'        => True,
482                                        'nolinks'       => False
483                                );
484
485                        $data = array_merge ($this->default_values ($data, $default_values), $data);
486
487                        $sep = SEP;
488
489                        $rarray['mask'] = RELATIVE_NONE;
490
491                        if (!($data['relatives'][0] & VFS_REAL))
492                        {
493                                $rarray['outside'] = False;
494                                $fake = True;
495                        }
496                        else
497                        {
498                                $rarray['outside'] = True;
499                                $rarray['mask'] |= VFS_REAL;
500                        }
501
502                        $string = $this->getabsolutepath (array(
503                                        'string'        => $data['string'],
504                                        'mask'  => array ($data['relatives'][0]),
505                                        'fake'  => $fake
506                                )
507                        );
508
509                        if ($fake)
510                        {
511                                $base_sep = '/';
512                                $base = '/';
513
514                                $opp_base = $this->basedir . $sep;
515
516                                $rarray['fake_full_path'] = $string;
517                        }
518                        else
519                        {
520                                $base_sep = $sep;
521                                if (ereg ("^$this->basedir" . $sep, $string))
522                                {
523                                        $base = $this->basedir . $sep;
524                                }
525                                else
526                                {
527                                        $base = $sep;
528                                }
529
530                                $opp_base = '/';
531                                $rarray['real_full_url'] = $string;
532                                $rarray['real_full_path'] = $this->dav_path($string);
533                        }
534
535                        /* This is needed because of substr's handling of negative lengths */
536                        $baselen = strlen ($base);
537                        $lastslashpos = strrpos ($string, $base_sep);
538                        $lastslashpos < $baselen ? $length = 0 : $length = $lastslashpos - $baselen;
539
540                        $extra_path = $rarray['fake_extra_path'] = $rarray['real_extra_path'] = substr ($string, strlen ($base), $length);
541                        $name = $rarray['fake_name'] = $rarray['real_name'] = substr ($string, strrpos ($string, $base_sep) + 1);
542
543                        if ($fake)
544                        {
545                                $rarray['real_extra_path'] ? $dispsep = $sep : $dispsep = '';
546                                $rarray['real_full_url'] = $opp_base . $rarray['real_extra_path'] . $dispsep . $rarray['real_name'];
547                                $rarray['real_full_path'] = $this->dav_path($rarray['real_full_url']);
548                                if ($extra_path)
549                                {
550                                        $rarray['fake_leading_dirs'] = $base . $extra_path;
551                                        $rarray['real_leading_dirs'] = $this->dav_path($opp_base . $extra_path);
552                                }
553                                elseif (strrpos ($rarray['fake_full_path'], $sep) == 0)
554                                {
555                                        /* If there is only one $sep in the path, we don't want to strip it off */
556                                        $rarray['fake_leading_dirs'] = $sep;
557                                        $rarray['real_leading_dirs'] = $this->dav_path( substr ($opp_base, 0, strlen ($opp_base) - 1));
558                                }
559                                else
560                                {
561                                        /* These strip the ending / */
562                                        $rarray['fake_leading_dirs'] = substr ($base, 0, strlen ($base) - 1);
563                                        $rarray['real_leading_dirs'] = $this->dav_path( substr ($opp_base, 0, strlen ($opp_base) - 1));
564                                }
565                        }
566                        else
567                        {
568                                $rarray['fake_full_path'] = $opp_base . $rarray['fake_extra_path'] . '/' . $rarray['fake_name'];
569                                if ($extra_path)
570                                {
571                                        $rarray['fake_leading_dirs'] = $opp_base . $extra_path;
572                                        $rarray['real_leading_dirs'] = $this->dav_path($base . $extra_path);
573                                }
574                                else
575                                {
576                                        $rarray['fake_leading_dirs'] = substr ($opp_base, 0, strlen ($opp_base) - 1);
577                                        $rarray['real_leading_dirs'] = $this->dav_path(substr ($base, 0, strlen ($base) - 1));
578                                }
579                        }
580
581                        /* We check for linked dirs made with make_link ().  This could be better, but it works */
582                        if (!$data['nolinks'])
583                        {
584                                reset ($this->linked_dirs);
585                                while (list ($num, $link_info) = each ($this->linked_dirs))
586                                {
587                                        if (ereg ("^$link_info[directory]/$link_info[name](/|$)", $rarray['fake_full_path']))
588                                        {
589                                                $rarray['real_full_path'] = ereg_replace ("^$this->basedir", '', $rarray['real_full_path']);
590                                                $rarray['real_full_path'] = ereg_replace ("^$link_info[directory]" . SEP . "$link_info[name]", $link_info['link_directory'] . SEP . $link_info['link_name'], $rarray['real_full_path']);
591
592                                                $p = $this->path_parts (array(
593                                                                'string'        => $rarray['real_full_path'],
594                                                                'relatives'     => array (RELATIVE_NONE|VFS_REAL),
595                                                                'nolinks'       => True
596                                                        )
597                                                );
598
599                                                $rarray['real_leading_dirs'] = $this->dav_path($p->real_leading_dirs);
600                                                $rarray['real_extra_path'] = $p->real_extra_path;
601                                                $rarray['real_name'] = $p->real_name;
602                                        }
603                                }
604                        }
605                       
606                        /*
607                                Create the 'real_auth_url', which includes the user and
608                                password (for the view method to redirect you there)
609                        */
610
611                        $parsed_url = parse_url($rarray['real_full_url']);
612                        $parsed_url['user'] = $this->dav_user;
613//                      $parsed_url['pass'] = $this->dav_pwd;
614                        $rarray['real_full_auth_url'] = $this->glue_url($parsed_url);
615                                       
616                        $parsed_url = parse_url($rarray['real_full_url']);
617                        $parsed_url['scheme'] = 'https';
618                        $parsed_url['user'] = $this->dav_user;
619                        $rarray['real_full_secure_url'] = $this->glue_url($parsed_url);
620                       
621                       
622                        /*
623                           We have to count it before because new keys will be added,
624                           which would create an endless loop
625                        */
626                        $count = count ($rarray);
627                        reset ($rarray);
628                        for ($i = 0; (list ($key, $value) = each ($rarray)) && $i != $count; $i++)
629                        {
630                                $rarray[$key . '_clean'] = $this->db_clean (array ('string' => $value));
631                        }
632
633                        if ($data['object'])
634                        {
635                                $robject = new path_class;
636
637                                reset ($rarray);
638                                while (list ($key, $value) = each ($rarray))
639                                {
640                                        $robject->$key = $value;
641                                }
642                        }
643
644/*
645                        echo "<br>fake_full_path: $rarray[fake_full_path]
646                                <br>fake_leading_dirs: $rarray[fake_leading_dirs]
647                                <br>fake_extra_path: $rarray[fake_extra_path]
648                                <br>fake_name: $rarray[fake_name]
649                                <br>real_full_path: $rarray[real_full_path]
650                                <br>real_full_url: $rarray[real_full_url]
651                                <br>real_leading_dirs: $rarray[real_leading_dirs]
652                                <br>real_extra_path: $rarray[real_extra_path]
653                                <br>real_name: $rarray[real_name]";
654*/
655
656                        if ($data['object'])
657                        {
658                                return ($robject);
659                        }
660                        else
661                        {
662                                return ($rarray);
663                        }
664                }
665
666                /*!
667                @function getabsolutepath
668                @abstract get the absolute path
669                @param string defaults to False, directory/file to get path of, relative to relatives[0]
670                @param mask Relativity bitmask (see RELATIVE_ defines).  RELATIVE_CURRENT means use $this->relative
671                @param fake Returns the "fake" path, ie /home/user/dir/file (not always possible.  use path_parts () instead)
672                @result $basedir Full fake or real path
673                */
674                function getabsolutepath ($data)
675                {
676                        $default_values = array
677                                (
678                                        'string'        => False,
679                                        'mask'  => array (RELATIVE_CURRENT),
680                                        'fake'  => True
681                                );
682
683                        $data = array_merge ($this->default_values ($data, $default_values), $data);
684
685                        $currentdir = $this->pwd (False);
686
687                        /* If they supply just VFS_REAL, we assume they want current relativity */
688                        if ($data['mask'][0] == VFS_REAL)
689                        {
690                                $data['mask'][0] |= RELATIVE_CURRENT;
691                        }
692
693                        if (!$this->securitycheck (array(
694                                        'string'        => $data['string']
695                                ))
696                        )
697                        {
698                                return False;
699                        }
700
701                        if ($data['mask'][0] & RELATIVE_NONE)
702                        {
703                                return $data['string'];
704                        }
705
706                        if ($data['fake'])
707                        {
708                                $sep = '/';
709                        }
710                        else
711                        {
712                                $sep = SEP;
713                        }
714
715                        /* if RELATIVE_CURRENT, retrieve the current mask */
716                        if ($data['mask'][0] & RELATIVE_CURRENT)
717                        {
718                                $mask = $data['mask'][0];
719                                /* Respect any additional masks by re-adding them after retrieving the current mask*/
720                                $data['mask'][0] = $this->get_relative () + ($mask - RELATIVE_CURRENT);
721                        }
722
723                        if ($data['fake'])
724                        {
725                                $basedir = '/';
726                        }
727                        else
728                        {
729                                $basedir = $this->basedir . $sep;
730
731                                /* This allows all requests to use /'s */
732                                $data['string'] = preg_replace ("|/|", $sep, $data['string']);
733                        }
734
735                        if (($data['mask'][0] & RELATIVE_PATH) && $currentdir)
736                        {
737                                $basedir = $basedir . $currentdir . $sep;
738                        }
739                        elseif (($data['mask'][0] & RELATIVE_USER) || ($data['mask'][0] & RELATIVE_USER_APP))
740                        {
741                                $basedir = $basedir . $this->fakebase . $sep;
742                        }
743
744                        if ($data['mask'][0] & RELATIVE_CURR_USER)
745                        {
746                                $basedir = $basedir . $this->working_lid . $sep;
747                        }
748
749                        if (($data['mask'][0] & RELATIVE_USER) || ($data['mask'][0] & RELATIVE_USER_APP))
750                        {
751                                $basedir = $basedir . $GLOBALS['phpgw_info']['user']['account_lid'] . $sep;
752                        }
753
754                        if ($data['mask'][0] & RELATIVE_USER_APP)
755                        {
756                                $basedir = $basedir . "." . $GLOBALS['phpgw_info']['flags']['currentapp'] . $sep;
757                        }
758
759                        /* Don't add string if it's a /, just for aesthetics */
760                        if ($data['string'] && $data['string'] != $sep)
761                        {
762                                $basedir = $basedir . $data['string'];
763                        }
764
765                        /* Let's not return // */
766                        while (ereg ($sep . $sep, $basedir))
767                        {
768                                $basedir = ereg_replace ($sep . $sep, $sep, $basedir);
769                        }
770
771                        $basedir = ereg_replace ($sep . '$', '', $basedir);
772
773                        return $basedir;
774                }
775
776                /*!
777                @function acl_check
778                @abstract Check ACL access to $file for $GLOBALS['phpgw_info']["user"]["account_id"];
779                @param string File to check access of
780                @discussion To check the access for a file or directory, pass 'string'/'relatives'/'must_exist'.
781                                To check the access to another user or group, pass 'owner_id'.
782                                If 'owner_id' is present, we bypass checks on 'string'/'relatives'/'must_exist'
783                @param relatives Standard relativity array
784                @param operation Operation to check access to.  In the form of a PHPGW_ACL defines bitmask.  Default is read
785                @param owner_id Owner id to check access of (see discussion above)
786                @param must_exist Boolean.  Set to True if 'string' must exist.  Otherwise, we check the parent directory as well
787                @result Boolean.  True if access is ok, False otherwise
788                */
789                function acl_check ($data)
790                {
791                        return True;
792                        $default_values = array
793                                (
794                                        'relatives'     => array (RELATIVE_CURRENT),
795                                        'operation'     => PHPGW_ACL_READ,
796                                        'must_exist'    => False
797                                );
798
799                        $data = array_merge ($this->default_values ($data, $default_values), $data);
800
801                        /* Accommodate special situations */
802                        if ($this->override_acl || $data['relatives'][0] == RELATIVE_USER_APP)
803                        {
804                                return True;
805                        }
806
807                        if (!$data['owner_id'])
808                        {
809                                $p = $this->path_parts (array(
810                                                'string'        => $data['string'],
811                                                'relatives'     => array ($data['relatives'][0])
812                                        )
813                                );
814
815                                /* Temporary, until we get symlink type files set up */
816                                if ($p->outside)
817                                {
818                                        return True;
819                                }
820
821                                /* Read access is always allowed here, but nothing else is */
822                                if ($data['string'] == '/' || $data['string'] == $this->fakebase)
823                                {
824                                        if ($data['operation'] == PHPGW_ACL_READ)
825                                        {
826                                                return True;
827                                        }
828                                        else
829                                        {
830                                                return False;
831                                        }
832                                }
833
834                                /* If the file doesn't exist, we get ownership from the parent directory */
835                                if (!$this->file_exists (array(
836                                                'string'        => $p->fake_full_path,
837                                                'relatives'     => array ($p->mask)
838                                        ))
839                                )
840                                {
841                                        if ($data['must_exist'])
842                                        {
843                                                return False;
844                                        }
845
846                                        $data['string'] = $p->fake_leading_dirs;
847                                        $p2 = $this->path_parts (array(
848                                                        'string'        => $data['string'],
849                                                        'relatives'     => array ($p->mask)
850                                                )
851                                        );
852
853                                        if (!$this->file_exists (array(
854                                                        'string'        => $data['string'],
855                                                        'relatives'     => array ($p->mask)
856                                                ))
857                                        )
858                                        {
859                                                return False;
860                                        }
861                                }
862                                else
863                                {
864                                        $p2 = $p;
865                                }
866
867                                $file_info = $this->ls($data);
868                                $owner_id = $file_info['owner_id'];
869                        }
870                        else
871                        {
872                                $owner_id = $data['owner_id'];
873                        }
874
875                        /* This is correct.  The ACL currently doesn't handle undefined values correctly */
876                        if (!$owner_id)
877                        {
878                                $owner_id = 0;
879                        }
880
881                        $user_id = $GLOBALS['phpgw_info']['user']['account_id'];
882
883                        /* They always have access to their own files */
884                        if ($owner_id == $user_id)
885                        {
886                                return True;
887                        }
888
889                        /* Check if they're in the group */
890                        $memberships = $GLOBALS['phpgw']->accounts->membership ($user_id);
891
892                        if (is_array ($memberships))
893                        {
894                                reset ($memberships);
895                                while (list ($num, $group_array) = each ($memberships))
896                                {
897                                        if ($owner_id == $group_array['account_id'])
898                                        {
899                                                $group_ok = 1;
900                                                break;
901                                        }
902                                }
903                        }
904
905                        $acl = CreateObject ('phpgwapi.acl', $owner_id);
906                        $acl->account_id = $owner_id;
907                        $acl->read_repository ();
908
909                        $rights = $acl->get_rights ($user_id);
910
911                        /* Add privileges from the groups this user belongs to */
912                        if (is_array ($memberships))
913                        {
914                                reset ($memberships);
915                                while (list ($num, $group_array) = each ($memberships))
916                                {
917                                        $rights |= $acl->get_rights ($group_array['account_id']);
918                                }
919                        }
920
921                        if ($rights & $data['operation'])
922                        {
923                                return True;
924                        }
925                        elseif (!$rights && $group_ok)
926                        {
927                                $conf = CreateObject('phpgwapi.config', 'phpgwapi');
928                                $conf->read_repository();
929                                if ($conf->config_data['acl_default'] == 'grant')
930                                {
931                                        return True;
932                                }
933                                else
934                                {
935                                        return False;
936                                }
937                        }
938                        else
939                        {
940                                return False;
941                        }
942                }
943
944                /*!
945                @function cd
946                @abstract Change directory
947                @discussion To cd to the files root '/', use cd ('/', False, array (RELATIVE_NONE));
948                @param string default '/'.  directory to cd into.  if "/" and $relative is True, uses "/home/<working_lid>";
949                @param relative default True/relative means add target to current path, else pass $relative as mask to getabsolutepath()
950                @param relatives Relativity array
951                */
952                function cd ($data = '')
953                {
954                        if (!is_array ($data))
955                        {
956                                $noargs = 1;
957                                $data = array ();
958                        }
959
960                        $default_values = array
961                                (
962                                        'string'        => '/',
963                                        'relative'      => True,
964                                        'relatives'     => array (RELATIVE_CURRENT)
965                                );
966
967                        $data = array_merge ($this->default_values ($data, $default_values), $data);
968
969                        if ($data['relatives'][0] & VFS_REAL)
970                        {
971                                $sep = SEP;
972                        }
973                        else
974                        {
975                                $sep = '/';
976                        }
977
978                        if ($data['relative'] == 'relative' || $data['relative'] == True)
979                        {
980                                /* if 'string' is "/" and 'relative' is set, we cd to the user/group home dir */
981                                if ($data['string'] == '/')
982                                {
983                                        $data['relatives'][0] = RELATIVE_USER;
984                                        $basedir = $this->getabsolutepath (array(
985                                                        'string'        => False,
986                                                        'mask'  => array ($data['relatives'][0]),
987                                                        'fake'  => True
988                                                )
989                                        );
990                                }
991                                else
992                                {
993                                        $currentdir = $GLOBALS['phpgw']->session->appsession('vfs','');
994                                        $basedir = $this->getabsolutepath (array(
995                                                        'string'        => $currentdir . $sep . $data['string'],
996                                                        'mask'  => array ($data['relatives'][0]),
997                                                        'fake'  => True
998                                                )
999                                        );
1000                                }
1001                        }
1002                        else
1003                        {
1004                                $basedir = $this->getabsolutepath (array(
1005                                                'string'        => $data['string'],
1006                                                'mask'  => array ($data['relatives'][0])
1007                                        )
1008                                );
1009                        }
1010
1011                        $GLOBALS['phpgw']->session->appsession('vfs','',$basedir);
1012
1013                        return True;
1014                }
1015
1016                /*!
1017                @function pwd
1018                @abstract current working dir
1019                @param full default True returns full fake path, else just the extra dirs (false strips the leading /)
1020                @result $currentdir currentdir
1021                */
1022                function pwd ($data = '')
1023                {
1024                        $default_values = array
1025                                (
1026                                        'full'  => True
1027                                );
1028
1029                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1030
1031                        $currentdir = $GLOBALS['phpgw']->session->appsession('vfs','');
1032
1033                        if (!$data['full'])
1034                        {
1035                                $currentdir = ereg_replace ("^/", '', $currentdir);
1036                        }
1037
1038                        if ($currentdir == '' && $data['full'])
1039                        {
1040                                $currentdir = '/';
1041                        }
1042
1043                        $currentdir = trim ($currentdir);
1044
1045                        return $currentdir;
1046                }
1047
1048                /*!
1049                @function read
1050                @abstract return file contents
1051                @param string filename
1052                @param relatives Relativity array
1053                @result $contents Contents of $file, or False if file cannot be read
1054                */
1055                function read ($data)
1056                {
1057
1058                        /*If the user really wants to 'view' the file in the browser, it
1059                        is much smarter simply to redirect them to the files web-accessable
1060                        url */
1061/*                      $app = $GLOBALS['phpgw_info']['flags']['currentapp'];
1062                        if ( ! $data['noview'] && ($app == 'phpwebhosting' || $app = 'filemanager' ))
1063                        {
1064                                $this->view($data);
1065                        }       
1066*/                     
1067                        if (!is_array ($data))
1068                        {
1069                                $data = array ();
1070                        }
1071
1072                        $default_values = array
1073                                (
1074                                        'relatives'     => array (RELATIVE_CURRENT)
1075                                );
1076
1077                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1078
1079                        $p = $this->path_parts (array(
1080                                        'string'        => $data['string'],
1081                                        'relatives'     => array ($data['relatives'][0])
1082                                )
1083                        );
1084
1085                        if (!$this->acl_check (array(
1086                                        'string'        => $p->fake_full_path,
1087                                        'relatives'     => array ($p->mask),
1088                                        'operation'     => PHPGW_ACL_READ
1089                                ))
1090                        )
1091                        {
1092                                return False;
1093                        }
1094                        if ($p->outside)
1095                        {
1096                                           
1097                            if (! $fp = fopen ($p->real_full_path, 'r'))
1098                            {
1099                                return False;
1100                            }
1101                            $size=filesize($p->real_full_path);
1102                            $buffer=fread($fp, $size);
1103                            fclose ($fp);
1104                                return $buffer;
1105                        }
1106                        else
1107                        {
1108                                $status=$this->dav_client->get($p->real_full_path);
1109        $this->debug($this->dav_client->get_headers());
1110       
1111                                if($status != 200) return False;
1112                                $contents=$this->dav_client->get_body();
1113        $this->debug('Read:returning contents.  Status:'.$status);
1114                                return $contents;
1115                        }
1116                }
1117               
1118                /*
1119                @function view
1120                @abstract Redirect the users browser to the file
1121                @param string filename
1122                @param relatives Relativity array
1123                @result None (doesnt return)
1124                @discussion In the case of WebDAV, the file is web-accessible.  So instead
1125                of reading it into memory and then dumping it back out again when someone
1126                views a file, it makes much more sense to simply redirect, which is what
1127                this method does (its only called when reading from the file in the file manager,
1128                when the variable "noview" isnt set to "true"
1129                */
1130                function view($data)
1131                {       
1132               
1133                        $default_values = array
1134                                (
1135                                        'relatives'     => array (RELATIVE_CURRENT)
1136                                );
1137                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1138                        $p = $this->path_parts (array(
1139                                        'string'        => $data['string'],
1140                                        'relatives'     => array ($data['relatives'][0])
1141                                )
1142                        );
1143                       
1144                        //Determine whether the repository supports SSL
1145                        $parsed_url = parse_url($this->repository);
1146                        if ($parsed_url['scheme']=='https')
1147                        {
1148                                header( 'Location: '.$p->real_full_secure_url, true );
1149                        }
1150                        else
1151                        {
1152                                header( 'Location: '.$p->real_full_auth_url, true );                   
1153                        }
1154                        exit();
1155
1156                }
1157               
1158                /*
1159                @function lock
1160                @abstract DAV (class 2) locking - sets an exclusive write lock
1161                @param string filename
1162                @param relatives Relativity array
1163                @result True if successfull
1164                */             
1165                function lock ($data)
1166                {
1167                        $default_values = array
1168                                (
1169                                        'relatives'     => array (RELATIVE_CURRENT),
1170                                        'timeout'       => 'infinity',
1171                                       
1172                                );
1173
1174                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1175
1176                        $p = $this->path_parts (array(
1177                                        'string'        => $data['string'],
1178                                        'relatives'     => array ($data['relatives'][0])
1179                                )
1180                        );             
1181                        return $this->dav_client->lock($p->real_full_url, $this->dav_user, 0, $data['timeout']);
1182
1183                }
1184                function lock_token ($data)
1185                {
1186                        $default_values = array
1187                                (
1188                                        'relatives'     => array (RELATIVE_CURRENT),
1189                                        'token' => ''
1190                                );
1191
1192                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1193                       
1194                        $ls_array = $GLOBALS['phpgw']->vfs->ls (array (
1195                        'string'        => $data['string'],
1196                        'relatives'     => $data['relatives']
1197                        )
1198                        );     
1199                         $lock = @end($ls_array[0]['locks']);
1200                         $token = @end($lock['lock_tokens']);
1201                         return $token['full_name'];
1202                }
1203               
1204               
1205                /*
1206                @function add_lock_override
1207                @abstract override a lock
1208                @param string filename
1209                @param relatives Relativity array
1210                @param token (optional) a token for the lock we want to override
1211                @result None
1212                @discussion locks are no good unless you can write to a file you yourself locked:
1213                to do this call add_lock_override with the lock token (or without it - it will
1214                find it itself, so long as there is only one).  lock_override info is stored in
1215                the groupware session, so it will persist between page loads, but will be lost
1216                when the browser is closed
1217                */     
1218                function add_lock_override($data)
1219                {
1220                        $default_values = array
1221                        (
1222                                'relatives'     => array (RELATIVE_CURRENT),
1223                                'token' => ''
1224                               
1225                        );
1226
1227                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1228                       
1229                        if (!strlen($data['token']))
1230                        {
1231                                $ls_array = $GLOBALS['phpgw']->vfs->ls (array (
1232                                'string'        => $data['string'],
1233                                'relatives'     => $data['relatives']
1234                                )
1235                                );     
1236                                 $lock = @end($ls_array[0]['locks']);
1237                                 $token_array = @end($lock['lock_tokens']);
1238                                 $token =  $token_array['full_name'];
1239                        }
1240                        else
1241                        {
1242                                $token = $data['token'];
1243                        }
1244                         
1245                        $p = $this->path_parts (array(
1246                                        'string'        => $data['string'],
1247                                        'relatives'     => array ($data['relatives'][0])
1248                                )
1249                        );             
1250                        $this->override_locks[$p->real_full_path] = $token;
1251                        $this->save_session();
1252                }
1253               
1254                /*
1255                @function remove_lock_override
1256                @abstract stops overriding a lock
1257                @param string filename
1258                @param relatives Relativity array
1259                @result None
1260                */     
1261                function remove_lock_override($data)
1262                {
1263                        $default_values = array
1264                        (
1265                                'relatives'     => array (RELATIVE_CURRENT)
1266                               
1267                        );
1268
1269                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1270                       
1271                        if (!strlen($data['token']))
1272                        {
1273                                $ls_array = $GLOBALS['phpgw']->vfs->ls (array (
1274                                'string'        => $data['string'],
1275                                'relatives'     => $data['relatives']
1276                                )
1277                                );     
1278                                 $lock = @end($ls_array[0]['locks']);
1279                                 $token_array = @end($lock['lock_tokens']);
1280                                 $token =  $token_array['full_name'];
1281                        }
1282                        else
1283                        {
1284                                $token = $data['token'];
1285                        }
1286                         
1287                        $p = $this->path_parts (array(
1288                                        'string'        => $data['string'],
1289                                        'relatives'     => array ($data['relatives'][0])
1290                                )
1291                        );             
1292                        unset($this->override_locks[$p->real_full_path]);
1293                        $this->save_session();
1294                }       
1295               
1296                /*
1297                @function unlock
1298                @abstract DAV (class 2) unlocking - unsets the specified lock
1299                @param string filename
1300                @param relatives Relativity array
1301                @param tocken   The token for the lock we wish to remove.
1302                @result True if successfull
1303                */             
1304                function unlock ($data, $token)
1305                {
1306                        $default_values = array
1307                                (
1308                                        'relatives'     => array (RELATIVE_CURRENT),
1309                                        'content'       => ''
1310                                );
1311
1312                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1313
1314                        $p = $this->path_parts (array(
1315                                        'string'        => $data['string'],
1316                                        'relatives'     => array ($data['relatives'][0])
1317                                )
1318                        );
1319                        $this->remove_lock_override (array(
1320                                        'string'        => $data['string'],
1321                                        'relatives'     => array ($data['relatives'][0])
1322                                )
1323                        );
1324                        return $this->dav_client->unlock($p->real_full_url, $token);
1325
1326
1327                }
1328               
1329                /*
1330                @function options
1331                @abstract Allows querying for optional features - esp optional DAV features
1332                like locking
1333                @param option   The option you want to test for.  Options include 'LOCKING'
1334                        'VIEW', 'VERSION-CONTROL (eventually) etc
1335                @result true if the specified option is supported
1336                @discussion This should really check the server.  Unfortunately the overhead of doing this
1337                in every VFS instance is unacceptable (it essentially doubles the time for any request). Ideally
1338                we would store these features in the session perhaps?
1339                */             
1340                function options($option)
1341                {
1342                        switch ($option)
1343                        {
1344                        case 'LOCKING':
1345                                return true;
1346                        case 'VIEW':
1347                                return true;
1348                        default:
1349                                return false;
1350                        }
1351                }
1352               
1353                /*!
1354                @function write
1355                @abstract write to a file
1356                @param string file name
1357                @param relatives Relativity array
1358                @param content content
1359                @result Boolean True/False
1360                */
1361                function write ($data)
1362                {
1363                        $default_values = array
1364                                (
1365                                        'relatives'     => array (RELATIVE_CURRENT),
1366                                        'content'       => ''
1367                                );
1368
1369                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1370
1371                        $p = $this->path_parts (array(
1372                                        'string'        => $data['string'],
1373                                        'relatives'     => array ($data['relatives'][0])
1374                                )
1375                        );
1376
1377                        if ($this->file_exists (array (
1378                                        'string'        => $data['string'],
1379                                        'relatives'     => array ($data['relatives'][0])
1380                                ))
1381                        )
1382                        {
1383                                $acl_operation = PHPGW_ACL_EDIT;
1384                                $journal_operation = VFS_OPERATION_EDITED;
1385                        }
1386                        else
1387                        {
1388                                $acl_operation = PHPGW_ACL_ADD;
1389                        }
1390
1391                        if (!$this->acl_check (array(
1392                                        'string'        => $p->fake_full_path,
1393                                        'relatives'     => array ($p->mask),
1394                                        'operation'     => $acl_operation
1395                                ))
1396                        )
1397                        {
1398                                return False;
1399                        }
1400
1401                        //umask(000);
1402
1403                        /*
1404                           If 'string' doesn't exist, touch () creates both the file and the database entry
1405                           If 'string' does exist, touch () sets the modification time and modified by
1406                        */
1407                        /*$this->touch (array(
1408                                        'string'        => $p->fake_full_path,
1409                                        'relatives'     => array ($p->mask)
1410                                )
1411                        );*/
1412                       
1413                        $size=strlen($data['content']);
1414                        if ($p->outside)
1415                        {                           
1416                            if (! $fp = fopen ($p->real_full_path, 'w'))
1417                            {
1418                                return False;
1419                            }
1420                            $result = fwrite($fp, $data['content']);
1421                            fclose ($fp);
1422                            return $result;
1423                        }
1424                        else
1425                        {
1426                                $token =  $this->override_locks[$p->real_full_path];
1427                                $status=$this->dav_client->put($p->real_full_path,$data['content'],$token);
1428$this->debug('Put complete,  status: '.$status);
1429                                if($status!=201 && $status!=204)
1430                                {
1431                                        return False;
1432                                }
1433                                else
1434                                {
1435                                        return True;
1436                                }
1437                        }
1438                }
1439
1440                /*!
1441                @function touch
1442                @abstract Create blank file $file or set the modification time and modified by of $file to current time and user
1443                @param string File to touch or set modifies
1444                @param relatives Relativity array
1445                @result Boolean True/False
1446                */
1447                function touch ($data)
1448                {
1449                        $default_values = array(
1450                                                'relatives'     => array (RELATIVE_CURRENT)
1451                                                );
1452                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1453
1454                        $account_id = $GLOBALS['phpgw_info']['user']['account_id'];
1455                        $currentapp = $GLOBALS['phpgw_info']['flags']['currentapp'];
1456
1457                        $p = $this->path_parts (array(
1458                                                      'string'  => $data['string'],
1459                                                      'relatives'       => array ($data['relatives'][0])
1460                                                      )
1461                                                );
1462                        umask (000);
1463
1464                        /*
1465                           PHP's touch function will automatically decide whether to
1466                           create the file or set the modification time
1467                        */
1468                        if($p->outside)
1469                        {
1470                          return @touch($p->real_full_path);
1471                        }
1472                        elseif ($this->file_exists (array(
1473                                        'string'        => $p->fake_full_path,
1474                                        'relatives'     => array ($p->mask)
1475                                ))
1476                        )
1477                        {
1478                                $result =  $this->set_attributes (array(
1479                                                'string'        => $p->fake_full_path,
1480                                                'relatives'     => array ($p->mask),
1481                                                'attributes'    => array(
1482                                                                        'modifiedby_id' => $account_id,
1483                                                                        'modified' => $this->now
1484                                                )));
1485                        }
1486                        else
1487                        {
1488                                if (!$this->acl_check (array(
1489                                                'string'        => $p->fake_full_path,
1490                                                'relatives'     => array ($p->mask),
1491                                                'operation'     => PHPGW_ACL_ADD
1492                                        ))
1493                                ) return False;
1494                                $result = $this->write (array(
1495                                                      'string'  => $data['string'],
1496                                                      'relatives'       => array ($data['relatives'][0]),
1497                                                      'content' => ''
1498                                                      ));
1499                                $this->set_attributes(array(
1500                                        'string'        => $p->fake_full_path,
1501                                        'relatives'     => array ($p->mask),
1502                                        'attributes'    => array (
1503                                                                'createdby_id' => $account_id,
1504                                                                'created' => $this->now,
1505                                                                'app' => $currentapp
1506                                                        )));
1507                        }
1508
1509                        return ($result);
1510                }
1511
1512                /*!
1513                @function cp
1514                @abstract copy file
1515                @param from from file/directory
1516                @param to to file/directory
1517                @param relatives Relativity array
1518                @result boolean True/False
1519                */
1520                function cp ($data)
1521                {
1522                        $default_values = array
1523                                (
1524                                        'relatives'     => array (RELATIVE_CURRENT, RELATIVE_CURRENT)
1525                                );
1526
1527                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1528
1529                        $account_id = $GLOBALS['phpgw_info']['user']['account_id'];
1530
1531                        $f = $this->path_parts (array(
1532                                        'string'        => $data['from'],
1533                                        'relatives'     => array ($data['relatives'][0])
1534                                )
1535                        );
1536
1537                        $t = $this->path_parts (array(
1538                                        'string'        => $data['to'],
1539                                        'relatives'     => array ($data['relatives'][1])
1540                                )
1541                        );
1542
1543                        if (!$this->acl_check (array(
1544                                        'string'        => $f->fake_full_path,
1545                                        'relatives'     => array ($f->mask),
1546                                        'operation'     => PHPGW_ACL_READ
1547                                ))
1548                        )
1549                        {
1550                                return False;
1551                        }
1552
1553                        if ($this->file_exists (array(
1554                                        'string'        => $t->fake_full_path,
1555                                        'relatives'     => array ($t->mask)
1556                                ))
1557                        )
1558                        {
1559                                $remote_operation=PHPGW_ACL_EDIT;
1560                        }
1561                        else
1562                        {
1563                                $remote_operation=PHPGW_ACL_ADD;
1564
1565                        }
1566                        if (!$this->acl_check (array(
1567                                                     'string'   => $t->fake_full_path,
1568                                                     'relatives'        => array ($t->mask),
1569                                                     'operation'        => $remote_operation
1570                                                     ))
1571                            )
1572                        {
1573                                return False;
1574                        }
1575
1576                        umask(000);
1577
1578                        if ($this->file_type (array(
1579                                        'string'        => $f->fake_full_path,
1580                                        'relatives'     => array ($f->mask)
1581                                )) != 'Directory'
1582                        )
1583                        {
1584                         
1585                                if ($f->outside && $t->outside)
1586                                {
1587                                        return copy($f->real_full_path, $t->real_full_url);
1588                                }
1589                                elseif ($f->outside || $t->outside)
1590                                {
1591                                $content = $this->read(array(
1592                                                'string'        => $f->fake_full_path,
1593                                                'noview' => true,
1594                                                'relatives'     => array ($f->mask)
1595                                                )
1596                                        );
1597                                        $result = $this->write(array(
1598                                                'string'        => $t->fake_full_path,
1599                                                'relatives'     => array ($t->mask),
1600                                                'content' => $content
1601                                                )
1602                                        );
1603                            }
1604                                else
1605                                {
1606                                    $status=$this->dav_client->copy($f->real_full_path, $t->real_full_url,True, 'Infinity', $this->override_locks[$p->real_full_path]);
1607                                    $result = $status == 204 || $status==201;
1608                                    if (!$result)
1609                                    {
1610                                        return False;
1611                                    }
1612                                 }
1613
1614                                $this->set_attributes(array(
1615                                        'string'        => $t->fake_full_path,
1616                                        'relatives'     => array ($t->mask),
1617                                        'attributes' => array (
1618                                                                'owner_id' => $this->working_id,
1619                                                                'createdby_id' => $account_id,
1620                                                        )
1621                                                )
1622                                        );
1623                                return $result;
1624
1625                        }
1626                        else if (!($f->outside || $t->outside))
1627                        {
1628                                //if the files are both on server, its just a depth=infinity copy
1629                                $status=$this->dav_client->copy($f->real_full_path, $t->real_full_url,True, 'infinity', $this->override_locks[$p->real_full_path]);
1630                            if($status != 204 && $status!=201)
1631                            {
1632                                return False;
1633                            }
1634                            else
1635                            {
1636                                return True;
1637                            }
1638                        }
1639                        else    /* It's a directory, and one of the files is local */
1640                        {
1641                                /* First, make the initial directory */
1642                                $this->mkdir (array(
1643                                                'string'        => $data['to'],
1644                                                'relatives'     => array ($data['relatives'][1])
1645                                        )
1646                                );
1647
1648                                /* Next, we create all the directories below the initial directory */
1649                                $ls = $this->ls (array(
1650                                                'string'        => $f->fake_full_path,
1651                                                'relatives'     => array ($f->mask),
1652                                                'checksubdirs'  => True,
1653                                                'mime_type'     => 'Directory'
1654                                        )
1655                                );
1656
1657                                while (list ($num, $entry) = each ($ls))
1658                                {
1659                                        $newdir = ereg_replace ("^$f->fake_full_path", "$t->fake_full_path", $entry['directory']);
1660                                        $this->mkdir (array(
1661                                                        'string'        => $newdir.'/'.$entry['name'],
1662                                                        'relatives'     => array ($t->mask)
1663                                                )
1664                                        );
1665                                }
1666
1667                                /* Lastly, we copy the files over */
1668                                $ls = $this->ls (array(
1669                                                'string'        => $f->fake_full_path,
1670                                                'relatives'     => array ($f->mask)
1671                                        )
1672                                );
1673
1674                                while (list ($num, $entry) = each ($ls))
1675                                {
1676                                        if ($entry['mime_type'] == 'Directory')
1677                                        {
1678                                                continue;
1679                                        }
1680
1681                                        $newdir = ereg_replace ("^$f->fake_full_path", "$t->fake_full_path", $entry['directory']);
1682                                        $this->cp (array(
1683                                                        'from'  => "$entry[directory]/$entry[name]",
1684                                                        'to'    => "$newdir/$entry[name]",
1685                                                        'relatives'     => array ($f->mask, $t->mask)
1686                                                )
1687                                        );
1688                                }
1689                        }
1690
1691                        return True;
1692                }
1693
1694                function copy ($data)
1695                {
1696                        return $this->cp ($data);
1697                }
1698
1699                /*!
1700                @function mv
1701                @abstract move file/directory
1702                @param from from file/directory
1703                @param to to file/directory
1704                @param relatives Relativity array
1705                @result boolean True/False
1706                */
1707                function mv ($data)
1708                {
1709                        $default_values = array
1710                                (
1711                                        'relatives'     => array (RELATIVE_CURRENT, RELATIVE_CURRENT)
1712                                );
1713
1714                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1715
1716                        $account_id = $GLOBALS['phpgw_info']['user']['account_id'];
1717
1718                        $f = $this->path_parts (array(
1719                                        'string'        => $data['from'],
1720                                        'relatives'     => array ($data['relatives'][0])
1721                                )
1722                        );
1723
1724                        $t = $this->path_parts (array(
1725                                        'string'        => $data['to'],
1726                                        'relatives'     => array ($data['relatives'][1])
1727                                )
1728                        );
1729
1730                        if (!$this->acl_check (array(
1731                                        'string'        => $f->fake_full_path,
1732                                        'relatives'     => array ($f->mask),
1733                                        'operation'     => PHPGW_ACL_READ
1734                                ))
1735                                || !$this->acl_check (array(
1736                                        'string'        => $f->fake_full_path,
1737                                        'relatives'     => array ($f->mask),
1738                                        'operation'     => PHPGW_ACL_DELETE
1739                                ))
1740                        )
1741                        {
1742                                return False;
1743                        }
1744
1745                        if (!$this->acl_check (array(
1746                                        'string'        => $t->fake_full_path,
1747                                        'relatives'     => array ($t->mask),
1748                                        'operation'     => PHPGW_ACL_ADD
1749                                ))
1750                        )
1751                        {
1752                                return False;
1753                        }
1754
1755                        if ($this->file_exists (array(
1756                                        'string'        => $t->fake_full_path,
1757                                        'relatives'     => array ($t->mask)
1758                                ))
1759                        )
1760                        {
1761                                if (!$this->acl_check (array(
1762                                                'string'        => $t->fake_full_path,
1763                                                'relatives'     => array ($t->mask),
1764                                                'operation'     => PHPGW_ACL_EDIT
1765                                        ))
1766                                )
1767                                {
1768                                        return False;
1769                                }
1770                        }
1771                        umask (000);
1772
1773                        /* We can't move directories into themselves */
1774                        if (($this->file_type (array(
1775                                        'string'        => $f->fake_full_path,
1776                                        'relatives'     => array ($f->mask)
1777                                ) == 'Directory'))
1778                                && ereg ("^$f->fake_full_path", $t->fake_full_path)
1779                        )
1780                        {
1781                                if (($t->fake_full_path == $f->fake_full_path) || substr ($t->fake_full_path, strlen ($f->fake_full_path), 1) == '/')
1782                                {
1783                                        return False;
1784                                }
1785                        }
1786
1787                        if ($this->file_exists (array(
1788                                        'string'        => $f->fake_full_path,
1789                                        'relatives'     => array ($f->mask)
1790                                ))
1791                        )
1792                        {
1793                                /* We get the listing now, because it will change after we update the database */
1794                                $ls = $this->ls (array(
1795                                                'string'        => $f->fake_full_path,
1796                                                'relatives'     => array ($f->mask)
1797                                        )
1798                                );
1799
1800                                if ($this->file_exists (array(
1801                                                'string'        => $t->fake_full_path,
1802                                                'relatives'     => array ($t->mask)
1803                                        ))
1804                                )
1805                                {
1806                                        $this->rm (array(
1807                                                        'string'        => $t->fake_full_path,
1808                                                        'relatives'     => array ($t->mask)
1809                                                )
1810                                        );
1811                                }
1812
1813                                $this->correct_attributes (array(
1814                                                'string'        => $t->fake_full_path,
1815                                                'relatives'     => array ($t->mask)
1816                                        )
1817                                );
1818                               
1819                                if ($f->outside && $t->outside)
1820                                {
1821                                        echo 'local';
1822                                        $result = rename ($f->real_full_path, $t->real_full_path);
1823                                }
1824                                else if ($f->outside || $t->outside) //if either file is local, read then write
1825                                {
1826                                        $content = $this->read(array(
1827                                                'string'        => $f->fake_full_path,
1828                                                'noview' => true,
1829                                                'relatives'     => array ($f->mask)
1830                                                )
1831                                        );
1832                                        $result = $this->write(array(
1833                                                'string'        => $t->fake_full_path,
1834                                                'relatives'     => array ($t->mask),
1835                                                'content' => $content
1836                                                )
1837                                        );
1838                                        if ($result)
1839                                        {
1840                                                $result = $this->rm(array(
1841                                                        'string'        => $f->fake_full_path,
1842                                                        'relatives'     => array ($f->mask),
1843                                                        'content' => $content
1844                                                        )
1845                                                );
1846                                        }
1847                                }
1848                                else {  //we can do a server-side copy if both files are on the server
1849                                        $status=$this->dav_client->move($f->real_full_path, $t->real_full_url,True, 'infinity', $this->override_locks[$p->real_full_path]);
1850                                $result = ($status==201 || $status==204);
1851                                }
1852                               
1853                                if ($result) $this->set_attributes(array(
1854                                                'string'        => $t->fake_full_path,
1855                                                'relatives'     => array ($t->mask),
1856                                                'attributes'    => array (
1857                                                                        'modifiedby_id' => $account_id,
1858                                                                        'modified' => $this->now
1859                                                                )));
1860                                return $result;
1861                        }
1862                        else
1863                        {
1864                                return False;
1865                        }
1866
1867                        $this->add_journal (array(
1868                                        'string'        => $t->fake_full_path,
1869                                        'relatives'     => array ($t->mask),
1870                                        'operation'     => VFS_OPERATION_MOVED,
1871                                        'state_one'     => $f->fake_full_path,
1872                                        'state_two'     => $t->fake_full_path
1873                                )
1874                        );
1875
1876                        return True;
1877                }
1878
1879                /*!
1880                @function move
1881                @abstract shortcut to mv
1882                */
1883                function move ($data)
1884                {
1885                        return $this->mv ($data);
1886                }
1887
1888                /*!
1889                @function rm
1890                @abstract delete file/directory
1891                @param string file/directory to delete
1892                @param relatives Relativity array
1893                @result boolean True/False
1894                */
1895                function rm ($data)
1896                {
1897                        $default_values = array
1898                                (
1899                                        'relatives'     => array (RELATIVE_CURRENT)
1900                                );
1901
1902                        $data = array_merge ($this->default_values ($data, $default_values), $data);
1903                        $p = $this->path_parts (array(
1904                                        'string'        => $data['string'],
1905                                        'relatives'     => array ($data['relatives'][0])
1906                                )
1907                        );
1908                        $this->debug("rm: $p->real_full_path");
1909                        if (!$this->acl_check (array(
1910                                        'string'        => $p->fake_full_path,
1911                                        'relatives'     => array ($p->mask),
1912                                        'operation'     => PHPGW_ACL_DELETE
1913                                ))
1914                        )
1915                        {
1916                                return False;
1917                        }
1918
1919/*this would become apparent soon enough anyway?
1920                        if (!$this->file_exists (array(
1921                                        'string'        => $data['string'],
1922                                        'relatives'     => array ($data['relatives'][0])
1923                                ))
1924                        ) return False;
1925*/
1926                        if ($this->file_type (array(
1927                                        'string'        => $data['string'],
1928                                        'relatives'     => array ($data['relatives'][0])
1929                                )) != 'Directory'
1930                        )
1931                        {
1932                                if ($p->outside)
1933                                {
1934                                        return unlink($p->real_full_path);
1935                                }
1936                                else
1937                                {
1938                                        $rr=$this->dav_client->delete($p->real_full_path, 0, $this->override_locks[$p->real_full_path]);
1939                                        return $rr == 204;     
1940                                }
1941                        }
1942                        else
1943                        {
1944                                $ls = $this->ls (array(
1945                                                'string'        => $p->fake_full_path,
1946                                                'relatives'     => array ($p->mask)
1947                                        )
1948                                );
1949
1950                                while (list ($num, $entry) = each ($ls))
1951                                {
1952                                        $this->rm (array(
1953                                                        'string'        => "$entry[directory]/$entry[name]",
1954                                                        'relatives'     => array ($p->mask)
1955                                                )
1956                                        );
1957                                }
1958
1959                                /* If the directory is linked, we delete the placeholder directory */
1960                                $ls_array = $this->ls (array(
1961                                                'string'        => $p->fake_full_path,
1962                                                'relatives'     => array ($p->mask),
1963                                                'checksubdirs'  => False,
1964                                                'mime_type'     => False,
1965                                                'nofiles'       => True
1966                                        )
1967                                );
1968                                $link_info = $ls_array[0];
1969
1970                                if ($link_info['link_directory'] && $link_info['link_name'])
1971                                {
1972                                        $path = $this->path_parts (array(
1973                                                        'string'        => $link_info['directory'] . '/' . $link_info['name'],
1974                                                        'relatives'     => array ($p->mask),
1975                                                        'nolinks'       => True
1976                                                )
1977                                        );
1978                                        $this->dav_client->delete($path->real_full_path,0, $this->override_locks[$p->real_full_path]);
1979                                }
1980
1981                                /* Last, we delete the directory itself */
1982                                $this->add_journal (array(
1983                                                'string'        => $p->fake_full_path,
1984                                                'relatives'     => array ($p->mask),
1985                                                'operaton'      => VFS_OPERATION_DELETED
1986                                        )
1987                                );
1988
1989                                $query = $GLOBALS['phpgw']->db->query ("DELETE FROM phpgw_vfs WHERE directory='$p->fake_leading_dirs_clean' AND name='$p->fake_name_clean'" . $this->extra_sql (array ('query_type' => VFS_SQL_DELETE)), __LINE__, __FILE__);
1990
1991                                //rmdir ($p->real_full_path);
1992                                $this->dav_client->delete($p->real_full_path.'/','Infinity', $this->override_locks[$p->real_full_path]);
1993
1994                                return True;
1995                        }
1996                }
1997
1998                /*!
1999                @function delete
2000                @abstract shortcut to rm
2001                */
2002                function delete ($data)
2003                {
2004                        return $this->rm ($data);
2005                }
2006
2007                /*!
2008                @function mkdir
2009                @abstract make a new directory
2010                @param string Directory name
2011                @param relatives Relativity array
2012                @result boolean True on success
2013                */
2014                function mkdir ($data)
2015                {
2016                        if (!is_array ($data))
2017                        {
2018                                $data = array ();
2019                        }
2020
2021                        $default_values = array
2022                                (
2023                                        'relatives'     => array (RELATIVE_CURRENT)
2024                                );
2025
2026                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2027
2028                        $account_id = $GLOBALS['phpgw_info']['user']['account_id'];
2029                        $currentapp = $GLOBALS['phpgw_info']['flags']['currentapp'];
2030
2031                        $p = $this->path_parts (array(
2032                                        'string'        => $data['string'],
2033                                        'relatives'     => array ($data['relatives'][0])
2034                                )
2035                        );
2036
2037                        if (!$this->acl_check (array(
2038                                        'string'        => $p->fake_full_path,
2039                                        'relatives'     => array ($p->mask),
2040                                        'operation'     => PHPGW_ACL_ADD)
2041                                )
2042                        )
2043                        {
2044                                return False;
2045                        }
2046
2047                        /* We don't allow /'s in dir names, of course */
2048                        if (ereg ('/', $p->fake_name))
2049                        {
2050                                return False;
2051                        }
2052                        if ($p->outside)
2053                        {
2054                                if (!mkdir($p->real_full_path, 0777))
2055                                {
2056                                        return False;
2057                                }
2058                        }
2059                        else if($this->dav_client->mkcol($p->real_full_path, $this->override_locks[$p->real_full_path]) != 201)
2060                        {
2061                                return False;
2062                        }
2063                       
2064
2065                        if (!$this->file_exists (array(
2066                                        'string'        => $p->fake_full_path.'/'
2067                                ))
2068                        )
2069                        {
2070                                /*Now we need to set access control for this dir.  Simply create an .htaccess
2071                                file limiting access to this user, if we are creating this dir in the user's home dir*/
2072                                $homedir = $this->fakebase.'/'.$this->dav_user;
2073                                if ( substr($p->fake_leading_dirs, 0, strlen($homedir)) == $homedir)
2074                                {
2075                                        $conf = CreateObject('phpgwapi.config', 'phpgwapi');
2076                                        $conf->read_repository();
2077                                        if (!$conf->config_data['acl_default'] == 'grant')
2078                                        {
2079                                                $htaccess = 'require user '.$GLOBALS['phpgw_info']['user']['account_lid'];
2080                                                if ( ! $this->write(array(
2081                                                                'string' =>  $p->fake_full_path.'/.htaccess',
2082                                                                'content' => $htaccess,
2083                                                                'relatives' => array(RELATIVE_NONE)
2084                                                        )))
2085                                                {
2086                                                        echo '<p><b>Unable to write .htaccess file</b></p></b>';
2087                                                };     
2088                                        }
2089                                }
2090                                return True;
2091                        }
2092                        else
2093                        {
2094                                return False;
2095                        }
2096                }
2097
2098                /*!
2099                @function make_link
2100                @abstract Make a link from virtual directory 'vdir' to real directory 'rdir'
2101                @discussion Making a link from 'vdir' to 'rdir' will cause path_parts () to substitute 'rdir' for the real
2102                                path variables when presented with 'vdir'
2103                @param vdir Virtual dir to make link from
2104                @param rdir Real dir to make link to
2105                @param relatives Relativity array
2106                @result Boolean True/False
2107                */
2108                function make_link ($data)
2109                {
2110                        return False; //This code certainly wont work anymore.  Does anything use it?
2111                /*
2112                        $default_values = array
2113                                (
2114                                        'relatives'     => array (RELATIVE_CURRENT, RELATIVE_CURRENT)
2115                                );
2116
2117                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2118
2119                        $account_id = $GLOBALS['phpgw_info']['user']['account_id'];
2120                        $currentapp = $GLOBALS['phpgw_info']['flags']['currentapp'];
2121
2122                        $vp = $this->path_parts (array(
2123                                        'string'        => $data['vdir'],
2124                                        'relatives'     => array ($data['relatives'][0])
2125                                )
2126                        );
2127
2128                        $rp = $this->path_parts (array(
2129                                        'string'        => $data['rdir'],
2130                                        'relatives'     => array ($data['relatives'][1])
2131                                )
2132                        );
2133
2134                        if (!$this->acl_check (array(
2135                                        'string'        => $vp->fake_full_path,
2136                                        'relatives'     => array ($vp->mask),
2137                                        'operation'     => PHPGW_ACL_ADD
2138                                ))
2139                        ) return False;
2140
2141                        if ((!$this->file_exists (array(
2142                                        'string'        => $rp->real_full_path,
2143                                        'relatives'     => array ($rp->mask)
2144                                )))
2145                                && !mkdir ($rp->real_full_path, 0770)) return False;
2146
2147                        if (!$this->mkdir (array(
2148                                        'string'        => $vp->fake_full_path,
2149                                        'relatives'     => array ($vp->mask)
2150                                ))
2151                        )return False;
2152
2153                        $size = $this->get_size (array(
2154                                        'string'        => $rp->real_full_path,
2155                                        'relatives'     => array ($rp->mask)
2156                                )
2157                        );
2158
2159                        $this->set_attributes(array(
2160                                        'string'        => $vp->fake_full_path,
2161                                        'relatives'     => array ($vp->mask),
2162                                        'attributes'    => array (
2163                                                                'link_directory' => $rp->real_leading_dirs,
2164                                                                'link_name' => $rp->real_name,
2165                                                                'size' => $size
2166                                                        )
2167                                )
2168                        );
2169
2170                        $this->correct_attributes (array(
2171                                        'string'        => $vp->fake_full_path,
2172                                        'relatives'     => array ($vp->mask)
2173                                )
2174                        );
2175
2176                        return True;
2177        */
2178                }
2179
2180                /*!
2181                @function set_attributes
2182                @abstract Update database entry for 'string' with the attributes in 'attributes'
2183                @param string file/directory to update
2184                @param relatives Relativity array
2185                @param attributes keyed array of attributes.  key is attribute name, value is attribute value
2186                @result Boolean True/False
2187                @discussion Valid attributes are:
2188                                owner_id
2189                                createdby_id
2190                                modifiedby_id
2191                                created
2192                                modified
2193                                size
2194                                mime_type
2195                                deleteable
2196                                comment
2197                                app
2198                                link_directory
2199                                link_name
2200                                version
2201                                name
2202                                directory
2203                */
2204                function set_attributes ($data,$operation=PHPGW_ACL_EDIT)
2205                {
2206                        /*To get much benefit out of DAV properties we should use
2207                        some sensible XML namespace.  We will use the Dublin Core
2208                        metadata specification (http://dublincore.org/) here where
2209                        we can*/
2210                        $p = $this->path_parts (array(
2211                                'string'        => $data['string'],
2212                                'relatives'     => array ($data['relatives'][0])
2213                                ));
2214                        $dav_properties = array();
2215                        $lid=''; $fname = ''; $lname='';
2216                        if ($data['attributes']['comment'])
2217                        {
2218                                $dav_properties['dc:description'] = $data['attributes']['comment'];
2219                        }
2220                        if ($id=$data['attributes']['owner_id'])
2221                        {
2222                                $GLOBALS['phpgw']->accounts->get_account_name($id,&$lid,&$fname,&$lname);
2223                                $dav_properties['dc:publisher'] = $fname .' '. $lname;
2224                                $dav_properties['publisher_id'] = $id;
2225                        }
2226                        if ($id=$data['attributes']['createdby_id'])
2227                        {
2228                                $GLOBALS['phpgw']->accounts->get_account_name($id,&$lid,&$fname,&$lname);
2229                                $dav_properties['dc:creator'] = $fname .' '. $lname;
2230                                $dav_properties['creator_id'] = $id;
2231                        }
2232                        if ($id=$data['attributes']['modifiedby_id'])
2233                        {
2234                                $GLOBALS['phpgw']->accounts->get_account_name($id,&$lid,&$fname,&$lname);
2235                                $dav_properties['dc:contributor'] = $fname .' '. $lname;
2236                                $dav_properties['contributor_id'] = $id;
2237                        }
2238
2239                        $xmlns = 'xmlns:dc="http://purl.org/dc/elements/1.0/"';
2240                        $this->dav_client->proppatch($p->real_full_path, $dav_properties, $xmlns, $this->override_locks[$p->real_full_path]);
2241                        return True;
2242                }
2243
2244                /*!
2245                @function correct_attributes
2246                @abstract Set the correct attributes for 'string' (e.g. owner)
2247                @param string File/directory to correct attributes of
2248                @param relatives Relativity array
2249                @result Boolean True/False
2250                */
2251                function correct_attributes ($data)
2252                {
2253                        $default_values = array
2254                                (
2255                                        'relatives'     => array (RELATIVE_CURRENT)
2256                                );
2257
2258                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2259$this->debug('correct_attributes: '.$data['string']);
2260                        $p = $this->path_parts (array(
2261                                        'string'        => $data['string'],
2262                                        'relatives'     => array ($data['relatives'][0])
2263                                )
2264                        );
2265
2266                        if ($p->fake_leading_dirs != $this->fakebase && $p->fake_leading_dirs != '/')
2267                        {
2268                                $ls_array = $this->ls (array(
2269                                                'string'        => $p->fake_leading_dirs,
2270                                                'relatives'     => array ($p->mask),
2271                                                'checksubdirs'  => False,
2272                                                'nofiles'       => True
2273                                        )
2274                                );
2275                                $set_attributes_array = Array(
2276                                        'owner_id' => $ls_array[0]['owner_id']
2277                                );
2278                        }
2279                        elseif (preg_match ("+^$this->fakebase\/(.*)$+U", $p->fake_full_path, $matches))
2280                        {
2281                                $set_attributes_array = Array(
2282                                        'owner_id' => $GLOBALS['phpgw']->accounts->name2id ($matches[1])
2283                                );
2284                        }
2285                        else
2286                        {
2287                                $set_attributes_array = Array(
2288                                        'owner_id' => 0
2289                                );
2290                        }
2291
2292                        $this->set_attributes (array(
2293                                        'string'        => $p->fake_full_name,
2294                                        'relatives'     => array ($p->mask),
2295                                        'attributes'    => $set_attributes_array
2296                                )
2297                        );
2298
2299                        return True;
2300                }
2301
2302                /*!
2303                @function file_type
2304                @abstract return file/dir type (MIME or other)
2305                @param string File or directory path (/home/user/dir/dir2/dir3, /home/user/dir/dir2/file)
2306                @param relatives Relativity array
2307                @result MIME type, "Directory", or nothing if MIME type is not known
2308                */
2309                function file_type ($data)
2310                {
2311$this->debug('file_type');
2312                        $default_values = array
2313                                (
2314                                        'relatives'     => array (RELATIVE_CURRENT)
2315                                );
2316
2317                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2318
2319                        $p = $this->path_parts (array(
2320                                        'string'        => $data['string'],
2321                                        'relatives'     => array ($data['relatives'][0])
2322                                )
2323                        );
2324
2325                        if (!$this->acl_check (array(
2326                                        'string'        => $p->fake_full_path,
2327                                        'relatives'     => array ($p->mask),
2328                                        'operation'     => PHPGW_ACL_READ,
2329                                        'must_exist'    => True
2330                                ))
2331                        ) return False;
2332
2333                        if ($p->outside)
2334                        {
2335                          if(is_dir($p->real_full_path)) return ('Directory');
2336                          else return $this->get_ext_mime_type(array('string' => $p->real_full_path));
2337
2338                        }
2339                        $tmp_prop=$this->dav_client->get_properties($p->real_full_path);
2340$this->debug('tmpprop: '.$p->real_full_path);
2341$this->debug($tmp_prop);
2342                        $mime_type=$tmp_prop[$p->real_full_path]['mime_type'];
2343                        if ($mime_type == 'httpd/unix-directory' || $tmp_prop[$p->real_full_path]['is_dir']== '1')
2344                        {
2345                                $mime_type='Directory';
2346                        }
2347$this->debug('file_type: Mime type : '.$mime_type);
2348                        return $mime_type;
2349                }
2350
2351                /*!
2352                @function get_ext_mime_type
2353                @abstract return MIME type based on file extension
2354                @description Authors: skeeter
2355                             Internal use only.  Applications should call vfs->file_type ()
2356                @param string File name, with or without leading paths
2357                @result MIME type based on file extension
2358                */
2359                function get_ext_mime_type ($data)
2360                {
2361                        $file=basename($data['string']);
2362                        $mimefile=PHPGW_API_INC.'/phpgw_mime.types';
2363                        $fp=fopen($mimefile,'r');
2364                        $contents = explode("\n",fread($fp,filesize($mimefile)));
2365                        fclose($fp);
2366
2367                        $parts=explode('.',strtolower($file));
2368                        $ext=$parts[(sizeof($parts)-1)];
2369
2370                        for($i=0;$i<sizeof($contents);$i++)
2371                        {
2372                                if (!ereg("^#",$contents[$i]))
2373                                {
2374                                        $line=split("[[:space:]]+", $contents[$i]);
2375                                        if (sizeof($line) >= 2)
2376                                        {
2377                                                for($j=1;$j<sizeof($line);$j++)
2378                                                {
2379                                                        if($line[$j] == $ext)
2380                                                        {
2381                                                                return $line[0];
2382                                                        }
2383                                                }
2384                                        }
2385                                }
2386                        }
2387
2388                        return '';
2389                }
2390               
2391
2392 
2393                /*!
2394                @function file_exists
2395                @abstract check if file/directory exists
2396                @param string file/directory to check existance of
2397                @param relatives Relativity array
2398                @result Boolean True/False
2399                */
2400                function file_exists ($data)
2401                {
2402                        $default_values = array
2403                                (
2404                                        'relatives'     => array (RELATIVE_CURRENT)
2405                                );
2406
2407                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2408
2409                        $p = $this->path_parts (array(
2410                                        'string'        => $data['string'],
2411                                        'relatives'     => array ($data['relatives'][0])
2412                                )
2413                        );
2414$this->debug('vfs->file_exists() data:'.$data['string']);
2415$this->debug('vfs->file_exists() full_path:  '.$p->real_full_path);
2416                        if ($p->outside)
2417                        {
2418                          return file_exists($p->real_full_path);
2419                        }
2420                       
2421                        $path = $p->real_full_path;
2422                       
2423                        //Even though this does full XML parsing on the output, because
2424                        // it then caches the result this limits the amount of traffic to
2425                        //the dav server (which makes it faster even over a local connection)
2426                        $props = $this->dav_client->get_properties($path);
2427                        if ($props[$path])
2428                        {
2429                                $this->debug('found');
2430                                return True;
2431                        }
2432                        else
2433                        {
2434                                $this->debug('not found');
2435                                return False;
2436                        }
2437                }
2438
2439                /*!
2440                @function get_size
2441                @abstract Return size of 'string'
2442                @param string file/directory to get size of
2443                @param relatives Relativity array
2444                @param checksubdirs Boolean, recursively add the size of all sub directories as well?
2445                @result Size of 'string' in bytes
2446                */
2447                function get_size ($data)
2448                {
2449                        if (!is_array ($data))
2450                        {
2451                                $data = array ();
2452                        }
2453
2454                        $default_values = array
2455                                (
2456                                        'relatives'     => array (RELATIVE_CURRENT),
2457                                        'checksubdirs'  => True
2458                                );
2459
2460                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2461
2462                        $p = $this->path_parts (array(
2463                                        'string'        => $data['string'],
2464                                        'relatives'     => array ($data['relatives'][0])
2465                                )
2466                        );
2467
2468                        if (!$this->acl_check (array(
2469                                        'string'        => $p->fake_full_path,
2470                                        'relatives'     => array ($p->mask),
2471                                        'operation'     => PHPGW_ACL_READ,
2472                                        'must_exist'    => True
2473                                ))
2474                        )
2475                        {
2476                                return False;
2477                        }
2478
2479                        /*
2480                           WIP - this should run through all of the subfiles/directories in the directory and tally up
2481                           their sizes.  Should modify ls () to be able to return a list for files outside the virtual root
2482                        */
2483                        if ($p->outside){
2484                          return filesize($p->real_full_path);
2485                        }
2486
2487                        $ls_array = $this->ls (array(
2488                                        'string'        => $p->fake_full_path,
2489                                        'relatives'     => array ($p->mask),
2490                                        'checksubdirs'  => $data['checksubdirs'],
2491                                        'nofiles'       => !$data['checksubdirs']
2492                                )
2493                        );
2494
2495                        while (list ($num, $file_array) = each ($ls_array))
2496                        {
2497                                /*
2498                                   Make sure the file is in the directory we want, and not
2499                                   some deeper nested directory with a similar name
2500                                */
2501/*
2502                                if (@!ereg ('^' . $file_array['directory'], $p->fake_full_path))
2503                                {
2504                                        continue;
2505                                }
2506*/
2507
2508                                $size += $file_array['size'];
2509$this->debug('size:getting size from fs: '.$size);
2510                        }
2511
2512                        return $size;
2513                }
2514
2515                /*!
2516                @function checkperms
2517                @abstract Check if $this->working_id has write access to create files in $dir
2518                @discussion Simple call to acl_check
2519                @param string Directory to check access of
2520                @param relatives Relativity array
2521                @result Boolean True/False
2522                */
2523                function checkperms ($data)
2524                {
2525                        if (!is_array ($data))
2526                        {
2527                                $data = array ();
2528                        }
2529
2530                        $default_values = array
2531                                (
2532                                        'relatives'     => array (RELATIVE_CURRENT)
2533                                );
2534
2535                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2536
2537                        $p = $this->path_parts (array(
2538                                        'string'        => $data['string'],
2539                                        'relatives'     => array ($data['relatives'][0])
2540                                )
2541                        );
2542
2543                        if (!$this->acl_check (array(
2544                                        'string'        => $p->fake_full_path,
2545                                        'relatives'     => array ($p->mask),
2546                                        'operation'     => PHPGW_ACL_ADD
2547                                ))
2548                        )
2549                        {
2550                                return False;
2551                        }
2552                        else
2553                        {
2554                                return True;
2555                        }
2556                }
2557
2558                /*!
2559                @function ls
2560                @abstract get directory listing or info about a single file
2561                @discussion Note: The entries are not guaranteed to be returned in any logical order
2562                            Note: The size for directories does not include subfiles/subdirectories.
2563                                  If you need that, use $this->get_size ()
2564                @param string File or Directory
2565                @param relatives Relativity array
2566                @param checksubdirs Boolean, recursively list all sub directories as well?
2567                @param mime_type Only return entries matching MIME-type 'mime_type'.  Can be any MIME-type, "Directory" or "\ " for those without MIME types
2568                @param nofiles Boolean.  True means you want to return just the information about the directory $dir.  If $dir is a file, $nofiles is implied.  This is the equivalent of 'ls -ld $dir'
2569                @param orderby How to order results.  Note that this only works for directories inside the virtual root
2570                @result array of arrays.  Subarrays contain full info for each file/dir.
2571                */
2572                function ls ($data)
2573                {
2574                        $default_values = array
2575                                (
2576                                        'relatives'     => array (RELATIVE_CURRENT),
2577                                        'checksubdirs'  => True,
2578                                        'mime_type'     => False,
2579                                        'nofiles'       => False,
2580                                        'orderby'       => 'directory'
2581                                );
2582                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2583                        //Stupid "nofiles" fix"
2584                        if ($data['nofiles'])
2585                        {
2586                                $data['relatives'] = array (RELATIVE_NONE);
2587                        }
2588                        $p = $this->path_parts (array(
2589                                        'string'        => $data['string'],
2590                                        'relatives'     => array ($data['relatives'][0])
2591                                )
2592                        );
2593               
2594                        if ($data['checksubdirs']==False && ereg('.*/$', $data['string']) && $data['nofiles'] )
2595                        {
2596$this->debug('Returning empty for'.$data['string']);
2597                                return array();
2598                        }
2599                        $dir = $p->fake_full_path;
2600$this->debug("ls'ing dir: $dir path: ".$p->real_full_path);
2601                        /* If they pass us a file or 'nofiles' is set, return the info for $dir only */
2602                        if (((($type = $this->file_type (array(
2603                                        'string'        => $dir,
2604                                        'relatives'     => array ($p->mask)
2605                                )) != 'Directory'))
2606                                || ($data['nofiles'])) && !$p->outside
2607                        )
2608                        {
2609$this->debug('ls branch 1');
2610                        $prop=$this->dav_client->get_properties($p->real_full_path, 1);
2611                        //make the key the 'orderby' attribute
2612                        if (! ($data['orderby'] == 'directory'))
2613                        {
2614                                $tmp_prop = array();
2615                                $id=0;
2616                                foreach ( $prop as $key=>$value)
2617                                {
2618                                        $id++;
2619                                        $new_key =  substr($value[$data['orderby']].'        ',0, 8);
2620                                        $tmp_prop[strtolower($new_key).'_'.$id] = $value;
2621                                }
2622                        }
2623                        else
2624                        {
2625                                $tmp_prop = $prop;
2626                        }
2627                        ksort($tmp_prop);
2628                        $rarray = array ();
2629                        foreach($tmp_prop as $idx => $value)
2630                        {
2631                                if($value['mime_type']==$data['mime_type'] or $data['mime_type']=='')
2632                                {
2633                                        $directory = $this->path_parts($value['directory']);
2634                                        $value['directory'] = $directory->fake_full_path;
2635                                        if($value['is_dir']) $value['mime_type']='Directory';
2636                                        $rarray[] = $value;
2637                                }
2638                        }
2639$this->debug('ls returning 1:');
2640                                return $rarray;
2641                        }
2642
2643                        //WIP - this should recurse using the same options the virtual part of ls () does
2644                        /* If $dir is outside the virutal root, we have to check the file system manually */
2645                        if ($p->outside)
2646                        {
2647$this->debug('ls branch 2 (outside)');
2648                                if ($this->file_type (array(
2649                                                'string'        => $p->fake_full_path,
2650                                                'relatives'     => array ($p->mask)
2651                                        )) == 'Directory'
2652                                        && !$data['nofiles']
2653                                )
2654                                {
2655                                        $dir_handle = opendir ($p->real_full_path);
2656                                        while ($filename = readdir ($dir_handle))
2657                                        {
2658                                                if ($filename == '.' || $filename == '..')
2659                                                {
2660                                                        continue;
2661                                                }
2662
2663                                                $rarray[] = $this->get_real_info (array(
2664                                                                'string'        => $p->real_full_path . SEP . $filename,
2665                                                                'relatives'     => array ($p->mask)
2666                                                        )
2667                                                );
2668                                        }
2669                                }
2670                                else
2671                                {
2672                                        $rarray[] = $this->get_real_info (array(
2673                                                        'string'        => $p->real_full_path,
2674                                                        'relatives'     => array ($p->mask)
2675                                                )
2676                                        );
2677                                }
2678$this->debug('ls returning 2:');
2679                                return $rarray;
2680                        }
2681$this->debug('ls branch 3');
2682                        /* $dir's not a file, is inside the virtual root, and they want to check subdirs */
2683                        $prop=$this->dav_client->get_properties($p->real_full_path,1);
2684                        unset($prop[$p->real_full_path]);
2685                        //make the key the 'orderby' attribute
2686
2687                        if (! ($data['orderby'] == 'directory'))
2688                        {
2689                                $tmp_prop = array();
2690                                $id=0;
2691                                foreach ( $prop as $key=>$value)
2692                                {
2693                                        $id++;
2694                                        $new_key =  substr($value[$data['orderby']].'        ',0, 8);
2695                                        $tmp_prop[strtolower($new_key).'_'.$id] = $value;
2696                                }
2697                        }
2698                        else
2699                        {
2700                                $tmp_prop = $prop;
2701                        }
2702                       
2703                        ksort($tmp_prop);
2704
2705                        unset($tmp_prop[ $p->real_full_path]);
2706                        $rarray = array ();
2707                        foreach($tmp_prop as $idx => $value)
2708                        {       
2709                                if($data['mime_type']=='' || $value['mime_type']==$data['mime_type'])
2710                                {
2711                                        //$directory = $this->path_parts($value['directory']);
2712                                        $value['directory'] = $p->fake_full_path;
2713                                        $rarray[] = $value;
2714                                }
2715                        }               
2716$this->debug('ls:returning 3:');
2717                        return $rarray;
2718                }
2719
2720                /*!
2721                @function dir
2722                @abstract shortcut to ls
2723                */
2724                function dir ($data)
2725                {
2726                        return $this->ls ($data);
2727                }
2728
2729                /*!
2730                @function command_line
2731                @abstract Process and run a Unix-sytle command line
2732                @discussion EXPERIMENTAL.  DANGEROUS.  DO NOT USE THIS UNLESS YOU KNOW WHAT YOU'RE DOING!
2733                            This is mostly working, but the command parser needs to be improved to take
2734                            files with spaces into consideration (those should be in "").
2735                @param command_line Unix-style command line with one of the commands in the $args array
2736                @result $result The return value of the actual VFS call
2737                */
2738                function command_line ($data)
2739                {
2740                        if (!is_array ($data))
2741                        {
2742                                $data = array ();
2743                        }
2744
2745                        $args = array
2746                        (
2747                                array ('name'   => 'mv', 'params'       => 2),
2748                                array ('name'   => 'cp', 'params'       => 2),
2749                                array ('name'   => 'rm', 'params'       => 1),
2750                                array ('name'   => 'ls', 'params'       => -1),
2751                                array ('name'   => 'du', 'params'       => 1, 'func'    => get_size),
2752                                array ('name'   => 'cd', 'params'       => 1),
2753                                array ('name'   => 'pwd', 'params'      => 0),
2754                                array ('name'   => 'cat', 'params'      => 1, 'func'    => read),
2755                                array ('name'   => 'file', 'params'     => 1, 'func'    => file_type),
2756                                array ('name'   => 'mkdir', 'params'    => 1),
2757                                array ('name'   => 'touch', 'params'    => 1)
2758                        );
2759
2760                        if (!$first_space = strpos ($data['command_line'], ' '))
2761                        {
2762                                $first_space = strlen ($data['command_line']);
2763                        }
2764                        if ((!$last_space = strrpos ($data['command_line'], ' ')) || ($last_space == $first_space))
2765                        {
2766                                $last_space = strlen ($data['command_line']) + 1;
2767                        }
2768                        $argv[0] = substr ($data['command_line'], 0, $first_space);
2769                        if (strlen ($argv[0]) != strlen ($data['command_line']))
2770                        {
2771                                $argv[1] = substr ($data['command_line'], $first_space + 1, $last_space - ($first_space + 1));
2772                                if ((strlen ($argv[0]) + 1 + strlen ($argv[1])) != strlen ($data['command_line']))
2773                                {
2774                                        $argv[2] = substr ($data['command_line'], $last_space + 1);
2775                                }
2776                        }
2777                        $argc = count ($argv);
2778
2779                        reset ($args);
2780                        while (list (,$arg_info) = each ($args))
2781                        {
2782                                if ($arg_info['name'] == $argv[0])
2783                                {
2784                                        $command_ok = 1;
2785                                        if (($argc == ($arg_info['params'] + 1)) || ($arg_info['params'] == -1))
2786                                        {
2787                                                $param_count_ok = 1;
2788                                        }
2789                                        break;
2790                                }
2791                        }
2792
2793                        if (!$command_ok)
2794                        {
2795//                              return E_VFS_BAD_COMMAND;
2796                                return False;
2797                        }
2798                        if (!$param_count_ok)
2799                        {
2800//                              return E_VFS_BAD_PARAM_COUNT;
2801                                return False;
2802                        }
2803
2804                        for ($i = 1; $i != ($arg_info['params'] + 1); $i++)
2805                        {
2806                                if (substr ($argv[$i], 0, 1) == '/')
2807                                {
2808                                        $relatives[] = RELATIVE_NONE;
2809                                }
2810                                else
2811                                {
2812                                        $relatives[] = RELATIVE_ALL;
2813                                }
2814                        }
2815
2816                        $func = $arg_info['func'] ? $arg_info['func'] : $arg_info['name'];
2817
2818                        if (!$argv[2])
2819                        {
2820                                $rv = $this->$func (array(
2821                                                'string'        => $argv[1],
2822                                                'relatives'     => $relatives
2823                                        )
2824                                );
2825                        }
2826                        else
2827                        {
2828                                $rv = $this->$func (array(
2829                                                'from'  => $argv[1],
2830                                                'to'    => $argv[2],
2831                                                'relatives'     => $relatives
2832                                        )
2833                                );
2834                        }
2835
2836                        return ($rv);
2837                }
2838
2839                /* Helper functions */
2840
2841                function default_values ($data, $default_values)
2842                {
2843                  if(!is_array($data)) $data=array();
2844                        for ($i = 0; list ($key, $value) = each ($default_values); $i++)
2845                        {
2846                                if (!isset ($data[$key]))
2847                                {
2848                                        $data[$key] = $value;
2849                                }
2850                        }
2851
2852                        return $data;
2853                }
2854
2855                /* Since we are always dealing with real info, this just calls ls */
2856                function get_real_info ($data){
2857                        if (!is_array ($data))
2858                        {
2859                                $data = array ();
2860                        }
2861
2862                        $default_values = array
2863                                (
2864                                        'relatives'     => array (RELATIVE_CURRENT)
2865                                );
2866
2867                        $data = array_merge ($this->default_values ($data, $default_values), $data);
2868
2869                        $p = $this->path_parts (array(
2870                                        'string'        => $data['string'],
2871                                        'relatives'     => array ($data['relatives'][0])
2872                                )
2873                        );
2874
2875                        if ( $this->file_type(array('string' => $p->real_full_path, 'relatives' => array (RELATIVE_ROOT))) == 'Directory')
2876                        {
2877                                $mime_type = 'Directory';
2878                        }
2879                        else
2880                        {
2881                                $mime_type = $this->get_ext_mime_type (array(
2882                                                'string'        => $p->fake_name
2883                                        )
2884                                );
2885
2886                        }
2887
2888                        $size = filesize ($p->real_full_path);
2889                        $rarray = array(
2890                                'directory' => $p->fake_leading_dirs,
2891                                'name' => $p->fake_name,
2892                                'size' => $size,
2893                                'mime_type' => $mime_type
2894                        );
2895
2896                        return ($rarray);
2897                }
2898               
2899                function update_real()
2900                { //hmmm. things break without this, but it does nothing in this implementation
2901                        return True;
2902                }
2903               
2904                function save_session()
2905                {
2906                        //Save the overrided locks in the session
2907                        $app = $GLOBALS['phpgw_info']['flags']['currentapp'];
2908                        $a = array();
2909                        foreach ($this->override_locks as $name => $token)
2910                        {
2911                                $a[] = $name.';'.$token;       
2912                        }       
2913                        $session_data = implode('\n', $a);
2914                        $this->session = $GLOBALS['phpgw']->session->appsession ('vfs_dav',$app, base64_encode($session_data));
2915                               
2916                }       
2917        }
2918?>
Note: See TracBrowser for help on using the repository browser.