/[drupal]/contributions/modules/dav/dav.module
ViewVC logotype

Contents of /contributions/modules/dav/dav.module

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


Revision 1.11 - (show annotations) (download) (as text)
Mon Nov 17 16:22:31 2008 UTC (12 months, 1 week ago) by arto
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-ALPHA4, HEAD
Changes since 1.10: +2 -0 lines
File MIME type: text/x-php
Merged latest changes from http://github.com/incanus/drupal-dav (3859c2a) by Justin Miller.

Changelog (DAV API):
- Fix issue with DAV root module patch (improper variable cast).
- Added custom setting for character encoding:
  * default PEAR library encodes <space>, &, <, >
  * we now give an admin option and add | as well.
- Added MIME type icons to web listing (i.e., HTTP GET) of DAV resources.
- Fix fatal bug by replacing db_next_id(), which doesn't exist on Drupal 6.x, with db_last_insert_id().
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Provides connectivity for the WebDAV protocol, enabling other modules to
7 * export and use WebDAV resources.
8 */
9
10 //////////////////////////////////////////////////////////////////////////////
11 // Module settings
12
13 define('DAV_ROOT', variable_get('dav_root', 'dav'));
14 define('DAV_ROOT_MODULE', variable_get('dav_root_module', 'dav_fs'));
15 define('DAV_SERVER', variable_get('dav_server', FALSE));
16 define('DAV_CLIENT', variable_get('dav_client', FALSE));
17 define('DAV_DOT_FILES', variable_get('dav_dot_files', TRUE));
18 define('DAV_TRACE', variable_get('dav_trace', FALSE));
19 define('DAV_ENCODE', variable_get('dav_encode', ' &<>|'));
20 define('DAV_ICONS', variable_get('dav_icons', module_exists('file')));
21 define('DAV_CRON', variable_get('dav_cron', TRUE));
22
23 define('DAV_WINDOWS_SERVER_DISCOVERY', variable_get('dav_windows_server_discovery', TRUE));
24 define('DAV_WINDOWS_BASIC_AUTH', variable_get('dav_windows_basic_auth', TRUE));
25 define('DAV_WINDOWS_NO_THUMBS_DB', variable_get('dav_windows_no_thumbs_db', TRUE));
26 define('DAV_MACOSX_NO_DS_STORE', variable_get('dav_macosx_no_ds_store', TRUE));
27 define('DAV_MACOSX_NO_FORKS', variable_get('dav_macosx_no_forks', TRUE));
28
29 define('DAV_PEAR_SERVER', 'http://pear.php.net/package/HTTP_WebDAV_Server');
30 define('DAV_PEAR_CLIENT', 'http://pear.php.net/package/HTTP_WebDAV_Client');
31
32 define('DAV_ROOT_COLLECTION', NULL);
33 define('DAV_TRANSIENT_RESOURCE', 'dav_blob');
34
35 //////////////////////////////////////////////////////////////////////////////
36 // Core API hooks
37
38 /**
39 * Implementation of hook_help().
40 */
41 function dav_help($path, $arg = NULL) {
42 switch ($path) {
43 case 'admin/settings/dav':
44 return '<p>'. t('') .'</p>'; // TODO
45 }
46 }
47
48 /**
49 * Implementation of hook_perm().
50 */
51 function dav_perm() {
52 return array(
53 'access DAV resources',
54 'create DAV resources',
55 'rename DAV resources',
56 'move DAV resources',
57 'update DAV resources',
58 'delete DAV resources',
59 );
60 }
61
62 /**
63 * Implementation of hook_menu().
64 */
65 function dav_menu() {
66 return array(
67 // DAV endpoint
68 DAV_ROOT => array(
69 'title' => 'DAV',
70 'type' => MENU_CALLBACK,
71 'access callback' => 'is_bool',
72 'access arguments' => array(TRUE), // further access controls are enforced in the DAV server
73 'page callback' => 'dav_request',
74 ),
75 // Administer >> Site configuration >> DAV settings
76 'admin/settings/dav' => array(
77 'title' => 'DAV settings',
78 'description' => 'Configure WebDAV server settings.',
79 'access arguments' => array('administer site configuration'),
80 'page callback' => 'drupal_get_form',
81 'page arguments' => array('dav_admin_settings'),
82 'file' => 'dav.admin.inc',
83 ),
84 );
85 }
86
87 /**
88 * Implementation of hook_init().
89 */
90 function dav_init() {
91 if (DAV_CLIENT) {
92 // Register the "webdav(s)://" PHP stream wrappers (aka fopen()
93 // wrappers) using the PEAR WebDAV Client
94 _dav_load_client();
95 }
96 }
97
98 /**
99 * Implementation of hook_cron().
100 */
101 function dav_cron() {
102 if (DAV_CRON) {
103 // Purge all expired resource locks from the database
104 db_query("DELETE FROM {dav_locks} WHERE expires_at < %d", time());
105 }
106 }
107
108 /**
109 * Implementation of hook_theme()
110 */
111 function dav_theme() {
112 return array(
113 'dav_page' => array(
114 'arguments' => array('path' => NULL, 'resources' => NULL),
115 'file' => 'dav.theme.inc',
116 ),
117 'dav_resource' => array(
118 'arguments' => array('path' => NULL, 'name' => NULL, 'resource' => NULL),
119 'file' => 'dav.theme.inc',
120 ),
121 'dav_footer' => array(
122 'arguments' => array(),
123 'file' => 'dav.theme.inc',
124 ),
125 );
126 }
127
128 //////////////////////////////////////////////////////////////////////////////
129 // DAV API hooks (namespace/metadata)
130
131 /**
132 * Implementation of hook_dav_lookup().
133 */
134 function dav_dav_lookup($collection, $name) {
135 global $user;
136
137 if (_dav_is_transient_file($name)) {
138 $resources = dav_dav_list($collection);
139 return isset($resources[$name]) ? $resources[$name] : NULL;
140 }
141 }
142
143 /**
144 * Implementation of hook_dav_list().
145 */
146 function dav_dav_list($collection) {
147 global $user;
148
149 $resources = array();
150 if (($container_id = dav_intern_resource($collection, NULL, FALSE)) !== NULL) {
151 $result = db_query("SELECT * FROM {dav_resources} WHERE type = '%s' AND `key` LIKE '%s'", DAV_TRANSIENT_RESOURCE, $container_id .'#%');
152 while ($resource = db_fetch_object($result)) {
153 if (($blob = db_fetch_object(db_query("SELECT * FROM {dav_blobs} WHERE resource_id = %d AND user_id = %d", $resource->id, $user->uid)))) {
154 list(, $name) = explode('#', $resource->key, 2);
155 $resources[$name] = array(DAV_TRANSIENT_RESOURCE, $resource->key);
156 }
157 }
158 }
159 return $resources;
160 }
161
162 /**
163 * Implementation of hook_dav_propfind().
164 */
165 function dav_dav_propfind($resource) {
166 global $user;
167
168 list($type, $key) = $resource;
169 $props = array();
170 switch ($type) {
171 // Somebody has to provide the metadata for the root collection, so we
172 // might as well do it right here to save DAV modules the trouble.
173 case DAV_ROOT_COLLECTION:
174 $props['displayname'] = '/';
175 $props['creationdate'] = time();
176 $props['getlastmodified'] = time();
177 $props['resourcetype'] = 'collection';
178 $props['getcontenttype'] = 'httpd/unix-directory';
179 break;
180 case DAV_TRANSIENT_RESOURCE:
181 if (($blob = _dav_get_transient_blob($resource))) {
182 list(, $name) = explode('#', $key, 2);
183 $props['displayname'] = $name;
184 $props['creationdate'] = time();
185 $props['getlastmodified'] = time();
186 $props['resourcetype'] = '';
187 $props['getcontenttype'] = $blob->content_type;
188 $props['getcontentlength'] = $blob->content_length;
189 }
190 break;
191 default:
192 // TODO: Obtain arbitrary properties for arbitrary resources
193 break;
194 }
195 return $props;
196 }
197
198 //////////////////////////////////////////////////////////////////////////////
199 // DAV API hooks (verbs)
200
201 /**
202 * Implementation of hook_dav_get().
203 */
204 function dav_dav_get($resource, &$options) {
205 global $user;
206
207 if (_dav_is_transient_resource($resource)) {
208 if (($blob = _dav_get_transient_blob($resource))) {
209 $options['mtime'] = time();
210 $options['mimetype'] = $blob->content_type;
211 $options['size'] = $blob->content_length;
212 $options['data'] = $blob->content;
213 return $options;
214 }
215 return FALSE;
216 }
217 }
218
219 /**
220 * Implementation of hook_dav_put().
221 */
222 function dav_dav_put($container, $name, &$options, $filepath = NULL) {
223 global $user;
224
225 if (_dav_is_transient_file($name)) {
226 if (empty($filepath)) { // pre-process upload
227 return TRUE; // accept the file upload
228 }
229 else { // post-process upload
230 $resource = array(DAV_TRANSIENT_RESOURCE, dav_intern_resource($container) .'#'. $name);
231 $resource_id = dav_intern_resource($resource, 'dav');
232 return db_query("REPLACE INTO {dav_blobs} (resource_id, user_id, content_type, content_length, content) VALUES (%d, %d, '%s', %d, %b)", $resource_id, $user->uid, $options['content_type'], filesize($filepath), file_get_contents($filepath));
233 }
234 }
235 }
236
237 /**
238 * Implementation of hook_dav_delete().
239 */
240 function dav_dav_delete($resource, $container, $move = FALSE) {
241 global $user;
242
243 if (_dav_is_transient_resource($resource)) {
244 if (($resource_id = dav_intern_resource($resource, NULL, FALSE))) {
245 db_query("DELETE FROM {dav_blobs} WHERE resource_id = %d AND user_id = %d", $resource_id, $user->uid);
246 db_query("DELETE FROM {dav_resources} WHERE id = %d", $resource_id); // Hmm
247 return TRUE;
248 }
249 return FALSE;
250 }
251 }
252
253 /**
254 * Implementation of hook_dav_rename().
255 */
256 function dav_dav_rename($resource, $source_name, $target_name) {
257 global $user;
258
259 // TODO: if attempting to rename to non-transient name, we should probably
260 // perform an internal PUT request to handle it correctly
261 if (_dav_is_transient_resource($resource)) {
262 if (($resource_id = dav_intern_resource($resource, NULL, FALSE))) {
263 $key = str_replace($source_name, $target_name, $resource[1]);
264 db_query("UPDATE {dav_resources} SET `key` WHERE id = %d", $key, $resource_id);
265 return TRUE;
266 }
267 }
268 }
269
270 //////////////////////////////////////////////////////////////////////////////
271 // DAV module implementation
272
273 /**
274 * Menu callback dispatching an incoming DAV request to the
275 * HTTP_WebDAV_Server-based implementation class.
276 */
277 function dav_request() {
278 // If DAV server functionality has been disabled, we'll just spew out a
279 // 404 Not Found and pretend this never happened.
280 if (!DAV_SERVER) {
281 return drupal_not_found();
282 }
283
284 // Make sure the administrator understands that installing
285 // HTTP_WebDAV_Server really is not merely optional...
286 if (!_dav_load_server()) {
287 watchdog('dav', 'Unable to serve DAV request because the PEAR HTTP_WebDAV_Server library is not available.', array(), WATCHDOG_ERROR);
288 return drupal_access_denied();
289 }
290
291 // Make sure that at least one (other) module implements our API:
292 if (count(module_implements('dav_lookup')) < 2) {
293 watchdog('dav', 'Unable to serve DAV request because no modules implementing the DAV API are available.', array(), WATCHDOG_ERROR);
294 return drupal_access_denied();
295 }
296
297 // Calculate the request path for DAV.
298 $path = substr($_GET['q'], strlen(DAV_ROOT));
299 $path = !empty($path) ? $path : '/';
300
301 // Redirect to a URL ending in a slash if at the top level.
302 // This prevents some relative linking problems further down.
303 if ($path == '/' && !preg_match("/\/$/", $_SERVER['REQUEST_URI'])) {
304 drupal_goto(DAV_ROOT .'/', NULL, NULL, 301);
305 }
306
307 // All is green, so boot us up to DAV specs and hand over the reins to our
308 // bastard OO offspring of HTTP_WebDAV_Server, pretty much ending Drupal's
309 // active involvement in serving the request:
310 module_load_include('inc', 'dav');
311 drupal_dav_server::serve(url(DAV_ROOT), $path);
312 }
313
314 //////////////////////////////////////////////////////////////////////////////
315 // DAV core helpers
316
317 function dav_get_modules($op = NULL) {
318 switch ($op) {
319 case 'info':
320 $modules = dav_get_modules();
321 if (!empty($modules)) {
322 $result = db_query("SELECT name, info FROM {system} WHERE type = 'module' AND name IN (". db_placeholders($modules, 'varchar') .") ORDER BY weight ASC", $modules);
323 while ($row = db_fetch_object($result)) {
324 $modules[$row->name] = (object)unserialize($row->info);
325 }
326 }
327 return $modules;
328 case 'titles':
329 $modules = dav_get_modules('info');
330 foreach ($modules as $name => $info) {
331 $modules[$name] = $info->name;
332 }
333 return $modules;
334 case 'names':
335 default:
336 $modules = array_diff(module_implements('dav_list'), array('dav'));
337 return array_combine($modules, $modules);
338 }
339 }
340
341 function _dav_is_transient_file($name) {
342 return ($name[0] == '.'); // TODO
343 }
344
345 function _dav_is_transient_resource($resource) {
346 return is_array($resource) && $resource[0] == DAV_TRANSIENT_RESOURCE;
347 }
348
349 function _dav_get_transient_blob($resource) {
350 global $user;
351 if (_dav_is_transient_resource($resource)) {
352 if (($resource_id = dav_intern_resource($resource, NULL, FALSE)) !== NULL) {
353 if (($blob = db_fetch_object(db_query("SELECT * FROM {dav_blobs} WHERE resource_id = %d AND user_id = %d", $resource_id, $user->uid)))) {
354 $blob->content = db_decode_blob($blob->content);
355 return $blob;
356 }
357 }
358 }
359 }
360
361 function _dav_authenticate($name, $pass) {
362 // This is an ugly special case to support the LDAP auth module, which
363 // does not use Drupal's innate authentication API.
364 if (function_exists('ldapauth_authenticate')) {
365 // ldapauth_authenticate() sets the global $user variable if successful
366 ldapauth_authenticate(array('name' => $name, 'pass' => $pass));
367 return $GLOBALS['user'];
368 }
369 else {
370 // user_authenticate() returns a $user object
371 return user_authenticate(array('name' => $name, 'pass' => $pass));
372 }
373 }
374
375 function _dav_init_include_path() {
376 static $done = FALSE;
377 if (!$done) {
378 set_include_path(dirname(__FILE__) .'/vendor'. PATH_SEPARATOR . get_include_path());
379 $done = TRUE;
380 }
381 }
382
383 function _dav_load_server() {
384 _dav_init_include_path();
385 return (@include_once('HTTP/WebDAV/Server.php')) !== FALSE;
386 }
387
388 function _dav_load_client() {
389 _dav_init_include_path();
390 return (@include_once('HTTP/WebDAV/Client.php')) !== FALSE;
391 }

  ViewVC Help
Powered by ViewVC 1.1.2