Simple Groupware PHP Cross Reference Groupware Applications

Source: /src/modules/lib/gdocs.php - 455 lines - 18896 bytes - Summary - Text - Print

   1  <?php
   2      /**************************************************************************\
   3      * Simple Groupware 0.743                                                   *
   4      * http://www.simple-groupware.de                                           *
   5      * Copyright (C) 2002-2012 by Thomas Bley                                   *
   6      * ------------------------------------------------------------------------ *
   7      *  This program is free software; you can redistribute it and/or           *
   8      *  modify it under the terms of the GNU General Public License Version 2   *
   9      *  as published by the Free Software Foundation; only version 2            *
  10      *  of the License, no later version.                                       *
  11      *                                                                          *
  12      *  This program is distributed in the hope that it will be useful,         *
  13      *  but WITHOUT ANY WARRANTY; without even the implied warranty of          *
  14      *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
  15      *  GNU General Public License for more details.                            *
  16      *                                                                          *
  17      *  You should have received a copy of the GNU General Public License       *
  18      *  Version 2 along with this program; if not, write to the Free Software   *
  19      *  Foundation, Inc., 59 Temple Place - Suite 330, Boston,                  *
  20      *  MA  02111-1307, USA.                                                    *
  21      \**************************************************************************/
  22  
  23  class lib_gdocs extends lib_default {
  24  
  25  static function get_dirs($path, $parent, $recursive) {
  26    $tree = array();
  27    self::_get_dirs($path, 1, 0, $parent, $recursive, $tree);
  28    return $tree;
  29  }
  30  
  31  private static function _fix_namespace($content, $tags) {
  32    $content = str_replace("<feed xmlns=", "<feed ns=", $content);
  33    return preg_replace("!(</?|\s)(".implode("|", $tags)."):!", "\\1\\2_", $content);
  34  }
  35  
  36  static function folder_info($path, $mfolder) {
  37    $url = "https://docs.google.com/feeds/metadata/default";
  38    $http_response_header = array();
  39    $response = file_get_contents($url, false, self::_get_context($mfolder));
  40    try {
  41      $xml = new SimpleXMLElement(self::_fix_namespace($response, array("docs", "gd")));
  42    }
  43    catch (Exception $e) {
  44      exit("{t}Error{/t} ".implode("\n", $e->getMessage())."\n".$response." ".$http_response_header[0]);    
  45    }
  46    $result = array();
  47    $result["quota"]["quota"] = (int)$xml->gd_quotaBytesTotal;
  48    $result["quota"]["remain"] = $xml->gd_quotaBytesTotal-$xml->gd_quotaBytesUsed;
  49    if ($path==sys_remove_trans("docs.google.com/{t}Trash{/t}/")) {
  50      $result["fsizecount"] = (int)$xml->docs_quotaBytesUsedInTrash;
  51    }
  52    return $result;
  53  }
  54  
  55  static function count($path,$where,$vars,$mfolder) {
  56    return count(self::_select_xml($path, $mfolder));
  57  }
  58  
  59  private static function _get_docs_path($path, $mfolder) {
  60    $path = explode("/", $path);
  61    $boxes = self::_get_boxes($mfolder);
  62    
  63    array_shift($path);
  64    if ($path[0]==sys_remove_trans("{t}Trash{/t}")) return "-/trashed";
  65    if ($path[0]==sys_remove_trans("{t}Shared{/t}")) return "-/-mine";
  66    
  67    $current = "folder%3Aroot";
  68    foreach ($path as $elem) {
  69      if ($elem=="" or !isset($boxes[$current])) continue;
  70      $key = array_search($elem, $boxes[$current]);
  71      if ($key) $current = $key;
  72    }
  73    return $current;
  74  }
  75  
  76  private static function _select_xml($path, $mfolder) {
  77    $creds = sys_credentials($mfolder);
  78    $cid = "gdocs_xml_".md5(serialize($creds).$path);
  79    if (($entries = sys_cache_get($cid))) return new SimpleXMLElement($entries);
  80  
  81    $url = "https://docs.google.com/feeds/default/private/full/".
  82      self::_get_docs_path($path, $mfolder)."/contents?showfolders=false";
  83    $limit = 0;
  84    while ($url!="" and $limit<10) {
  85      $http_response_header = array();
  86      $content = @file_get_contents($url, false, self::_get_context($mfolder));
  87      try {
  88        if (!strpos($http_response_header[0], "200")) throw new Exception($http_response_header[0]);
  89        $xml = new SimpleXMLElement(self::_fix_namespace($content, array("docs", "gd")));
  90        foreach ($xml->entry as $entry) $entries .= $entry->asXML();
  91        $url = (string)@array_shift($xml->xpath("/feed/link[@rel='next']/@href"));
  92      } catch (Exception $e) {
  93        sys_warning("{t}Error{/t} [select_xml] ".$e->getMessage());
  94        return array();
  95      }
  96      $limit++;
  97    }
  98    sys_cache_set($cid, "<e>".$entries."</e>", GDOCS_LIST_CACHE);
  99    return new SimpleXMLElement("<e>".$entries."</e>");
 100  }
 101  
 102  static function select($path,$fields,$where,$order,$limit,$vars,$mfolder) {
 103    if ($fields==array("*")) $fields = array("id", "folder");
 104    $rows = array();
 105    $entries = self::_select_xml($path, $mfolder);
 106    foreach ($entries as $entry) {
 107      $ext = modify::getfileext($entry->title);
 108      $row = array();
 109      foreach ($fields as $field) {
 110        switch ($field) {
 111          case "filedata":
 112          case "id": $row[$field] = basename($entry->id); break;
 113          case "folder": $row[$field] = $path; break;
 114          case "filedata_show":
 115          case "filename":
 116          case "searchcontent": $row[$field] = (string)$entry->title; break;
 117          case "fileext": $row[$field] = $ext; break;
 118          case "created": $row[$field] = strtotime($entry->published); break;
 119          case "lastmodified": $row[$field] = strtotime($entry->updated); break;
 120          case "lastmodifiedby": $row[$field] = (string)$entry->author->name; break;
 121          case "filesize": $row[$field] = (int)$entry->gd_quotaBytesUsed; break;
 122          default: $row[$field] = ""; break;
 123        }
 124      }
 125      $row["_lastmodified"] = strtotime($entry->updated);
 126      $row["_url"] = (string)$entry->content["src"];
 127      $row["_filename"] = (string)$entry->title;
 128  
 129      $meta = sys_build_meta($entry->docs_description,array());
 130      if (empty($meta)) $meta["description"] = (string)$entry->docs_description;
 131      $row = array_merge($row, $meta);
 132      
 133      if (sys_select_where($row,$where,$vars)) $rows[] = $row;
 134    }
 135    
 136    $rows = sys_select($rows,$order,$limit,$fields);
 137    if (count($rows)>0 and in_array("filedata",$fields)) {
 138      foreach ($rows as $key=>$row) {
 139        $filename = sys_cache_get_file("gdocs", $row["id"].$row["_lastmodified"], "--".modify::basename($row["_filename"]), true);
 140        if (!file_exists($filename) and (!isset($row["filesize"]) or $row["filesize"]<GDOCS_PREVIEW_LIMIT)) {
 141          $fout = fopen($filename, "wb");
 142          $fin = fopen($row["_url"], "rb", false, self::_get_context($mfolder));
 143          if (is_resource($fin) and is_resource($fout)) {
 144            while (!feof($fin)) fwrite($fout, fread($fin, 8192));
 145            fclose($fin);
 146            fclose($fout);
 147          }
 148        }
 149        $rows[$key]["filedata"] = $filename;
 150      }
 151    }
 152    return $rows;
 153  }
 154  
 155  static function delete($path,$where,$vars,$mfolder) {
 156    if (empty($vars["id"])) return "error";
 157  
 158    $url = "https://docs.google.com/feeds/default/private/full/".$vars["id"];
 159    if (basename($path)==sys_remove_trans("{t}Trash{/t}")) $url .= "?delete=true";
 160    $context = self::_get_context_action($mfolder, "DELETE");
 161  
 162    $http_response_header = array();
 163    $response = file_get_contents($url, false, $context);
 164    sys_cache_remove("gdocs_xml_".md5(serialize(sys_credentials($mfolder)).$path));
 165    
 166    if (!strpos($http_response_header[0], "200")) {
 167      exit("{t}Error{/t} ".implode("\n", $http_response_header)."\n".$vars["id"]."\n".$response);
 168    }
 169    return "";
 170  }
 171  
 172  static function insert($path,$data,$mfolder) {
 173    $source = $data["filedata"];
 174    if (!is_dir($source) and file_exists($source)) {
 175  
 176      $url = "https://docs.google.com/feeds/upload/create-session/default/private/full/".
 177        self::_get_docs_path($path, $mfolder)."/contents?convert=false";
 178  
 179      $drop = array("filedata", "folder", "created", "lastmodified", "handler", "mfolder", "dsize", "id");
 180      $meta = sys_build_meta_str($data, array_diff(array_keys($data), $drop));
 181  
 182      $content = "<?xml version='1.0' encoding='UTF-8'?>".
 183        "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:docs='http://schemas.google.com/docs/2007'>".
 184        "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/docs/2007#document'/>".
 185        "<title>".modify::htmlquote(modify::basename($source))."</title>".
 186        "<docs:description>".modify::htmlquote($meta)."</docs:description></entry>";
 187  
 188      $header = "X-Upload-Content-Type: application/octet-stream\r\n";
 189      $context = self::_get_context_action($mfolder, "POST", $content, $header);
 190      $http_response_header = array();
 191      $response = file_get_contents($url, false, $context);
 192      
 193      preg_match("/Location: (.+)/m", implode("\n", $http_response_header), $match);
 194  
 195      if (!strpos($http_response_header[0], "200") or empty($match[1])) {
 196        return "{t}Error{/t} [insert] ".implode("\n", $http_response_header)."\n".$response;
 197      }
 198      $header = "POST ".$match[1]." HTTP/1.0\r\n";
 199      $header .= "Host: docs.google.com\r\n";
 200      $header .= "Content-Length: ".filesize($source)."\r\n\r\n";
 201      
 202      $errorNumber = 0;
 203      $errorString = "";
 204      $fp = fsockopen("ssl://docs.google.com", "443", $errorNumber, $errorString, 5);
 205      $fin = fopen($source, "rb");
 206      if (is_resource($fp) and is_resource($fin)) {
 207        fwrite($fp, $header);
 208        while (!feof($fin)) fwrite($fp, fread($fin, 8192));
 209        $resp = "";
 210        while (!feof($fp)) $resp .= fread($fp, 8192);
 211        fclose($fp);
 212        fclose($fin);
 213        if (!sys_strbegins($resp, "HTTP/1.0 201")) return "{t}Error{/t} [insert2] ".$resp;
 214      } else {
 215        return "{t}Error{/t} [insert3] ".$errorString." ".$errorNumber;
 216      }
 217      sys_cache_remove("gdocs_xml_".md5(serialize(sys_credentials($mfolder)).$path));
 218    }
 219    return "";
 220  }
 221  
 222  static function _move_file($file, $source, $target, $mfolder) {
 223    $base = "https://docs.google.com/feeds/default/private/full/";
 224    $source = substr($source,strpos($source,"/")+1);
 225      
 226    $url = $base.self::_get_docs_path($source, $mfolder)."/contents/".$file;
 227    $context = self::_get_context_action($mfolder, "DELETE");
 228  
 229    $http_response_header = array();
 230    $response = file_get_contents($url, false, $context);
 231    sys_cache_remove("gdocs_xml_".md5(serialize(sys_credentials($mfolder)).$source));
 232    
 233    if (!strpos($http_response_header[0], "200")) {
 234      return "{t}Error{/t} [update] ".implode("\n", $http_response_header)."\n".$file."\n".$response;
 235    }
 236    $content = "<?xml version='1.0' encoding='UTF-8'?>".
 237      "<entry xmlns='http://www.w3.org/2005/Atom'>".
 238      "<id>".$base.$file."</id></entry>";
 239      
 240    $url = $base.self::_get_docs_path($target, $mfolder)."/contents";
 241    $context = self::_get_context_action($mfolder, "POST", $content);
 242  
 243    $http_response_header = array();
 244    $response = file_get_contents($url, false, $context);
 245    sys_cache_remove("gdocs_xml_".md5(serialize(sys_credentials($mfolder)).$target));
 246    
 247    if (!strpos($http_response_header[0], "201")) {
 248      return "{t}Error{/t} [update2] ".implode("\n", $http_response_header)."\n".$file."\n".$response;
 249    }
 250    return "";
 251  }
 252  
 253  static function update($path,$data,$where,$vars,$mfolder) {
 254    if (empty($vars["id"])) return "";
 255    if (!empty($data["filedata"])) $source = $data["filedata"]; else $source = $vars["id"];
 256  
 257    if (!empty($vars["folder_source"])) {
 258      return self::_move_file($vars["id"], $vars["folder_source"], $path, $mfolder);
 259    }
 260    
 261    $drop = array("filedata", "folder", "lastmodified", "handler", "mfolder", "dsize");
 262    $meta = sys_build_meta_str($data, array_diff(array_keys($data), $drop));
 263  
 264    $content = "<?xml version='1.0' encoding='UTF-8'?>".
 265      "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:docs='http://schemas.google.com/docs/2007'>".
 266      "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/docs/2007#document'/>".
 267      "<title>".modify::htmlquote(modify::basename($source))."</title>".
 268      "<docs:description>".modify::htmlquote($meta)."</docs:description></entry>";
 269  
 270    if (file_exists($source) and sys_strbegins($source, SIMPLE_CACHE."/upload/")) {
 271      $url = "https://docs.google.com/feeds/upload/create-session/default/private/full/".
 272        $vars["id"]."?convert=false";
 273  
 274      $header = "X-Upload-Content-Type: application/octet-stream\r\n";
 275      $context = self::_get_context_action($mfolder, "PUT", $content, $header);
 276      $http_response_header = array();
 277      $response = file_get_contents($url, false, $context);
 278      
 279      preg_match("/Location: (.+)/m", implode("\n", $http_response_header), $match);
 280  
 281      if (!strpos($http_response_header[0], "200") or empty($match[1])) {
 282        return "{t}Error{/t} [update] ".implode("\n", $http_response_header)."\n".$response;
 283      }
 284  
 285      $header = "PUT ".$match[1]." HTTP/1.0\r\n";
 286      $header .= "Host: docs.google.com\r\n";
 287      $header .= "Content-Length: ".filesize($source)."\r\n\r\n";
 288      
 289      $errorNumber = 0;
 290      $errorString = "";
 291      $fp = fsockopen("ssl://docs.google.com", "443", $errorNumber, $errorString, 5);
 292      $fin = fopen($source, "rb");
 293      if (is_resource($fp) and is_resource($fin)) {
 294        fwrite($fp, $header);
 295        while (!feof($fin)) fwrite($fp, fread($fin, 8192));
 296        $resp = "";
 297        while (!feof($fp)) $resp .= fread($fp, 8192);
 298        fclose($fp);
 299        fclose($fin);
 300        if (!sys_strbegins($resp, "HTTP/1.0 200")) return "{t}Error{/t} [update2] ".$resp;
 301      } else {
 302        return "{t}Error{/t} [update3] ".$errorString." ".$errorNumber;
 303      }
 304  
 305    } else {
 306      $url = "https://docs.google.com/feeds/default/private/full/".$vars["id"]."?convert=false";
 307  
 308      $context = self::_get_context_action($mfolder, "PUT", $content);
 309      $http_response_header = array();
 310      $response = file_get_contents($url, false, $context);
 311    
 312      if (!strpos($http_response_header[0], "200")) {
 313        return "{t}Error{/t} [update4] ".$http_response_header."\n".$response;
 314      }
 315    }
 316    sys_cache_remove("gdocs_xml_".md5(serialize(sys_credentials($mfolder)).$path));
 317    return "";
 318  }
 319  
 320  static function create_folder($title,$parent,$mfolder) {
 321    $url = "https://docs.google.com/feeds/default/private/full/".self::_get_docs_path($parent, $mfolder)."/contents";
 322    $content = "<?xml version='1.0' encoding='UTF-8'?>".
 323      "<entry xmlns='http://www.w3.org/2005/Atom'>".
 324      "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/docs/2007#folder'/>".
 325      "<title>".modify::htmlquote($title)."</title></entry>";
 326  
 327    $context = self::_get_context_action($mfolder, "POST", $content);
 328    $http_response_header = array();
 329    file_get_contents($url, false, $context);
 330  
 331    sys_cache_remove("gdocs_boxes_".md5(serialize(sys_credentials($mfolder))));
 332    if (strpos($http_response_header[0], "201")) {
 333      return "ok";
 334    } else {
 335      exit("{t}Error{/t} ".implode("\n", $http_response_header)." ".$parent);
 336    }
 337    return "";
 338  }
 339  
 340  static function rename_folder($title,$path,$mfolder) {
 341    $url = "https://docs.google.com/feeds/default/private/full/".self::_get_docs_path($path, $mfolder);
 342    $content = "<?xml version='1.0' encoding='UTF-8'?>".
 343      "<entry xmlns='http://www.w3.org/2005/Atom'>".
 344      "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/docs/2007#folder'/>".
 345      "<title>".modify::htmlquote($title)."</title></entry>";
 346  
 347    $context = self::_get_context_action($mfolder, "PUT", $content);
 348    $http_response_header = array();
 349    file_get_contents($url, false, $context);
 350  
 351    sys_cache_remove("gdocs_boxes_".md5(serialize(sys_credentials($mfolder))));
 352    if (!strpos($http_response_header[0], "200")) {
 353      exit("{t}Error{/t} ".implode("\n", $http_response_header)." ".$path);
 354    }
 355    return "ok";
 356  }
 357  
 358  static function delete_folder($path,$mfolder) {
 359    $url = "https://docs.google.com/feeds/default/private/full/".self::_get_docs_path($path, $mfolder);
 360    $http_response_header = array();
 361    file_get_contents($url, false, self::_get_context_action($mfolder, "DELETE"));
 362  
 363    sys_cache_remove("gdocs_boxes_".md5(serialize(sys_credentials($mfolder))));
 364    if (!strpos($http_response_header[0], "200")) {
 365      exit("{t}Error{/t} ".implode("\n", $http_response_header)." ".$path);
 366    }
 367    return "ok";
 368  }
 369  
 370  private static function _get_boxes($mfolder) {
 371    $cid = "gdocs_boxes_".md5(serialize(sys_credentials($mfolder)));
 372    if (($boxes = sys_cache_get($cid))) return $boxes;
 373    $boxes = array();
 374    $limit = 0;
 375    $url = "https://docs.google.com/feeds/default/private/full/-/folder";
 376    while ($url!="" and $limit<10) {
 377      $content = @file_get_contents($url, false, self::_get_context($mfolder));
 378      try {
 379        $xml = new SimpleXMLElement(str_replace("<feed xmlns=", "<feed ns=", $content));
 380        $url = (string)@array_shift($xml->xpath("/feed/link[@rel='next']/@href"));
 381      }
 382      catch (Exception $unused) { return array(); }
 383      foreach ($xml->entry as $entry) {
 384        $parent = "folder%3Aroot";
 385        if (strpos($entry->link[0]["rel"], "#parent")) {
 386          $parent = basename($entry->link[0]["href"]);
 387        }
 388        $id = basename($entry->id);
 389        $boxes[$parent][$id] = str_replace("@", "", $entry->title);
 390      }
 391      $limit++;
 392    }
 393    foreach (array_keys($boxes) as $key) natcasesort($boxes[$key]);
 394    $boxes["folder%3Aroot"]["trash"] = sys_remove_trans("{t}Trash{/t}");
 395    $boxes["folder%3Aroot"]["shared"] = sys_remove_trans("{t}Shared{/t}");
 396    sys_cache_set($cid, $boxes, GDOCS_CACHE);
 397    return $boxes;
 398  }
 399  
 400  private static function _get_dirs($path, $left, $level, $parent, $recursive, &$tree) {
 401    $right = $left+1;
 402    if ($recursive and sys_is_folderstate_open($path,"gdocs",$parent)) {
 403      // TODO optimize
 404      $docs_path = self::_get_docs_path($path, $parent);
 405      $docs_boxes = self::_get_boxes($parent);
 406  
 407      if (!empty($docs_boxes[$docs_path])) {
 408        foreach ($docs_boxes[$docs_path] as $id) {
 409          $right = self::_get_dirs($path.$id."/", $right, $level+1, $parent, true, $tree);
 410        }
 411      }
 412    } else $right = $right+2;
 413  
 414    $title = basename($path);
 415    $icon = "";
 416    if ($level==0) $icon = "sys_nodb_gdocs.png";
 417    $tree[$left] = array("id"=>$path,"lft"=>$left,"rgt"=>$right,"flevel"=>$level,"ftitle"=>$title,"ftype"=>"sys_nodb_gdocs","icon"=>$icon);
 418    return $right+1;
 419  }
 420  
 421  private static function _get_context($mfolder) {
 422    $opts = array("method"=>"GET", "header"=>self::_get_auth($mfolder), "timeout"=>5);
 423    return stream_context_create(array("http"=>$opts));
 424  }
 425  
 426  private static function _get_context_action($mfolder, $method, $content="", $header="") {
 427    $header = self::_get_auth($mfolder)."Content-Type: application/atom+xml\r\n".$header;
 428    if ($method=="DELETE" or $method=="PUT") $header = "If-Match: *\r\n".$header;
 429    $opts = array("method"=>$method, "header"=>$header, "content"=>$content, "timeout"=>5, "max_redirects"=>"0", "ignore_errors"=>"1");
 430    return stream_context_create(array("http"=>$opts));
 431  }
 432  
 433  private static function _get_auth($mfolder, $match=true) {
 434    $cid = "gdocs_".$mfolder;
 435    static $conn = array();
 436    if (($auth = sys_cache_get($cid))) return $auth;
 437  
 438    $creds = sys_credentials($mfolder);
 439    $url_auth = "https://www.google.com/accounts/ClientLogin?Email=".urlencode($creds["username"])."&Passwd=".urlencode($creds["password"]).
 440      "&accountType=HOSTED_OR_GOOGLE&source=SimpleGroupware&service=writely";
 441    $http_response_header = array();
 442    $response = @file_get_contents($url_auth);
 443    preg_match("/^Auth=(.+)/m", $response, $match);
 444      
 445    $auth = "";
 446    if (!empty($match[1])) {
 447      $auth = "GData-Version: 3.0\r\nAuthorization: GoogleLogin auth=".trim($match[1])."\r\n";
 448      sys_cache_set($cid, $auth, GDOCS_CACHE);
 449    } else if (!isset($conn[$cid])) {
 450      sys_warning(sprintf("{t}Connection error: %s [%s]{/t}",$http_response_header[0], "Google Docs"));
 451    }
 452    $conn[$cid] = true;
 453    return $auth;
 454  }
 455  }

title

Description

title

Description

title

Description

title

title

Body