]> source.dussan.org Git - nextcloud-server.git/commitdiff
Add support for mounting Google Drive in external storage UI
authorMichael Gapczynski <GapczynskiM@gmail.com>
Wed, 13 Jun 2012 17:54:32 +0000 (13:54 -0400)
committerMichael Gapczynski <GapczynskiM@gmail.com>
Wed, 13 Jun 2012 17:54:50 +0000 (13:54 -0400)
3rdparty/Google/LICENSE.txt [new file with mode: 0644]
3rdparty/Google/OAuth.php [new file with mode: 0755]
3rdparty/Google/common.inc.php [new file with mode: 0755]
apps/files_external/ajax/google.php [new file with mode: 0644]
apps/files_external/js/google.js [new file with mode: 0644]
apps/files_external/lib/config.php
apps/files_external/lib/google.php

diff --git a/3rdparty/Google/LICENSE.txt b/3rdparty/Google/LICENSE.txt
new file mode 100644 (file)
index 0000000..8891c7d
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2007 Andy Smith
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/3rdparty/Google/OAuth.php b/3rdparty/Google/OAuth.php
new file mode 100755 (executable)
index 0000000..c7e75dd
--- /dev/null
@@ -0,0 +1,751 @@
+<?php
+// vim: foldmethod=marker
+
+/* Generic exception class
+ */
+class OAuthException extends Exception {/*{{{*/
+  // pass
+}/*}}}*/
+
+class OAuthConsumer {/*{{{*/
+  public $key;
+  public $secret;
+
+  public function __construct($key, $secret, $callback_url=NULL) {/*{{{*/
+    $this->key = $key;
+    $this->secret = $secret;
+    $this->callback_url = $callback_url;
+  }/*}}}*/
+}/*}}}*/
+
+class OAuthToken {/*{{{*/
+  // access tokens and request tokens
+  public $key;
+  public $secret;
+
+  /**
+   * key = the token
+   * secret = the token secret
+   */
+  function __construct($key, $secret) {/*{{{*/
+    $this->key = $key;
+    $this->secret = $secret;
+  }/*}}}*/
+
+  /**
+   * generates the basic string serialization of a token that a server
+   * would respond to request_token and access_token calls with
+   */
+  function to_string() {/*{{{*/
+    return "oauth_token=" . OAuthUtil::urlencodeRFC3986($this->key) . 
+        "&oauth_token_secret=" . OAuthUtil::urlencodeRFC3986($this->secret);
+  }/*}}}*/
+
+  function __toString() {/*{{{*/
+    return $this->to_string();
+  }/*}}}*/
+}/*}}}*/
+
+class OAuthSignatureMethod {/*{{{*/
+  public function check_signature(&$request, $consumer, $token, $signature) {
+    $built = $this->build_signature($request, $consumer, $token);
+    return $built == $signature;
+  }
+}/*}}}*/
+
+class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {/*{{{*/
+  function get_name() {/*{{{*/
+    return "HMAC-SHA1";
+  }/*}}}*/
+
+  public function build_signature($request, $consumer, $token, $privKey=NULL) {/*{{{*/
+    $base_string = $request->get_signature_base_string();
+    $request->base_string = $base_string;
+
+    $key_parts = array(
+      $consumer->secret,
+      ($token) ? $token->secret : ""
+    );
+
+    $key_parts = array_map(array('OAuthUtil','urlencodeRFC3986'), $key_parts);
+    $key = implode('&', $key_parts);
+
+    return base64_encode( hash_hmac('sha1', $base_string, $key, true));
+  }/*}}}*/
+}/*}}}*/
+
+class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {/*{{{*/
+  public function get_name() {/*{{{*/
+    return "RSA-SHA1";
+  }/*}}}*/
+
+  protected function fetch_public_cert(&$request) {/*{{{*/
+    // not implemented yet, ideas are:
+    // (1) do a lookup in a table of trusted certs keyed off of consumer
+    // (2) fetch via http using a url provided by the requester
+    // (3) some sort of specific discovery code based on request
+    //
+    // either way should return a string representation of the certificate
+    throw Exception("fetch_public_cert not implemented");
+  }/*}}}*/
+
+  protected function fetch_private_cert($privKey) {//&$request) {/*{{{*/
+    // not implemented yet, ideas are:
+    // (1) do a lookup in a table of trusted certs keyed off of consumer
+    //
+    // either way should return a string representation of the certificate
+    throw Exception("fetch_private_cert not implemented");
+  }/*}}}*/
+
+  public function build_signature(&$request, $consumer, $token, $privKey) {/*{{{*/
+    $base_string = $request->get_signature_base_string();
+
+    // Fetch the private key cert based on the request
+    //$cert = $this->fetch_private_cert($consumer->privKey);
+
+    //Pull the private key ID from the certificate
+    //$privatekeyid = openssl_get_privatekey($cert);
+
+    // hacked in 
+    if ($privKey == '') {
+      $fp = fopen($GLOBALS['PRIV_KEY_FILE'], "r");
+      $privKey = fread($fp, 8192);
+      fclose($fp); 
+    }
+    $privatekeyid = openssl_get_privatekey($privKey);
+
+    //Check the computer signature against the one passed in the query
+    $ok = openssl_sign($base_string, $signature, $privatekeyid);   
+
+    //Release the key resource
+    openssl_free_key($privatekeyid);
+
+    return base64_encode($signature);
+  } /*}}}*/
+
+  public function check_signature(&$request, $consumer, $token, $signature) {/*{{{*/
+    $decoded_sig = base64_decode($signature);
+
+    $base_string = $request->get_signature_base_string();
+
+    // Fetch the public key cert based on the request
+    $cert = $this->fetch_public_cert($request);
+
+    //Pull the public key ID from the certificate
+    $publickeyid = openssl_get_publickey($cert);
+
+    //Check the computer signature against the one passed in the query
+    $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);   
+
+    //Release the key resource
+    openssl_free_key($publickeyid);
+
+    return $ok == 1;
+  } /*}}}*/
+}/*}}}*/
+
+class OAuthRequest {/*{{{*/
+  private $parameters;
+  private $http_method;
+  private $http_url;
+  // for debug purposes
+  public $base_string;
+  public static $version = '1.0';
+
+  function __construct($http_method, $http_url, $parameters=NULL) {/*{{{*/
+    @$parameters or $parameters = array();
+    $this->parameters = $parameters;
+    $this->http_method = $http_method;
+    $this->http_url = $http_url;
+  }/*}}}*/
+
+
+  /**
+   * attempt to build up a request from what was passed to the server
+   */
+  public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {/*{{{*/
+    $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") ? 'http' : 'https';
+    @$http_url or $http_url = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+    @$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
+
+    $request_headers = OAuthRequest::get_headers();
+
+    // let the library user override things however they'd like, if they know
+    // which parameters to use then go for it, for example XMLRPC might want to
+    // do this
+    if ($parameters) {
+      $req = new OAuthRequest($http_method, $http_url, $parameters);
+    }
+    // next check for the auth header, we need to do some extra stuff
+    // if that is the case, namely suck in the parameters from GET or POST
+    // so that we can include them in the signature
+    else if (@substr($request_headers['Authorization'], 0, 5) == "OAuth") {
+      $header_parameters = OAuthRequest::split_header($request_headers['Authorization']);
+      if ($http_method == "GET") {
+        $req_parameters = $_GET;
+      } 
+      else if ($http_method = "POST") {
+        $req_parameters = $_POST;
+      } 
+      $parameters = array_merge($header_parameters, $req_parameters);
+      $req = new OAuthRequest($http_method, $http_url, $parameters);
+    }
+    else if ($http_method == "GET") {
+      $req = new OAuthRequest($http_method, $http_url, $_GET);
+    }
+    else if ($http_method == "POST") {
+      $req = new OAuthRequest($http_method, $http_url, $_POST);
+    }
+    return $req;
+  }/*}}}*/
+
+  /**
+   * pretty much a helper function to set up the request
+   */
+  public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {/*{{{*/
+    @$parameters or $parameters = array();
+    $defaults = array("oauth_version" => OAuthRequest::$version,
+                      "oauth_nonce" => OAuthRequest::generate_nonce(),
+                      "oauth_timestamp" => OAuthRequest::generate_timestamp(),
+                      "oauth_consumer_key" => $consumer->key);
+    $parameters = array_merge($defaults, $parameters);
+
+    if ($token) {
+      $parameters['oauth_token'] = $token->key;
+    }
+
+    // oauth v1.0a
+    /*if (isset($_REQUEST['oauth_verifier'])) {
+      $parameters['oauth_verifier'] = $_REQUEST['oauth_verifier'];
+    }*/
+
+
+    return new OAuthRequest($http_method, $http_url, $parameters);
+  }/*}}}*/
+
+  public function set_parameter($name, $value) {/*{{{*/
+    $this->parameters[$name] = $value;
+  }/*}}}*/
+
+  public function get_parameter($name) {/*{{{*/
+    return $this->parameters[$name];
+  }/*}}}*/
+
+  public function get_parameters() {/*{{{*/
+    return $this->parameters;
+  }/*}}}*/
+
+  /**
+   * Returns the normalized parameters of the request
+   * 
+   * This will be all (except oauth_signature) parameters,
+   * sorted first by key, and if duplicate keys, then by
+   * value.
+   *
+   * The returned string will be all the key=value pairs
+   * concated by &.
+   * 
+   * @return string
+   */
+  public function get_signable_parameters() {/*{{{*/
+    // Grab all parameters
+    $params = $this->parameters;
+               
+    // Remove oauth_signature if present
+    if (isset($params['oauth_signature'])) {
+      unset($params['oauth_signature']);
+    }
+               
+    // Urlencode both keys and values
+    $keys = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_keys($params));
+    $values = array_map(array('OAuthUtil', 'urlencodeRFC3986'), array_values($params));
+    $params = array_combine($keys, $values);
+
+    // Sort by keys (natsort)
+    uksort($params, 'strnatcmp');
+
+if(isset($params['title']) && isset($params['title-exact'])) {
+    $temp = $params['title-exact'];
+    $title = $params['title'];
+
+    unset($params['title']);
+    unset($params['title-exact']);
+
+    $params['title-exact'] = $temp;
+    $params['title'] = $title;
+}
+
+    // Generate key=value pairs
+    $pairs = array();
+    foreach ($params as $key=>$value ) {
+      if (is_array($value)) {
+        // If the value is an array, it's because there are multiple 
+        // with the same key, sort them, then add all the pairs
+        natsort($value);
+        foreach ($value as $v2) {
+          $pairs[] = $key . '=' . $v2;
+        }
+      } else {
+        $pairs[] = $key . '=' . $value;
+      }
+    }
+               
+    // Return the pairs, concated with &
+    return implode('&', $pairs);
+  }/*}}}*/
+
+  /**
+   * Returns the base string of this request
+   *
+   * The base string defined as the method, the url
+   * and the parameters (normalized), each urlencoded
+   * and the concated with &.
+   */
+  public function get_signature_base_string() {/*{{{*/
+    $parts = array(
+      $this->get_normalized_http_method(),
+      $this->get_normalized_http_url(),
+      $this->get_signable_parameters()
+    );
+
+    $parts = array_map(array('OAuthUtil', 'urlencodeRFC3986'), $parts);
+
+    return implode('&', $parts);
+  }/*}}}*/
+
+  /**
+   * just uppercases the http method
+   */
+  public function get_normalized_http_method() {/*{{{*/
+    return strtoupper($this->http_method);
+  }/*}}}*/
+
+/**
+   * parses the url and rebuilds it to be
+   * scheme://host/path
+   */
+  public function get_normalized_http_url() {
+    $parts = parse_url($this->http_url);
+
+    $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
+    $port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
+    $host = (isset($parts['host'])) ? strtolower($parts['host']) : '';
+    $path = (isset($parts['path'])) ? $parts['path'] : '';
+
+    if (($scheme == 'https' && $port != '443')
+        || ($scheme == 'http' && $port != '80')) {
+      $host = "$host:$port";
+    }
+    return "$scheme://$host$path";
+  }
+
+  /**
+   * builds a url usable for a GET request
+   */
+  public function to_url() {/*{{{*/
+    $out = $this->get_normalized_http_url() . "?";
+    $out .= $this->to_postdata();
+    return $out;
+  }/*}}}*/
+
+  /**
+   * builds the data one would send in a POST request
+   */
+  public function to_postdata() {/*{{{*/
+    $total = array();
+    foreach ($this->parameters as $k => $v) {
+      $total[] = OAuthUtil::urlencodeRFC3986($k) . "=" . OAuthUtil::urlencodeRFC3986($v);
+    }
+    $out = implode("&", $total);
+    return $out;
+  }/*}}}*/
+
+  /**
+   * builds the Authorization: header
+   */
+  public function to_header() {/*{{{*/
+    $out ='Authorization: OAuth ';
+    $total = array();
+    
+    /*
+    $sig = $this->parameters['oauth_signature'];  
+    unset($this->parameters['oauth_signature']); 
+    uksort($this->parameters, 'strnatcmp');  
+    $this->parameters['oauth_signature'] = $sig;
+    */
+    
+    foreach ($this->parameters as $k => $v) {
+      if (substr($k, 0, 5) != "oauth") continue;
+      $out .= OAuthUtil::urlencodeRFC3986($k) . '="' . OAuthUtil::urlencodeRFC3986($v) . '", ';
+    }
+    $out = substr_replace($out, '', strlen($out) - 2);
+    
+    return $out;
+  }/*}}}*/
+
+  public function __toString() {/*{{{*/
+    return $this->to_url();
+  }/*}}}*/
+
+
+  public function sign_request($signature_method, $consumer, $token, $privKey=NULL) {/*{{{*/
+    $this->set_parameter("oauth_signature_method", $signature_method->get_name());
+    $signature = $this->build_signature($signature_method, $consumer, $token, $privKey);
+    $this->set_parameter("oauth_signature", $signature);
+  }/*}}}*/
+
+  public function build_signature($signature_method, $consumer, $token, $privKey=NULL) {/*{{{*/
+    $signature = $signature_method->build_signature($this, $consumer, $token, $privKey);
+    return $signature;
+  }/*}}}*/
+
+  /**
+   * util function: current timestamp
+   */
+  private static function generate_timestamp() {/*{{{*/
+    return time();
+  }/*}}}*/
+
+  /**
+   * util function: current nonce
+   */
+  private static function generate_nonce() {/*{{{*/
+    $mt = microtime();
+    $rand = mt_rand();
+
+    return md5($mt . $rand); // md5s look nicer than numbers
+  }/*}}}*/
+
+  /**
+   * util function for turning the Authorization: header into
+   * parameters, has to do some unescaping
+   */
+  private static function split_header($header) {/*{{{*/
+    // this should be a regex
+    // error cases: commas in parameter values
+    $parts = explode(",", $header);
+    $out = array();
+    foreach ($parts as $param) {
+      $param = ltrim($param);
+      // skip the "realm" param, nobody ever uses it anyway
+      if (substr($param, 0, 5) != "oauth") continue;
+
+      $param_parts = explode("=", $param);
+
+      // rawurldecode() used because urldecode() will turn a "+" in the
+      // value into a space
+      $out[$param_parts[0]] = rawurldecode(substr($param_parts[1], 1, -1));
+    }
+    return $out;
+  }/*}}}*/
+
+  /**
+   * helper to try to sort out headers for people who aren't running apache
+   */
+  private static function get_headers() {/*{{{*/
+    if (function_exists('apache_request_headers')) {
+      // we need this to get the actual Authorization: header
+      // because apache tends to tell us it doesn't exist
+      return apache_request_headers();
+    }
+    // otherwise we don't have apache and are just going to have to hope
+    // that $_SERVER actually contains what we need
+    $out = array();
+    foreach ($_SERVER as $key => $value) {
+      if (substr($key, 0, 5) == "HTTP_") {
+        // this is chaos, basically it is just there to capitalize the first
+        // letter of every word that is not an initial HTTP and strip HTTP
+        // code from przemek
+        $key = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($key, 5)))));
+        $out[$key] = $value;
+      }
+    }
+    return $out;
+  }/*}}}*/
+}/*}}}*/
+
+class OAuthServer {/*{{{*/
+  protected $timestamp_threshold = 300; // in seconds, five minutes
+  protected $version = 1.0;             // hi blaine
+  protected $signature_methods = array();
+
+  protected $data_store;
+
+  function __construct($data_store) {/*{{{*/
+    $this->data_store = $data_store;
+  }/*}}}*/
+
+  public function add_signature_method($signature_method) {/*{{{*/
+    $this->signature_methods[$signature_method->get_name()] = 
+        $signature_method;
+  }/*}}}*/
+
+  // high level functions
+
+  /**
+   * process a request_token request
+   * returns the request token on success
+   */
+  public function fetch_request_token(&$request) {/*{{{*/
+    $this->get_version($request);
+
+    $consumer = $this->get_consumer($request);
+
+    // no token required for the initial token request
+    $token = NULL;
+
+    $this->check_signature($request, $consumer, $token);
+
+    $new_token = $this->data_store->new_request_token($consumer);
+
+    return $new_token;
+  }/*}}}*/
+
+  /**
+   * process an access_token request
+   * returns the access token on success
+   */
+  public function fetch_access_token(&$request) {/*{{{*/
+    $this->get_version($request);
+
+    $consumer = $this->get_consumer($request);
+
+    // requires authorized request token
+    $token = $this->get_token($request, $consumer, "request");
+
+    $this->check_signature($request, $consumer, $token);
+
+    $new_token = $this->data_store->new_access_token($token, $consumer);
+
+    return $new_token;
+  }/*}}}*/
+
+  /**
+   * verify an api call, checks all the parameters
+   */
+  public function verify_request(&$request) {/*{{{*/
+    $this->get_version($request);
+    $consumer = $this->get_consumer($request);
+    $token = $this->get_token($request, $consumer, "access");
+    $this->check_signature($request, $consumer, $token);
+    return array($consumer, $token);
+  }/*}}}*/
+
+  // Internals from here
+  /**
+   * version 1
+   */
+  private function get_version(&$request) {/*{{{*/
+    $version = $request->get_parameter("oauth_version");
+    if (!$version) {
+      $version = 1.0;
+    }
+    if ($version && $version != $this->version) {
+      throw new OAuthException("OAuth version '$version' not supported");
+    }
+    return $version;
+  }/*}}}*/
+
+  /**
+   * figure out the signature with some defaults
+   */
+  private function get_signature_method(&$request) {/*{{{*/
+    $signature_method =  
+        @$request->get_parameter("oauth_signature_method");
+    if (!$signature_method) {
+      $signature_method = "PLAINTEXT";
+    }
+    if (!in_array($signature_method, 
+                  array_keys($this->signature_methods))) {
+      throw new OAuthException(
+        "Signature method '$signature_method' not supported try one of the following: " . implode(", ", array_keys($this->signature_methods))
+      );      
+    }
+    return $this->signature_methods[$signature_method];
+  }/*}}}*/
+
+  /**
+   * try to find the consumer for the provided request's consumer key
+   */
+  private function get_consumer(&$request) {/*{{{*/
+    $consumer_key = @$request->get_parameter("oauth_consumer_key");
+    if (!$consumer_key) {
+      throw new OAuthException("Invalid consumer key");
+    }
+
+    $consumer = $this->data_store->lookup_consumer($consumer_key);
+    if (!$consumer) {
+      throw new OAuthException("Invalid consumer");
+    }
+
+    return $consumer;
+  }/*}}}*/
+
+  /**
+   * try to find the token for the provided request's token key
+   */
+  private function get_token(&$request, $consumer, $token_type="access") {/*{{{*/
+    $token_field = @$request->get_parameter('oauth_token');
+    $token = $this->data_store->lookup_token(
+      $consumer, $token_type, $token_field
+    );
+    if (!$token) {
+      throw new OAuthException("Invalid $token_type token: $token_field");
+    }
+    return $token;
+  }/*}}}*/
+
+  /**
+   * all-in-one function to check the signature on a request
+   * should guess the signature method appropriately
+   */
+  private function check_signature(&$request, $consumer, $token) {/*{{{*/
+    // this should probably be in a different method
+    $timestamp = @$request->get_parameter('oauth_timestamp');
+    $nonce = @$request->get_parameter('oauth_nonce');
+
+    $this->check_timestamp($timestamp);
+    $this->check_nonce($consumer, $token, $nonce, $timestamp);
+
+    $signature_method = $this->get_signature_method($request);
+
+    $signature = $request->get_parameter('oauth_signature');    
+    $valid_sig = $signature_method->check_signature(
+      $request, 
+      $consumer, 
+      $token, 
+      $signature
+    );
+
+    if (!$valid_sig) {
+      throw new OAuthException("Invalid signature");
+    }
+  }/*}}}*/
+
+  /**
+   * check that the timestamp is new enough
+   */
+  private function check_timestamp($timestamp) {/*{{{*/
+    // verify that timestamp is recentish
+    $now = time();
+    if ($now - $timestamp > $this->timestamp_threshold) {
+      throw new OAuthException("Expired timestamp, yours $timestamp, ours $now");
+    }
+  }/*}}}*/
+
+  /**
+   * check that the nonce is not repeated
+   */
+  private function check_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/
+    // verify that the nonce is uniqueish
+    $found = $this->data_store->lookup_nonce($consumer, $token, $nonce, $timestamp);
+    if ($found) {
+      throw new OAuthException("Nonce already used: $nonce");
+    }
+  }/*}}}*/
+
+
+
+}/*}}}*/
+
+class OAuthDataStore {/*{{{*/
+  function lookup_consumer($consumer_key) {/*{{{*/
+    // implement me
+  }/*}}}*/
+
+  function lookup_token($consumer, $token_type, $token) {/*{{{*/
+    // implement me
+  }/*}}}*/
+
+  function lookup_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/
+    // implement me
+  }/*}}}*/
+
+  function fetch_request_token($consumer) {/*{{{*/
+    // return a new token attached to this consumer
+  }/*}}}*/
+
+  function fetch_access_token($token, $consumer) {/*{{{*/
+    // return a new access token attached to this consumer
+    // for the user associated with this token if the request token
+    // is authorized
+    // should also invalidate the request token
+  }/*}}}*/
+
+}/*}}}*/
+
+
+/*  A very naive dbm-based oauth storage
+ */
+class SimpleOAuthDataStore extends OAuthDataStore {/*{{{*/
+  private $dbh;
+
+  function __construct($path = "oauth.gdbm") {/*{{{*/
+    $this->dbh = dba_popen($path, 'c', 'gdbm');
+  }/*}}}*/
+
+  function __destruct() {/*{{{*/
+    dba_close($this->dbh);
+  }/*}}}*/
+
+  function lookup_consumer($consumer_key) {/*{{{*/
+    $rv = dba_fetch("consumer_$consumer_key", $this->dbh);
+    if ($rv === FALSE) {
+      return NULL;
+    }
+    $obj = unserialize($rv);
+    if (!($obj instanceof OAuthConsumer)) {
+      return NULL;
+    }
+    return $obj;
+  }/*}}}*/
+
+  function lookup_token($consumer, $token_type, $token) {/*{{{*/
+    $rv = dba_fetch("${token_type}_${token}", $this->dbh);
+    if ($rv === FALSE) {
+      return NULL;
+    }
+    $obj = unserialize($rv);
+    if (!($obj instanceof OAuthToken)) {
+      return NULL;
+    }
+    return $obj;
+  }/*}}}*/
+
+  function lookup_nonce($consumer, $token, $nonce, $timestamp) {/*{{{*/
+    return dba_exists("nonce_$nonce", $this->dbh);
+  }/*}}}*/
+
+  function new_token($consumer, $type="request") {/*{{{*/
+    $key = md5(time());
+    $secret = time() + time();
+    $token = new OAuthToken($key, md5(md5($secret)));
+    if (!dba_insert("${type}_$key", serialize($token), $this->dbh)) {
+      throw new OAuthException("doooom!");
+    }
+    return $token;
+  }/*}}}*/
+
+  function new_request_token($consumer) {/*{{{*/
+    return $this->new_token($consumer, "request");
+  }/*}}}*/
+
+  function new_access_token($token, $consumer) {/*{{{*/
+
+    $token = $this->new_token($consumer, 'access');
+    dba_delete("request_" . $token->key, $this->dbh);
+    return $token;
+  }/*}}}*/
+}/*}}}*/
+
+class OAuthUtil {/*{{{*/
+  public static function urlencodeRFC3986($string) {/*{{{*/
+    return str_replace('%7E', '~', rawurlencode($string));
+  }/*}}}*/
+    
+  public static function urldecodeRFC3986($string) {/*{{{*/
+    return rawurldecode($string);
+  }/*}}}*/
+}/*}}}*/
+
+?>
\ No newline at end of file
diff --git a/3rdparty/Google/common.inc.php b/3rdparty/Google/common.inc.php
new file mode 100755 (executable)
index 0000000..57185cd
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+/* Copyright (c) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Eric Bidelman <e.bidelman@google.com>
+ */
+
+$PRIV_KEY_FILE = '/path/to/your/rsa_private_key.pem';
+
+// OAuth library - http://oauth.googlecode.com/svn/code/php/
+require_once('OAuth.php');
+
+// Google's accepted signature methods
+$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+$rsa_method = new OAuthSignatureMethod_RSA_SHA1();
+$SIG_METHODS = array($rsa_method->get_name() => $rsa_method,
+                     $hmac_method->get_name() => $hmac_method);
+
+/**
+ * Makes an HTTP request to the specified URL
+ *
+ * @param string $http_method The HTTP method (GET, POST, PUT, DELETE)
+ * @param string $url Full URL of the resource to access
+ * @param array $extraHeaders (optional) Additional headers to include in each
+ *     request. Elements are header/value pair strings ('Host: example.com')
+ * @param string $postData (optional) POST/PUT request body
+ * @param bool $returnResponseHeaders True if resp. headers should be returned.
+ * @return string Response body from the server
+ */
+function send_signed_request($http_method, $url, $extraHeaders=null,
+                             $postData=null, $returnResponseHeaders=true) {
+  $curl = curl_init($url);
+  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+  curl_setopt($curl, CURLOPT_FAILONERROR, false);
+  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+
+  // Return request headers in the reponse
+//   curl_setopt($curl, CURLINFO_HEADER_OUT, true);
+
+  // Return response headers ni the response?
+  if ($returnResponseHeaders) {
+    curl_setopt($curl, CURLOPT_HEADER, true);
+  }
+
+  $headers = array();
+  //$headers[] = 'GData-Version: 2.0';  // use GData v2 by default
+  if (is_array($extraHeaders)) {
+    $headers = array_merge($headers, $extraHeaders);
+  }
+
+  // Setup default curl options for each type of HTTP request.
+  // This is also a great place to add additional headers for each request.
+  switch($http_method) {
+    case 'GET':
+      curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+      break;
+    case 'POST':
+      curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+      curl_setopt($curl, CURLOPT_POST, 1);
+      curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
+      break;
+    case 'PUT':
+      $headers[] = 'If-Match: *';
+      curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+      curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method);
+      curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
+      break;
+    case 'DELETE':
+      $headers[] = 'If-Match: *';
+      curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+      curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method);
+      break;
+    default:
+      curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+  }
+
+  // Execute the request.  If an error occures, fill the response body with it.
+  $response = curl_exec($curl);
+  if (!$response) {
+    $response = curl_error($curl);
+  }
+
+  // Add server's response headers to our response body
+ $response = curl_getinfo($curl, CURLINFO_HEADER_OUT) . $response;
+
+  curl_close($curl);
+
+  return $response;
+}
+
+/**
+* Takes XML as a string and returns it nicely indented
+*
+* @param string $xml The xml to beautify
+* @param boolean $html_output True if returned XML should be escaped for HTML.
+* @return string The beautified xml
+*/
+function xml_pretty_printer($xml, $html_output=false) {
+  $xml_obj = new SimpleXMLElement($xml);
+  $level = 2;
+
+  // Get an array containing each XML element
+  $xml = explode("\n", preg_replace('/>\s*</', ">\n<", $xml_obj->asXML()));
+
+  // Hold current indentation level
+  $indent = 0;
+
+  $pretty = array();
+
+  // Shift off opening XML tag if present
+  if (count($xml) && preg_match('/^<\?\s*xml/', $xml[0])) {
+    $pretty[] = array_shift($xml);
+  }
+
+  foreach ($xml as $el) {
+    if (preg_match('/^<([\w])+[^>\/]*>$/U', $el)) {
+      // opening tag, increase indent
+      $pretty[] = str_repeat(' ', $indent) . $el;
+      $indent += $level;
+    } else {
+      if (preg_match('/^<\/.+>$/', $el)) {
+        $indent -= $level;  // closing tag, decrease indent
+      }
+      if ($indent < 0) {
+        $indent += $level;
+      }
+      $pretty[] = str_repeat(' ', $indent) . $el;
+    }
+  }
+
+  $xml = implode("\n", $pretty);
+  return $html_output ? htmlentities($xml) : $xml;
+}
+
+/**
+ * Joins key/value pairs by $inner_glue and each pair together by $outer_glue.
+ *
+ * Example: implode_assoc('=', '&', array('a' => 1, 'b' => 2)) === 'a=1&b=2'
+ *
+ * @param string $inner_glue What to implode each key/value pair with
+ * @param string $outer_glue What to impode each key/value string subset with
+ * @param array $array Associative array of query parameters
+ * @return string Urlencoded string of query parameters
+ */
+function implode_assoc($inner_glue, $outer_glue, $array) {
+  $output = array();
+  foreach($array as $key => $item) {
+    $output[] = $key . $inner_glue . urlencode($item);
+  }
+  return implode($outer_glue, $output);
+}
+
+/**
+ * Explodes a string of key/value url parameters into an associative array.
+ * This method performs the compliment operations of implode_assoc().
+ *
+ * Example: explode_assoc('=', '&', 'a=1&b=2') === array('a' => 1, 'b' => 2)
+ *
+ * @param string $inner_glue What each key/value pair is joined with
+ * @param string $outer_glue What each set of key/value pairs is joined with.
+ * @param array $array Associative array of query parameters
+ * @return array Urlencoded string of query parameters
+ */
+function explode_assoc($inner_glue, $outer_glue, $params) {
+  $tempArr = explode($outer_glue, $params);
+  foreach($tempArr as $val) {
+    $pos = strpos($val, $inner_glue);
+    $key = substr($val, 0, $pos);
+    $array2[$key] = substr($val, $pos + 1, strlen($val));
+  }
+  return $array2;
+}
+
+?>
\ No newline at end of file
diff --git a/apps/files_external/ajax/google.php b/apps/files_external/ajax/google.php
new file mode 100644 (file)
index 0000000..23ecfc3
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+require_once 'Google/common.inc.php';
+
+OCP\JSON::checkAppEnabled('files_external');
+OCP\JSON::checkLoggedIn();
+$consumer = new OAuthConsumer('anonymous', 'anonymous');
+$sigMethod = new OAuthSignatureMethod_HMAC_SHA1();
+if (isset($_POST['step'])) {
+       switch ($_POST['step']) {
+               case 1:
+                       if (isset($_POST['callback'])) {
+                               $callback = $_POST['callback'];
+                       } else {
+                               $callback = null;
+                       }
+                       $scope = 'https://docs.google.com/feeds/ https://docs.googleusercontent.com/ https://spreadsheets.google.com/feeds/';
+                       $url = 'https://www.google.com/accounts/OAuthGetRequestToken?scope='.urlencode($scope);
+                       $params = array('scope' => $scope, 'oauth_callback' => $callback);
+                       $request = OAuthRequest::from_consumer_and_token($consumer, null, 'GET', $url, $params);
+                       $request->sign_request($sigMethod, $consumer, null);
+                       $response = send_signed_request('GET', $url, array($request->to_header()), null, false);
+                       $token = array();
+                       parse_str($response, $token);
+                       if (isset($token['oauth_token']) && isset($token['oauth_token_secret'])) {
+                               $authUrl = 'https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token='.$token['oauth_token'];
+                               OCP\JSON::success(array('data' => array('url' => $authUrl, 'request_token' => $token['oauth_token'], 'request_token_secret' => $token['oauth_token_secret'])));
+                       } else {
+                               OCP\JSON::error(array('data' => array('message' => 'Fetching request tokens failed. Error: '.$response)));
+                       }
+                       break;
+               case 2:
+                       if (isset($_POST['oauth_verifier']) && isset($_POST['request_token']) && isset($_POST['request_token_secret'])) {
+                               $token = new OAuthToken($_POST['request_token'], $_POST['request_token_secret']);
+                               $url = 'https://www.google.com/accounts/OAuthGetAccessToken';
+                               $request = OAuthRequest::from_consumer_and_token($consumer, $token, 'GET', $url, array('oauth_verifier' => $_POST['oauth_verifier']));
+                               $request->sign_request($sigMethod, $consumer, $token);
+                               $response = send_signed_request('GET', $url, array($request->to_header()), null, false);
+                               $token = array();
+                               parse_str($response, $token);
+                               if (isset($token['oauth_token']) && isset($token['oauth_token_secret'])) {
+                                       OCP\JSON::success(array('access_token' => $token['oauth_token'], 'access_token_secret' => $token['oauth_token_secret']));
+                               } else {
+                                       OCP\JSON::error(array('data' => array('message' => 'Fetching access tokens failed. Error: '.$response)));
+                               }
+                       }
+                       break;
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/apps/files_external/js/google.js b/apps/files_external/js/google.js
new file mode 100644 (file)
index 0000000..0d65cfd
--- /dev/null
@@ -0,0 +1,48 @@
+$(document).ready(function() {
+       
+       $('#externalStorage tbody tr').each(function() {
+               if ($(this).find('.backend').data('class') == 'OC_Filestorage_Google') {
+                       var token = $(this).find('[data-parameter="token"]');
+                       var token_secret = $(this).find('[data-parameter="token_secret"]');
+                       if ($(token).val() == '' && $(token).val() == '') {
+                               $(this).find('.configuration').append('<a class="button google">Grant access</a>');
+                       } else  {
+                               var params = {};
+                               window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) {
+                                       params[key] = value;
+                               });
+                               if (params['oauth_token'].length > 1 && decodeURIComponent(params['oauth_token']) == $(token).val() && params['oauth_verifier'].length > 1) {
+                                       var tr = $(this);
+                                       $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 2, oauth_verifier: params['oauth_verifier'], request_token: $(token).val(), request_token_secret: $(token_secret).val() }, function(result) {
+                                               if (result && result.status == 'success') {
+                                                       $(token).val(result.access_token);
+                                                       $(token_secret).val(result.access_token_secret);
+                                                       OC.MountConfig.saveStorage(tr);
+                                               } else {
+                                                       OC.dialogs.alert(result.data.message, 'Error configuring Google Drive storage');
+                                               }
+                                       });
+                               }
+                       }
+                       return false;
+               }
+       });
+       
+       $('.google').live('click', function(event) {
+               event.preventDefault();
+               var tr = $(this).parent().parent();
+               var token = $(this).parent().find('[data-parameter="token"]');
+               var token_secret = $(this).parent().find('[data-parameter="token_secret"]');
+               $.post(OC.filePath('files_external', 'ajax', 'google.php'), { step: 1, callback: window.location.href }, function(result) {
+                       if (result && result.status == 'success') {
+                               $(token).val(result.data.request_token);
+                               $(token_secret).val(result.data.request_token_secret);
+                               OC.MountConfig.saveStorage(tr);
+                               window.location = result.data.url;
+                       } else {
+                               OC.dialogs.alert(result.data.message, 'Error configuring Google Drive storage');
+                       }
+               });
+       });
+       
+});
index b2ebcd96fcc1153ee9d353e85f8cb5c62b6c432e..870c13b5aed03ae799f4178a1a70fad302ae1953 100755 (executable)
@@ -41,8 +41,9 @@ class OC_Mount_Config {
                return array(
                        'OC_Filestorage_Local' => array('backend' => 'Local', 'configuration' => array('datadir' => 'Location')),
                        'OC_Filestorage_AmazonS3' => array('backend' => 'Amazon S3', 'configuration' => array('key' => 'Key', 'secret' => '*Secret', 'bucket' => 'Bucket')),
-                       'OC_Filestorage_Dropbox' => array('backend' => 'Dropbox', 'configuration' => array('app_key' => 'App key', 'app_secret' => 'App secret', 'token' => '#token', 'token_secret' => '#token_secret' ), 'custom' => 'dropbox'),
+                       'OC_Filestorage_Dropbox' => array('backend' => 'Dropbox', 'configuration' => array('app_key' => 'App key', 'app_secret' => 'App secret', 'token' => '#token', 'token_secret' => '#token_secret'), 'custom' => 'dropbox'),
                        'OC_Filestorage_FTP' => array('backend' => 'FTP', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure ftps://')),
+                       'OC_Filestorage_Google' => array('backend' => 'Google Drive', 'configuration' => array('token' => '#token', 'token_secret' => '#token secret'), 'custom' => 'google'),
                        'OC_Filestorage_SWIFT' => array('backend' => 'OpenStack Swift', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'token' => '*Token', 'root' => '&Root', 'secure' => '!Secure ftps://')),
                        'OC_Filestorage_SMB' => array('backend' => 'SMB', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root')),
                        'OC_Filestorage_DAV' => array('backend' => 'WebDAV', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure https://'))
index d2285a6d82ce15830016a62c7c5e4b834e7c8059..c2a4af0ff8a8240fb3a43646e17dda272ebdd0dd 100644 (file)
@@ -20,7 +20,7 @@
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-require_once 'common.inc.php';
+require_once 'Google/common.inc.php';
 
 class OC_Filestorage_Google extends OC_Filestorage_Common {