/[drupal]/contributions/modules/fileapi/drivers/s3.driver
ViewVC logotype

Diff of /contributions/modules/fileapi/drivers/s3.driver

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.3, Sat Dec 23 09:12:36 2006 UTC revision 1.3.4.3, Thu Jan 4 16:41:48 2007 UTC
# Line 2  Line 2 
2    
3  /**  /**
4   * @file   * @file
5   * Amazon S3 storage driver for filesystem.   * Amazon S3 storage driver for fileapi.
6     *
7     * We are treating S3 like a normal hierarchal filesystem. The bucket
8     * is predetermined by the mountpoint settings. Except for list buckets
9     * and put bucket functions.
10     *
11     * We use the keys with a delimiter of '/' to create our heirarchy.
12     *
13     * @todo examine adding caching to save some requests to S3.
14     * @todo wrap amazon s3 functions into a single class with multiple instance and
15     *       caching support.
16     *
17   */   */
18    
19    
20  /**  /**
21   * driver settings form. called by filesystem.module when creating a respository using this driver.   * driver settings form. called by fileapi.module when creating a respository using this driver.
22   * @param $settings   * @param $settings
23   *  settings array from database.   *   settings array from database.
24     * @todo convert form to multistep to allow input of credentials, then bucket selection/creation.
25     * @todo add bucket access control policy settings
26   */   */
27    
28  function fileapi_driver_s3_settings_form($settings = array()) {  function fileapi_driver_s3_settings_form($settings = array()) {
# Line 18  function fileapi_driver_s3_settings_form Line 31  function fileapi_driver_s3_settings_form
31      '#type' => 'fieldset',      '#type' => 'fieldset',
32      '#title' => t('Amazon S3 storage driver settings'),      '#title' => t('Amazon S3 storage driver settings'),
33      '#tree' => TRUE,      '#tree' => TRUE,
34        '#validate' => array('fileapi_driver_s3_settings_form_validate' => array()),
35        '#submit' => array('fileapi_driver_s3_settings_form_submit' => array()),
36    );    );
37    
38    $form['driver'] = array(    $form['driver'] = array(
# Line 28  function fileapi_driver_s3_settings_form Line 43  function fileapi_driver_s3_settings_form
43    $form['s3 url'] = array(    $form['s3 url'] = array(
44      '#type' => 'textfield',      '#type' => 'textfield',
45      '#title' => t('Amazon S3 url'),      '#title' => t('Amazon S3 url'),
46        '#description' => t('The URL must not contain a trailing slash.'),
47      '#default_value' => strlen($settings['s3 url']) ? $settings['s3 url'] : 'http://s3.amazonaws.com',      '#default_value' => strlen($settings['s3 url']) ? $settings['s3 url'] : 'http://s3.amazonaws.com',
48    );    );
49    
# Line 43  function fileapi_driver_s3_settings_form Line 59  function fileapi_driver_s3_settings_form
59      '#default_value' => $settings['awsSecret'],      '#default_value' => $settings['awsSecret'],
60    );    );
61    
62    
63      if (isset($settings['awsID']) && isset($settings['awsSecret'])) {
64    
65        $buckets = s3_buckets($settings);
66        debug_msg($buckets, 'buckets');
67        // use !== FALSE to pass on empty array checks.
68        if ($buckets !== FALSE) {
69          $buckets['Create a New Bucket'] = t('Create a New Bucket');
70          $form['bucket'] = array(
71            '#type' => 'select',
72            '#title' => 'Use Existing S3 Bucket',
73            '#options' => $buckets,
74            '#default_value' => (isset($settings['bucket'])) ? $settings['bucket'] : 'Create a New Bucket',
75          );
76    
77          $form['new_bucket'] = array(
78            '#type' => 'textfield',
79            '#title' => t('New S3 Bucket'),
80            '#default_value' => '',
81          );
82        }
83      }
84    
85    
86    return $form;    return $form;
87  }  }
88    
89  function fileapi_driver_s3_settings_form_validate() {  function fileapi_driver_s3_settings_form_validate($element) {
90      //@todo test for trailing slash in 's3 url';
91      if (preg_match('/\/$/', $element['s3 url']['#value'])) {
92        form_set_error('s3 url', t('The URL must not include a trailing slash.'));
93      }
94    
95    
96    
97      //@todo validate AWS credentials.
98      $settings = array(
99          's3 url' => $element['s3 url']['#value'],
100          'awsID' => $element['awsID']['#value'],
101          'awsSecret' => $element['awsSecret']['#value'],
102      );
103    
104      debug_msg($settings);
105      $result = s3_buckets($settings);
106      debug_msg(htmlspecialchars($result));
107      if ($result === FALSE)  {
108        form_set_error('awsID', t('Your AWS credentials seem to be invalid.'));
109        form_set_error('awsSecret','');
110      }
111    
112    }
113    
114    //function fileapi_driver_s3_settings_form_submit($form_id, &$form_values) {
115    function fileapi_driver_s3_settings_form_submit($element) {
116      $settings = array(
117          's3 url' => $element['s3 url']['#value'],
118          'awsID' => $element['awsID']['#value'],
119          'awsSecret' => $element['awsSecret']['#value'],
120      );
121      // @todo create a new bucket if one doesn't exist...
122      if (isset($element['awsID']['#value']) && isset($element['aws_Secret']['#value'])) {
123        if ($element['bucket']['#value'] == 'Create A New Bucket') {
124          s3_bucket_put($settings, $element['new_bucket']['#value']);
125          $element['bucket']['#value'] = $element['new_bucket']['#value'];
126          unset($element['new_bucket']['#value']);
127        }
128      }
129      else {
130        return FALSE;
131      }
132  }  }
133    
134    
135  /**  /**
136   * test if a path is a file.   * test if a path is a file.
137     *
138     * for S3 we are going to operate on an object if an object
139     * has the key we're requesting.
140     * see: S3 Developers Guide pg 67.
141     *
142   * @param $path   * @param $path
143   *   path to be tested   *   path to be tested
144     *
145   */   */
146  function fileapi_driver_s3_is_file($settings, $path) {  function fileapi_driver_s3_is_file($settings, $path) {
147      // request meta data for an object with the key $path.
148      $result = s3_request('HEAD', $settings['bucket'] .'/'. $path, $settings);
149    
150      //parse results for request...
151  }  }
152    
153  /**  /**
# Line 95  function fileapi_driver_s3_rmdir($settin Line 186  function fileapi_driver_s3_rmdir($settin
186   * @param $path   * @param $path
187   */   */
188  function fileapi_driver_s3_remove($settings, $path) {  function fileapi_driver_s3_remove($settings, $path) {
   clearstatcache();  
189    
190  }  }
191    
# Line 135  function fileapi_driver_s3_touch($settin Line 225  function fileapi_driver_s3_touch($settin
225    $headers = array();    $headers = array();
226    $headers['Content-Type'] = 'text/plain';    $headers['Content-Type'] = 'text/plain';
227    $body = file_get_contents($tmp);    $body = file_get_contents($tmp);
228    s3_request('PUT', $path, $settings, array(), $body);    $result = s3_request('PUT', $path, $settings, array(), $body);
229    unlink($tmp);    unlink($tmp);
230      if ($result) {
231        return $TRUE;
232      }
233      else {
234        return FALSE;
235      }
236  }  }
237    
238    
# Line 148  function fileapi_driver_s3_touch($settin Line 244  function fileapi_driver_s3_touch($settin
244   *   action results and array of directory contents.   *   action results and array of directory contents.
245   */   */
246  function fileapi_driver_s3_readdir($settings, $path) {  function fileapi_driver_s3_readdir($settings, $path) {
247      // $result = s3_request('GET', $settings['bucket'], $settings, array(), array(), NULL, array('prefix='. $path, 'delimiter=/');
248  }  }
249    
250  /**  /**
# Line 159  function fileapi_driver_s3_readdir($sett Line 256  function fileapi_driver_s3_readdir($sett
256   *       and our support target is 4.3.0.   *       and our support target is 4.3.0.
257   */   */
258  function fileapi_driver_s3_fileread($settings, $path) {  function fileapi_driver_s3_fileread($settings, $path) {
259    return s3_request('GET', $path, $settings);    // $result = s3_request('GET', $path, $settings);
260      return $result;
261  }  }
262    
263  function fileapi_driver_s3_filewrite($settings, $path, $data) {  function fileapi_driver_s3_filewrite($settings, $path, $data) {
264    $headers = array('Content-Type' => 'text/plain');    $headers = array('Content-Type' => 'text/plain');
265    s3_request('PUT', $path, $settings, array(), $data);    // $result = s3_request('PUT', $settings['bucket'] .'/'. $path, $settings, array(), $data);
266      return $result;
267    }
268    
269    
270    
271    function s3_buckets($settings) {
272      $result = s3_request('GET','', $settings);
273      if ($result) {
274        $buckets = array();
275        $xml = simplexml_load_string($result);
276        foreach($xml->Buckets->Bucket->Name as $Name) {
277          $name = (string)$Name;
278          $buckets[$name] = $name;
279        }
280        return $buckets;
281      }
282      return FALSE;
283    }
284    
285    /** Operations on Buckets **/
286    function s3_bucket_put($settings, $bucket = '') {
287      $result = s3_request('PUT', $bucket, $settings);
288      return $result;
289  }  }
290    
291    
292  function s3_request($method, $resource, $settings, $headers=array(), $amzheaders=array(),  $body= '') {  
293    $url = $settings['s3 url'] . $settings['bucket'];  
294    function s3_request($method, $resource, $settings, $headers=array(), $amzheaders=array(),  $body=NULL, $query = array()) {
295      $url = $settings['s3 url'];
296    
297    // Set date to proper format for S3.    // Set date to proper format for S3.
298    $headers['Date'] = gmdate(DATE_RFC822);    $headers['Date'] = gmdate(DATE_RFC822);
299    $headers['Authorization'] = 'AWS '. $settings['awsID'] .':'. $signature;    if (!is_null($body)) {
300        $headers['Content-MD5'] = hex2b64(md5($body));
301      }
302    
303    // Build S3 authorization hash.    // Build S3 authorization hash.
304    $auth_string = $method ."\n\n". $headers['Content-type'] ."\n";    $auth_string = $method ."\n";
305    $auth_string .= $headers['Date'] ."\n". implode("\n",$amzheaders) ."\n". $resource;    // Apparently we need a trailing \n whether we have a body or not...
306      if (isset($headers['Content-MD5'])) {
307        $auth_string .= $headers['Content-MD5'];
308      }
309      $auth_string .= "\n" . $headers['Content-Type'] ."\n" . $headers['Date'] ."\n";
310      // Apparently we DONT need a trailing \n here if its not present.
311      if (count($amzheaders)) {
312        $auth_string .= implode("\n",$amzheaders) ."\n";
313      }
314    
315      $auth_string .= '/'. $resource;
316    $signature = hex2b64(s3_hmac_sha1($settings['awsSecret'], $auth_string));    $signature = hex2b64(s3_hmac_sha1($settings['awsSecret'], $auth_string));
317      //print "auth_string:\n$auth_string\n";
318      //print "signature: $signature\n";
319    
320    // Set date to proper format for S3.    // Set date to proper format for S3.
321    $headers['Date'] = gmdate(DATE_RFC822);    $headers['Date'] = gmdate(DATE_RFC822);
322    $headers['Authorization'] = 'AWS '. $settings['awsID'] .':'. $signature;    $headers['Authorization'] = 'AWS '. $settings['awsID'] .':'. $signature;
323    
324      if (count($query)) {
325        $qs = '?'. implode('&', $query);
326      }
327    
328    $result = drupal_http_request($url .'/'. $resource, array_merge($headers, $amzheaders), $method, $body);    $result = drupal_http_request($url .'/'. $resource . $qs, array_merge($headers, $amzheaders), $method, $body);
329    drupal_set_message('<pre>'. print_r($result, TRUE) .'</pre>');    if ($result->code == '403') {
330        //watchdog('fileapi', t('Amazon S3 Authentication Failed'));
331        // should we log our string to sign, amazon's string to sign, and our signature here?
332        return FALSE;
333      }
334      // clean up the start and finish lines from the XML data that S3 adds for some reason.
335      $result  = split("\n", trim($result->data));
336      unset($result[0]);
337      unset($result[count($result)]);
338      $result = implode("\n", $result);
339      return $result;
340  }  }
341    
342    
343  /**  /**
344   * Generate an sha1 signed hash of a string.   * Generate an sha1 signed hash of a string.
345   * @param K   * @param K
# Line 239  function s3_hmac($h, $B, $K, $string) { Line 391  function s3_hmac($h, $B, $K, $string) {
391    return $h($opad.$string);    return $h($opad.$string);
392  }  }
393    
394    // conversion.
395    function hex2b64($str)
396    {
397        $raw = '';
398        for ($i=0; $i < strlen($str); $i+=2)
399        {
400            $raw .= chr(hexdec(substr($str, $i, 2)));
401        }
402        return base64_encode($raw);
403    }
404    

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.3.4.3

  ViewVC Help
Powered by ViewVC 1.1.2