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

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

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


Revision 1.3.4.3 - (hide annotations) (download)
Thu Jan 4 16:41:48 2007 UTC (2 years, 10 months ago) by dopry
Branch: DRUPAL-5
Changes since 1.3.4.2: +117 -16 lines
adding bucket selection/creation to s3 driver settings form.
1 dopry 1.1 <?php
2    
3     /**
4     * @file
5 dopry 1.3.4.2 * Amazon S3 storage driver for fileapi.
6 dopry 1.3.4.1 *
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 dopry 1.3.4.2 *
13     * @todo examine adding caching to save some requests to S3.
14 dopry 1.3.4.3 * @todo wrap amazon s3 functions into a single class with multiple instance and
15     * caching support.
16 dopry 1.3.4.1 *
17 dopry 1.1 */
18    
19    
20     /**
21 dopry 1.3.4.1 * driver settings form. called by fileapi.module when creating a respository using this driver.
22 dopry 1.1 * @param $settings
23 dopry 1.3.4.1 * 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 dopry 1.1 */
27    
28     function fileapi_driver_s3_settings_form($settings = array()) {
29     //debug_msg($settings);
30     $form = array(
31     '#type' => 'fieldset',
32     '#title' => t('Amazon S3 storage driver settings'),
33     '#tree' => TRUE,
34 dopry 1.3.4.3 '#validate' => array('fileapi_driver_s3_settings_form_validate' => array()),
35     '#submit' => array('fileapi_driver_s3_settings_form_submit' => array()),
36 dopry 1.1 );
37    
38     $form['driver'] = array(
39     '#type' => 'hidden',
40     '#value' => 's3',
41     );
42    
43     $form['s3 url'] = array(
44     '#type' => 'textfield',
45     '#title' => t('Amazon S3 url'),
46 dopry 1.3.4.1 '#description' => t('The URL must not contain a trailing slash.'),
47 dopry 1.1 '#default_value' => strlen($settings['s3 url']) ? $settings['s3 url'] : 'http://s3.amazonaws.com',
48     );
49    
50     $form['awsID'] = array(
51     '#type' => 'textfield',
52     '#title' => t('AWS Access Key ID'),
53     '#default_value' => $settings['awsID'],
54     );
55    
56     $form['awsSecret'] = array(
57     '#type' => 'textfield',
58     '#title' => t('AWS Secret'),
59     '#default_value' => $settings['awsSecret'],
60     );
61    
62 dopry 1.3.4.3
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 dopry 1.3.4.1
86 dopry 1.1 return $form;
87     }
88    
89 dopry 1.3.4.3 function fileapi_driver_s3_settings_form_validate($element) {
90 dopry 1.3.4.1 //@todo test for trailing slash in 's3 url';
91 dopry 1.3.4.3 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 dopry 1.3.4.1 }
113    
114 dopry 1.3.4.3 //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 dopry 1.1 }
133    
134    
135     /**
136     * test if a path is a file.
137 dopry 1.3.4.1 *
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 dopry 1.1 * @param $path
143     * path to be tested
144 dopry 1.3.4.1 *
145 dopry 1.1 */
146     function fileapi_driver_s3_is_file($settings, $path) {
147 dopry 1.3.4.1 // request meta data for an object with the key $path.
148 dopry 1.3.4.2 $result = s3_request('HEAD', $settings['bucket'] .'/'. $path, $settings);
149 dopry 1.3.4.1
150     //parse results for request...
151 dopry 1.1 }
152    
153     /**
154     * test if a path is a directory.
155     * @param $path
156     * path to be tested
157     */
158     function fileapi_driver_s3_is_dir($settings, $path) {
159     }
160    
161    
162     /**
163     * create a directory using standard php functions
164     * @param $path
165     */
166     function fileapi_driver_s3_mkdir($settings, $path) {
167 dopry 1.3 // there are technically no directories, so we can safely do
168     // nothing here.
169     return TRUE;
170 dopry 1.1 }
171    
172 dopry 1.3
173 dopry 1.1 /**
174     * remove a directory using standard php functions
175     * @param $path
176     */
177    
178     function fileapi_driver_s3_rmdir($settings, $path) {
179 dopry 1.3 // there are technically no directories.
180     // so we can test is there is a file in the 'directory' and return false here.
181 dopry 1.1 }
182    
183    
184     /**
185     * remove a file using standard php functions
186     * @param $path
187     */
188     function fileapi_driver_s3_remove($settings, $path) {
189 dopry 1.3
190 dopry 1.1 }
191    
192     /**
193     * copy a file using standard php functions
194     * @param $src
195     * @param $dst
196     */
197     function fileapi_driver_s3_copy($settings, $src, $dst) {
198 dopry 1.3
199 dopry 1.1 }
200    
201     /**
202     * move a file using standard php functions
203     * @param $src
204     * @param $dst
205     */
206     function fileapi_driver_s3_move($settings, $src, $dst) {
207 dopry 1.3
208 dopry 1.1 }
209    
210     /**
211     * check if a file exists using standard php functions
212     * @param $path
213     */
214     function fileapi_driver_s3_exists($settings, $path) {
215     }
216    
217     /**
218     * touch a path and create a file there.
219     * @param $path
220     * path to be touched.
221     */
222     function fileapi_driver_s3_touch($settings, $path) {
223     $tmp = file_directory_tmp() .'/'. basename($path);
224     touch($tmp);
225     $headers = array();
226     $headers['Content-Type'] = 'text/plain';
227     $body = file_get_contents($tmp);
228 dopry 1.3.4.2 $result = s3_request('PUT', $path, $settings, array(), $body);
229 dopry 1.3 unlink($tmp);
230 dopry 1.3.4.3 if ($result) {
231     return $TRUE;
232     }
233     else {
234     return FALSE;
235     }
236 dopry 1.1 }
237    
238    
239     /**
240     * read the contents of a directory.
241     * @param $path
242     * directory to be read.
243     * @return array
244     * action results and array of directory contents.
245     */
246     function fileapi_driver_s3_readdir($settings, $path) {
247 dopry 1.3.4.3 // $result = s3_request('GET', $settings['bucket'], $settings, array(), array(), NULL, array('prefix='. $path, 'delimiter=/');
248 dopry 1.1 }
249    
250     /**
251     * read the contents of a file.
252     * @param $path
253     * path of file to be read.
254     *
255     * NOTE: we are not including offset and maxlen since it is only supported in php > 5.1.0
256     * and our support target is 4.3.0.
257     */
258     function fileapi_driver_s3_fileread($settings, $path) {
259 dopry 1.3.4.2 // $result = s3_request('GET', $path, $settings);
260 dopry 1.3.4.1 return $result;
261 dopry 1.1 }
262    
263     function fileapi_driver_s3_filewrite($settings, $path, $data) {
264 dopry 1.3 $headers = array('Content-Type' => 'text/plain');
265 dopry 1.3.4.2 // $result = s3_request('PUT', $settings['bucket'] .'/'. $path, $settings, array(), $data);
266 dopry 1.3.4.1 return $result;
267 dopry 1.1 }
268    
269 dopry 1.3.4.3
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 dopry 1.3.4.1
293 dopry 1.3.4.3
294     function s3_request($method, $resource, $settings, $headers=array(), $amzheaders=array(), $body=NULL, $query = array()) {
295 dopry 1.3.4.1 $url = $settings['s3 url'];
296 dopry 1.3
297     // Set date to proper format for S3.
298     $headers['Date'] = gmdate(DATE_RFC822);
299 dopry 1.3.4.2 if (!is_null($body)) {
300     $headers['Content-MD5'] = hex2b64(md5($body));
301     }
302 dopry 1.3
303     // Build S3 authorization hash.
304 dopry 1.3.4.2 $auth_string = $method ."\n";
305     // 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 dopry 1.3.4.1 if (count($amzheaders)) {
312     $auth_string .= implode("\n",$amzheaders) ."\n";
313     }
314 dopry 1.3.4.3
315 dopry 1.3.4.1 $auth_string .= '/'. $resource;
316 dopry 1.3 $signature = hex2b64(s3_hmac_sha1($settings['awsSecret'], $auth_string));
317 dopry 1.3.4.3 //print "auth_string:\n$auth_string\n";
318     //print "signature: $signature\n";
319 dopry 1.3
320     // Set date to proper format for S3.
321     $headers['Date'] = gmdate(DATE_RFC822);
322     $headers['Authorization'] = 'AWS '. $settings['awsID'] .':'. $signature;
323 dopry 1.3.4.3
324     if (count($query)) {
325     $qs = '?'. implode('&', $query);
326     }
327 dopry 1.3
328 dopry 1.3.4.3 $result = drupal_http_request($url .'/'. $resource . $qs, array_merge($headers, $amzheaders), $method, $body);
329     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 dopry 1.3.4.1 return $result;
340 dopry 1.3 }
341 dopry 1.1
342 dopry 1.3.4.1
343 dopry 1.1 /**
344     * Generate an sha1 signed hash of a string.
345     * @param K
346     * key to be used for the signature.
347     * @param string
348     * string to be signed.
349     */
350     function s3_hmac_sha1($K, $string) {
351     // hash function
352     $h = 'sha1';
353     // byte length of blocks operated on by hash function.
354     $B = '64';
355     // byte length of hash function output.
356     // seems unused in the HMAC algorithm
357     $L = '20';
358     return s3_hmac($h, $B, $K, $string);
359     }
360    
361     /**
362     * HMAC implementation for drupal as per
363     * http://www.ietf.org/rfc/rfc2104.txt
364     *
365     * @param $h
366     * hashing function can be any
367     * @param $B
368     * block length iterated over by hashing algorithm
369     * @param $K
370     * key to be used for signing.
371     * @param $string
372     * string to be signed.
373     */
374     function s3_hmac($h, $B, $K, $string) {
375     // Hash the key if it exceeds the blocklength.
376     if (strlen($K) > $B) {
377     // Pack that dirty string.
378     $K = pack('H*', $h($K));
379     }
380     // pad it with 0 bytes to block length
381     $K = str_pad($K, $B, chr(0x00));
382    
383     // Setup inner and outer pads as per the RFC.
384     $ipad = str_repeat(chr(0x36), $B)^$K;
385     $opad = str_repeat(chr(0x5C), $B)^$K;
386    
387     // Do inner concat XOR hashing. Pack that dirty string.
388     $string = pack('H*', $h($ipad.$string));
389    
390     // return outer concat XOR hash
391     return $h($opad.$string);
392     }
393 dopry 1.2
394 dopry 1.3.4.1 // 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 dopry 1.2

  ViewVC Help
Powered by ViewVC 1.1.2