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