| 1 | efrain | 1 | <?php
 | 
        
           |  |  | 2 |   | 
        
           |  |  | 3 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 4 | //
 | 
        
           |  |  | 5 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 6 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 7 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 8 | // (at your option) any later version.
 | 
        
           |  |  | 9 | //
 | 
        
           |  |  | 10 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 13 | // GNU General Public License for more details.
 | 
        
           |  |  | 14 | //
 | 
        
           |  |  | 15 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 16 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 17 |   | 
        
           |  |  | 18 | /**
 | 
        
           |  |  | 19 |  * This plugin is used to access s3 files
 | 
        
           |  |  | 20 |  *
 | 
        
           |  |  | 21 |  * @since Moodle 2.0
 | 
        
           |  |  | 22 |  * @package    repository_s3
 | 
        
           |  |  | 23 |  * @copyright  2010 Dongsheng Cai {@link http://dongsheng.org}
 | 
        
           |  |  | 24 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 25 |  */
 | 
        
           |  |  | 26 | require_once($CFG->dirroot . '/repository/lib.php');
 | 
        
           |  |  | 27 | require_once($CFG->dirroot . '/repository/s3/S3.php');
 | 
        
           |  |  | 28 |   | 
        
           |  |  | 29 | // This constant is not defined in php 5.4. Set it to avoid errors.
 | 
        
           |  |  | 30 | if (!defined('CURL_SSLVERSION_TLSv1')) {
 | 
        
           |  |  | 31 |     define('CURL_SSLVERSION_TLSv1', 1);
 | 
        
           |  |  | 32 | }
 | 
        
           |  |  | 33 |   | 
        
           |  |  | 34 | /**
 | 
        
           |  |  | 35 |  * This is a repository class used to browse Amazon S3 content.
 | 
        
           |  |  | 36 |  *
 | 
        
           |  |  | 37 |  * @since Moodle 2.0
 | 
        
           |  |  | 38 |  * @package    repository_s3
 | 
        
           |  |  | 39 |  * @copyright  2009 Dongsheng Cai {@link http://dongsheng.org}
 | 
        
           |  |  | 40 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 41 |  */
 | 
        
           |  |  | 42 | class repository_s3 extends repository {
 | 
        
           |  |  | 43 |   | 
        
           |  |  | 44 |     /** @var string access key. */
 | 
        
           |  |  | 45 |     protected $access_key;
 | 
        
           |  |  | 46 |     /** @var string secret key. */
 | 
        
           |  |  | 47 |     protected $secret_key;
 | 
        
           |  |  | 48 |     /** @var string endpoint URL. */
 | 
        
           |  |  | 49 |     protected $endpoint;
 | 
        
           |  |  | 50 |     /** @var S3 S3 class. */
 | 
        
           |  |  | 51 |     protected $s;
 | 
        
           |  |  | 52 |   | 
        
           |  |  | 53 |     /**
 | 
        
           |  |  | 54 |      * Constructor
 | 
        
           |  |  | 55 |      * @param int $repositoryid
 | 
        
           |  |  | 56 |      * @param object $context
 | 
        
           |  |  | 57 |      * @param array $options
 | 
        
           |  |  | 58 |      */
 | 
        
           |  |  | 59 |     public function __construct($repositoryid, $context = SYSCONTEXTID, $options = array()) {
 | 
        
           |  |  | 60 |         global $CFG;
 | 
        
           |  |  | 61 |         parent::__construct($repositoryid, $context, $options);
 | 
        
           |  |  | 62 |         $this->access_key = get_config('s3', 'access_key');
 | 
        
           |  |  | 63 |         $this->secret_key = get_config('s3', 'secret_key');
 | 
        
           |  |  | 64 |         $this->endpoint = get_config('s3', 'endpoint');
 | 
        
           |  |  | 65 |         if ($this->endpoint === false) { // If no endpoint has been set, use the default.
 | 
        
           |  |  | 66 |             $this->endpoint = 's3.amazonaws.com';
 | 
        
           |  |  | 67 |         }
 | 
        
           |  |  | 68 |         $this->s = new S3($this->access_key, $this->secret_key, false, $this->endpoint);
 | 
        
           |  |  | 69 |         $this->s->setExceptions(true);
 | 
        
           |  |  | 70 |   | 
        
           |  |  | 71 |         // Port of curl::__construct().
 | 
        
           |  |  | 72 |         if (!empty($CFG->proxyhost)) {
 | 
        
           |  |  | 73 |             if (empty($CFG->proxyport)) {
 | 
        
           |  |  | 74 |                 $proxyhost = $CFG->proxyhost;
 | 
        
           |  |  | 75 |             } else {
 | 
        
           |  |  | 76 |                 $proxyhost = $CFG->proxyhost . ':' . $CFG->proxyport;
 | 
        
           |  |  | 77 |             }
 | 
        
           |  |  | 78 |             $proxytype = CURLPROXY_HTTP;
 | 
        
           |  |  | 79 |             $proxyuser = null;
 | 
        
           |  |  | 80 |             $proxypass = null;
 | 
        
           |  |  | 81 |             if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
 | 
        
           |  |  | 82 |                 $proxyuser = $CFG->proxyuser;
 | 
        
           |  |  | 83 |                 $proxypass = $CFG->proxypassword;
 | 
        
           |  |  | 84 |             }
 | 
        
           |  |  | 85 |             if (!empty($CFG->proxytype) && $CFG->proxytype == 'SOCKS5') {
 | 
        
           |  |  | 86 |                 $proxytype = CURLPROXY_SOCKS5;
 | 
        
           |  |  | 87 |             }
 | 
        
           |  |  | 88 |             $this->s->setProxy($proxyhost, $proxyuser, $proxypass, $proxytype);
 | 
        
           |  |  | 89 |         }
 | 
        
           |  |  | 90 |     }
 | 
        
           |  |  | 91 |   | 
        
           |  |  | 92 |     /**
 | 
        
           |  |  | 93 |      * Extracts the Bucket and URI from the path
 | 
        
           |  |  | 94 |      *
 | 
        
           |  |  | 95 |      * @param string $path path in this format 'bucket/path/to/folder/and/file'
 | 
        
           |  |  | 96 |      * @return array including bucket and uri
 | 
        
           |  |  | 97 |      */
 | 
        
           |  |  | 98 |     protected function explode_path($path) {
 | 
        
           |  |  | 99 |         $parts = explode('/', $path, 2);
 | 
        
           |  |  | 100 |         if (isset($parts[1]) && $parts[1] !== '') {
 | 
        
           |  |  | 101 |             list($bucket, $uri) = $parts;
 | 
        
           |  |  | 102 |         } else {
 | 
        
           |  |  | 103 |             $bucket = $parts[0];
 | 
        
           |  |  | 104 |             $uri = '';
 | 
        
           |  |  | 105 |         }
 | 
        
           |  |  | 106 |         return array($bucket, $uri);
 | 
        
           |  |  | 107 |     }
 | 
        
           |  |  | 108 |   | 
        
           |  |  | 109 |     /**
 | 
        
           |  |  | 110 |      * Get S3 file list
 | 
        
           |  |  | 111 |      *
 | 
        
           |  |  | 112 |      * @param string $path
 | 
        
           |  |  | 113 |      * @return array The file list and options
 | 
        
           |  |  | 114 |      */
 | 
        
           |  |  | 115 |     public function get_listing($path = '', $page = '') {
 | 
        
           |  |  | 116 |         global $CFG, $OUTPUT;
 | 
        
           |  |  | 117 |         if (empty($this->access_key)) {
 | 
        
           |  |  | 118 |             throw new moodle_exception('needaccesskey', 'repository_s3');
 | 
        
           |  |  | 119 |         }
 | 
        
           |  |  | 120 |   | 
        
           |  |  | 121 |         $list = array();
 | 
        
           |  |  | 122 |         $list['list'] = array();
 | 
        
           |  |  | 123 |         $list['path'] = array(
 | 
        
           |  |  | 124 |             array('name' => get_string('pluginname', 'repository_s3'), 'path' => '')
 | 
        
           |  |  | 125 |         );
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 |         // the management interface url
 | 
        
           |  |  | 128 |         $list['manage'] = false;
 | 
        
           |  |  | 129 |         // dynamically loading
 | 
        
           |  |  | 130 |         $list['dynload'] = true;
 | 
        
           |  |  | 131 |         // the current path of this list.
 | 
        
           |  |  | 132 |         // set to true, the login link will be removed
 | 
        
           |  |  | 133 |         $list['nologin'] = true;
 | 
        
           |  |  | 134 |         // set to true, the search button will be removed
 | 
        
           |  |  | 135 |         $list['nosearch'] = true;
 | 
        
           |  |  | 136 |   | 
        
           |  |  | 137 |         $tree = array();
 | 
        
           |  |  | 138 |   | 
        
           |  |  | 139 |         if (empty($path)) {
 | 
        
           |  |  | 140 |             try {
 | 
        
           |  |  | 141 |                 $buckets = $this->s->listBuckets();
 | 
        
           |  |  | 142 |             } catch (S3Exception $e) {
 | 
        
           |  |  | 143 |                 throw new moodle_exception(
 | 
        
           |  |  | 144 |                     'errorwhilecommunicatingwith',
 | 
        
           |  |  | 145 |                     'repository',
 | 
        
           |  |  | 146 |                     '',
 | 
        
           |  |  | 147 |                     $this->get_name(),
 | 
        
           |  |  | 148 |                     $e->getMessage()
 | 
        
           |  |  | 149 |                 );
 | 
        
           |  |  | 150 |             }
 | 
        
           |  |  | 151 |             foreach ($buckets as $bucket) {
 | 
        
           |  |  | 152 |                 $folder = array(
 | 
        
           |  |  | 153 |                     'title' => $bucket,
 | 
        
           |  |  | 154 |                     'children' => array(),
 | 
        
           |  |  | 155 |                     'thumbnail' => $OUTPUT->image_url(file_folder_icon())->out(false),
 | 
        
           |  |  | 156 |                     'path' => $bucket
 | 
        
           |  |  | 157 |                     );
 | 
        
           |  |  | 158 |                 $tree[] = $folder;
 | 
        
           |  |  | 159 |             }
 | 
        
           |  |  | 160 |         } else {
 | 
        
           |  |  | 161 |             $files = array();
 | 
        
           |  |  | 162 |             $folders = array();
 | 
        
           |  |  | 163 |             list($bucket, $uri) = $this->explode_path($path);
 | 
        
           |  |  | 164 |   | 
        
           |  |  | 165 |             try {
 | 
        
           |  |  | 166 |                 $contents = $this->s->getBucket($bucket, $uri, null, null, '/', true);
 | 
        
           |  |  | 167 |             } catch (S3Exception $e) {
 | 
        
           |  |  | 168 |                 throw new moodle_exception(
 | 
        
           |  |  | 169 |                     'errorwhilecommunicatingwith',
 | 
        
           |  |  | 170 |                     'repository',
 | 
        
           |  |  | 171 |                     '',
 | 
        
           |  |  | 172 |                     $this->get_name(),
 | 
        
           |  |  | 173 |                     $e->getMessage()
 | 
        
           |  |  | 174 |                 );
 | 
        
           |  |  | 175 |             }
 | 
        
           |  |  | 176 |             foreach ($contents as $object) {
 | 
        
           |  |  | 177 |   | 
        
           |  |  | 178 |                 // If object has a prefix, it is a 'CommonPrefix', which we consider a folder
 | 
        
           |  |  | 179 |                 if (isset($object['prefix'])) {
 | 
        
           |  |  | 180 |                     $title = rtrim($object['prefix'], '/');
 | 
        
           |  |  | 181 |                 } else {
 | 
        
           |  |  | 182 |                     $title = $object['name'];
 | 
        
           |  |  | 183 |                 }
 | 
        
           |  |  | 184 |   | 
        
           |  |  | 185 |                 // Removes the prefix (folder path) from the title
 | 
        
           |  |  | 186 |                 if (strlen($uri) > 0) {
 | 
        
           |  |  | 187 |                     $title = substr($title, strlen($uri));
 | 
        
           |  |  | 188 |                     // Check if title is empty and not zero
 | 
        
           |  |  | 189 |                     if (empty($title) && !is_numeric($title)) {
 | 
        
           |  |  | 190 |                         // Amazon returns the prefix itself, we skip it
 | 
        
           |  |  | 191 |                         continue;
 | 
        
           |  |  | 192 |                     }
 | 
        
           |  |  | 193 |                 }
 | 
        
           |  |  | 194 |   | 
        
           |  |  | 195 |                 // This is a so-called CommonPrefix, we consider it as a folder
 | 
        
           |  |  | 196 |                 if (isset($object['prefix'])) {
 | 
        
           |  |  | 197 |                     $folders[] = array(
 | 
        
           |  |  | 198 |                         'title' => $title,
 | 
        
           |  |  | 199 |                         'children' => array(),
 | 
        
           |  |  | 200 |                         'thumbnail' => $OUTPUT->image_url(file_folder_icon())->out(false),
 | 
        
           |  |  | 201 |                         'path' => $bucket . '/' . $object['prefix'],
 | 
        
           |  |  | 202 |                     );
 | 
        
           |  |  | 203 |                 } else {
 | 
        
           |  |  | 204 |                     $files[] = array(
 | 
        
           |  |  | 205 |                         'title' => $title,
 | 
        
           |  |  | 206 |                         'size' => $object['size'],
 | 
        
           |  |  | 207 |                         'datemodified' => $object['time'],
 | 
        
           |  |  | 208 |                         'source' => $bucket . '/' . $object['name'],
 | 
        
           |  |  | 209 |                         'thumbnail' => $OUTPUT->image_url(file_extension_icon($title))->out(false)
 | 
        
           |  |  | 210 |                     );
 | 
        
           |  |  | 211 |                 }
 | 
        
           |  |  | 212 |             }
 | 
        
           |  |  | 213 |             $tree = array_merge($folders, $files);
 | 
        
           |  |  | 214 |         }
 | 
        
           |  |  | 215 |   | 
        
           |  |  | 216 |         $trail = '';
 | 
        
           |  |  | 217 |         if (!empty($path)) {
 | 
        
           |  |  | 218 |             $parts = explode('/', $path);
 | 
        
           |  |  | 219 |             if (count($parts) > 1) {
 | 
        
           |  |  | 220 |                 foreach ($parts as $part) {
 | 
        
           |  |  | 221 |                     if (!empty($part)) {
 | 
        
           |  |  | 222 |                         $trail .= $part . '/';
 | 
        
           |  |  | 223 |                         $list['path'][] = array('name' => $part, 'path' => $trail);
 | 
        
           |  |  | 224 |                     }
 | 
        
           |  |  | 225 |                 }
 | 
        
           |  |  | 226 |             } else {
 | 
        
           |  |  | 227 |                 $list['path'][] = array('name' => $path, 'path' => $path);
 | 
        
           |  |  | 228 |             }
 | 
        
           |  |  | 229 |         }
 | 
        
           |  |  | 230 |   | 
        
           |  |  | 231 |         $list['list'] = $tree;
 | 
        
           |  |  | 232 |   | 
        
           |  |  | 233 |         return $list;
 | 
        
           |  |  | 234 |     }
 | 
        
           |  |  | 235 |   | 
        
           |  |  | 236 |     /**
 | 
        
           |  |  | 237 |      * Download S3 files to moodle
 | 
        
           |  |  | 238 |      *
 | 
        
           |  |  | 239 |      * @param string $filepath
 | 
        
           |  |  | 240 |      * @param string $file The file path in moodle
 | 
        
           |  |  | 241 |      * @return array The local stored path
 | 
        
           |  |  | 242 |      */
 | 
        
           |  |  | 243 |     public function get_file($filepath, $file = '') {
 | 
        
           |  |  | 244 |         list($bucket, $uri) = $this->explode_path($filepath);
 | 
        
           |  |  | 245 |         $path = $this->prepare_file($file);
 | 
        
           |  |  | 246 |         try {
 | 
        
           |  |  | 247 |             $this->s->getObject($bucket, $uri, $path);
 | 
        
           |  |  | 248 |         } catch (S3Exception $e) {
 | 
        
           |  |  | 249 |             throw new moodle_exception(
 | 
        
           |  |  | 250 |                 'errorwhilecommunicatingwith',
 | 
        
           |  |  | 251 |                 'repository',
 | 
        
           |  |  | 252 |                 '',
 | 
        
           |  |  | 253 |                 $this->get_name(),
 | 
        
           |  |  | 254 |                 $e->getMessage()
 | 
        
           |  |  | 255 |             );
 | 
        
           |  |  | 256 |         }
 | 
        
           |  |  | 257 |         return array('path' => $path);
 | 
        
           |  |  | 258 |     }
 | 
        
           |  |  | 259 |   | 
        
           |  |  | 260 |     /**
 | 
        
           |  |  | 261 |      * Return the source information
 | 
        
           |  |  | 262 |      *
 | 
        
           |  |  | 263 |      * @param stdClass $filepath
 | 
        
           |  |  | 264 |      * @return string
 | 
        
           |  |  | 265 |      */
 | 
        
           |  |  | 266 |     public function get_file_source_info($filepath) {
 | 
        
           |  |  | 267 |         return 'Amazon S3: ' . $filepath;
 | 
        
           |  |  | 268 |     }
 | 
        
           |  |  | 269 |   | 
        
           |  |  | 270 |     /**
 | 
        
           |  |  | 271 |      * S3 doesn't require login
 | 
        
           |  |  | 272 |      *
 | 
        
           |  |  | 273 |      * @return bool
 | 
        
           |  |  | 274 |      */
 | 
        
           |  |  | 275 |     public function check_login() {
 | 
        
           |  |  | 276 |         return true;
 | 
        
           |  |  | 277 |     }
 | 
        
           |  |  | 278 |   | 
        
           |  |  | 279 |     /**
 | 
        
           |  |  | 280 |      * S3 doesn't provide search
 | 
        
           |  |  | 281 |      *
 | 
        
           |  |  | 282 |      * @return bool
 | 
        
           |  |  | 283 |      */
 | 
        
           |  |  | 284 |     public function global_search() {
 | 
        
           |  |  | 285 |         return false;
 | 
        
           |  |  | 286 |     }
 | 
        
           |  |  | 287 |   | 
        
           |  |  | 288 |     public static function get_type_option_names() {
 | 
        
           |  |  | 289 |         return array('access_key', 'secret_key', 'endpoint', 'pluginname');
 | 
        
           |  |  | 290 |     }
 | 
        
           |  |  | 291 |   | 
        
           |  |  | 292 |     public static function type_config_form($mform, $classname = 'repository') {
 | 
        
           |  |  | 293 |         parent::type_config_form($mform);
 | 
        
           |  |  | 294 |         $strrequired = get_string('required');
 | 
        
           |  |  | 295 |         $endpointselect = array( // List of possible Amazon S3 Endpoints.
 | 
        
           |  |  | 296 |             "s3.amazonaws.com" => "s3.amazonaws.com",
 | 
        
           |  |  | 297 |             "s3-external-1.amazonaws.com" => "s3-external-1.amazonaws.com",
 | 
        
           |  |  | 298 |             "s3-us-west-2.amazonaws.com" => "s3-us-west-2.amazonaws.com",
 | 
        
           |  |  | 299 |             "s3-us-west-1.amazonaws.com" => "s3-us-west-1.amazonaws.com",
 | 
        
           |  |  | 300 |             "s3-eu-west-1.amazonaws.com" => "s3-eu-west-1.amazonaws.com",
 | 
        
           |  |  | 301 |             "s3.eu-central-1.amazonaws.com" => "s3.eu-central-1.amazonaws.com",
 | 
        
           |  |  | 302 |             "s3-eu-central-1.amazonaws.com" => "s3-eu-central-1.amazonaws.com",
 | 
        
           |  |  | 303 |             "s3-ap-southeast-1.amazonaws.com" => "s3-ap-southeast-1.amazonaws.com",
 | 
        
           |  |  | 304 |             "s3-ap-southeast-2.amazonaws.com" => "s3-ap-southeast-2.amazonaws.com",
 | 
        
           |  |  | 305 |             "s3-ap-northeast-1.amazonaws.com" => "s3-ap-northeast-1.amazonaws.com",
 | 
        
           |  |  | 306 |             "s3-sa-east-1.amazonaws.com" => "s3-sa-east-1.amazonaws.com"
 | 
        
           |  |  | 307 |         );
 | 
        
           |  |  | 308 |         $mform->addElement('text', 'access_key', get_string('access_key', 'repository_s3'));
 | 
        
           |  |  | 309 |         $mform->setType('access_key', PARAM_RAW_TRIMMED);
 | 
        
           |  |  | 310 |         $mform->addElement('text', 'secret_key', get_string('secret_key', 'repository_s3'));
 | 
        
           |  |  | 311 |         $mform->setType('secret_key', PARAM_RAW_TRIMMED);
 | 
        
           |  |  | 312 |         $mform->addElement('select', 'endpoint', get_string('endpoint', 'repository_s3'), $endpointselect);
 | 
        
           |  |  | 313 |         $mform->setDefault('endpoint', 's3.amazonaws.com'); // Default to US Endpoint.
 | 
        
           |  |  | 314 |         $mform->addRule('access_key', $strrequired, 'required', null, 'client');
 | 
        
           |  |  | 315 |         $mform->addRule('secret_key', $strrequired, 'required', null, 'client');
 | 
        
           |  |  | 316 |     }
 | 
        
           |  |  | 317 |   | 
        
           |  |  | 318 |     /**
 | 
        
           |  |  | 319 |      * S3 plugins doesn't support return links of files
 | 
        
           |  |  | 320 |      *
 | 
        
           |  |  | 321 |      * @return int
 | 
        
           |  |  | 322 |      */
 | 
        
           |  |  | 323 |     public function supported_returntypes() {
 | 
        
           |  |  | 324 |         return FILE_INTERNAL;
 | 
        
           |  |  | 325 |     }
 | 
        
           |  |  | 326 |   | 
        
           |  |  | 327 |     /**
 | 
        
           |  |  | 328 |      * Is this repository accessing private data?
 | 
        
           |  |  | 329 |      *
 | 
        
           |  |  | 330 |      * @return bool
 | 
        
           |  |  | 331 |      */
 | 
        
           |  |  | 332 |     public function contains_private_data() {
 | 
        
           |  |  | 333 |         return false;
 | 
        
           |  |  | 334 |     }
 | 
        
           |  |  | 335 | }
 |