diff --git a/_test/cases/inc/mail_isvalid.test.php b/_test/cases/inc/mail_isvalid.test.php
index 4e047499faef22ab4a008a8f4b0aabdab272655a..d8c88765e342c0babd24719baf4abee6f212b121 100644
--- a/_test/cases/inc/mail_isvalid.test.php
+++ b/_test/cases/inc/mail_isvalid.test.php
@@ -25,7 +25,8 @@ class mail_isvalid extends UnitTestCase {
         $tests[] = array('bu[g]s@php.net1',false);
         $tests[] = array('somebody@somewhere.museum',true);
         $tests[] = array('somebody@somewhere.travel',true);
-
+        $tests[] = array('root@[2010:fb:fdac::311:2101]',true);
+        $tests[] = array('test@example', true); // we allow local addresses
 
         // tests from http://code.google.com/p/php-email-address-validation/ below
 
@@ -62,7 +63,6 @@ class mail_isvalid extends UnitTestCase {
         $tests[] = array('test@.org', false);
         $tests[] = array('12345678901234567890123456789012345678901234567890123456789012345@example.com', false); // 64 characters is maximum length for local part. This is 65.
         $tests[] = array('test@123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012.com', false); // 255 characters is maximum length for domain. This is 256.
-        $tests[] = array('test@example', false);
         $tests[] = array('test@[123.123.123.123', false);
         $tests[] = array('test@123.123.123.123]', false);
 
diff --git a/_test/cases/inc/parserutils_set_metadata_during_rendering.test.php b/_test/cases/inc/parserutils_set_metadata_during_rendering.test.php
new file mode 100644
index 0000000000000000000000000000000000000000..8319da2983ae1c29183a6f9c928eb8f4eacb6713
--- /dev/null
+++ b/_test/cases/inc/parserutils_set_metadata_during_rendering.test.php
@@ -0,0 +1,93 @@
+<?php
+
+require_once DOKU_INC.'inc/init.php';
+
+class parserutils_set_metadata_during_rendering_test extends UnitTestCase {
+    // the id used for this test case
+    private $id;
+    // if the test case is currently running
+    private $active = false;
+    // the original plugin controller
+    private $plugin_controller;
+
+    // the actual test
+    function test_p_set_metadata_during_rendering() {
+        global $EVENT_HANDLER;
+        $this->id = 'test:p_set_metadata_during_rendering';
+        $this->active = true;
+
+        // write the wiki page so it exists and needs to be rendered
+        saveWikiText($this->id, 'Test '.time(), 'Test data setup');
+
+        $EVENT_HANDLER->register_hook('PARSER_METADATA_RENDER', 'BEFORE', $this, 'helper_set_metadata', array('test_before_set' => 'test'));
+        $EVENT_HANDLER->register_hook('PARSER_METADATA_RENDER', 'AFTER', $this, 'helper_set_metadata', array('test_after_set' => 'test'));
+        $EVENT_HANDLER->register_hook('PARSER_HANDLER_DONE', 'BEFORE', $this, 'helper_inject_test_instruction');
+
+        // Change the global plugin controller so this test can be a fake syntax plugin
+        global $plugin_controller;
+        $this->plugin_controller = $plugin_controller;
+        $plugin_controller = $this;
+
+        // the actual rendering, all hooks should be executed here
+        $newMeta = p_get_metadata($this->id);
+
+        // restore the plugin controller
+        $plugin_controller = $this->plugin_controller;
+
+        // assert that all three calls to p_set_metadata have been successful
+        $this->assertEqual($newMeta['test_before_set'], 'test');
+        $this->assertEqual($newMeta['test_after_set'], 'test');
+        $this->assertEqual($newMeta['test_during_rendering'], 'test');
+
+        // clean up
+        $this->active = false;
+
+        // make sure the saved metadata is the one that has been rendered
+        $this->assertEqual($newMeta, p_get_metadata($this->id));
+
+        saveWikiText($this->id, '', 'Test data remove');
+    }
+
+    // helper for the action plugin part of the test, tries executing p_set_metadata during rendering
+    function helper_set_metadata($event, $meta) {
+        if ($this->active) {
+            p_set_metadata($this->id, $meta, false, true);
+            $key = array_pop(array_keys($meta));
+            $this->assertTrue(is_string($meta[$key])); // ensure we really have a key
+            // ensure that the metadata property hasn't been set previously
+            $this->assertNotEqual($meta[$key], p_get_metadata($this->id, $key));
+        }
+    }
+
+    // helper for injecting an instruction for this test case
+    function helper_inject_test_instruction($event) {
+        if ($this->active)
+            $event->data->calls[] = array('plugin', array('parserutils_test', array()));
+    }
+
+    // fake syntax plugin rendering method that tries calling p_set_metadata during the actual rendering process
+    function render($format, &$renderer, $data) {
+        if ($this->active) {
+            $key = 'test_during_rendering';
+            p_set_metadata($this->id, array($key => 'test'), false, true);
+            // ensure that the metadata property hasn't been set previously
+            $this->assertNotEqual($key, p_get_metadata($this->id, $key));
+        }
+    }
+
+    // wrapper function for the fake plugin controller
+    function getList($type='',$all=false){
+        return $this->plugin_controller->getList();
+    }
+
+    // wrapper function for the fake plugin controller, return $this for the fake syntax of this test
+    function &load($type,$name,$new=false,$disabled=false){
+        if ($name == 'parserutils_test') {
+            return $this;
+        } else {
+            return $this->plugin_controller->load($type, $name, $new, $disabled);
+        }
+    }
+}
+
+// vim:ts=4:sw=4:et:
diff --git a/inc/EmailAddressValidator.php b/inc/EmailAddressValidator.php
index 31b34cc58adff5df8629949f9a243421d3a29f42..bb4ef0ca9f151452b98a633c3bd226c1a2334a3a 100644
--- a/inc/EmailAddressValidator.php
+++ b/inc/EmailAddressValidator.php
@@ -5,7 +5,7 @@
  * @author  Dave Child <dave@addedbytes.com>
  * @link    http://code.google.com/p/php-email-address-validation/
  * @license http://www.opensource.org/licenses/bsd-license.php
- * @version SVN r10 + Issue 15 fix
+ * @version SVN r10 + Issue 15 fix + Issue 12 fix
  */
 class EmailAddressValidator {
     /**
@@ -121,13 +121,30 @@ class EmailAddressValidator {
         if (!$this->check_text_length($strDomainPortion, 1, 255)) {
             return false;
         }
+
+        // some IPv4/v6 regexps borrowed from Feyd
+        // see: http://forums.devnetwork.net/viewtopic.php?f=38&t=53479
+        $dec_octet = '(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|[0-9])';
+        $hex_digit = '[A-Fa-f0-9]';
+        $h16 = "{$hex_digit}{1,4}";
+        $IPv4Address = "$dec_octet\\.$dec_octet\\.$dec_octet\\.$dec_octet";
+        $ls32 = "(?:$h16:$h16|$IPv4Address)";
+        $IPv6Address =
+            "(?:(?:{$IPv4Address})|(?:".
+            "(?:$h16:){6}$ls32" .
+            "|::(?:$h16:){5}$ls32" .
+            "|(?:$h16)?::(?:$h16:){4}$ls32" .
+            "|(?:(?:$h16:){0,1}$h16)?::(?:$h16:){3}$ls32" .
+            "|(?:(?:$h16:){0,2}$h16)?::(?:$h16:){2}$ls32" .
+            "|(?:(?:$h16:){0,3}$h16)?::(?:$h16:){1}$ls32" .
+            "|(?:(?:$h16:){0,4}$h16)?::$ls32" .
+            "|(?:(?:$h16:){0,5}$h16)?::$h16" .
+            "|(?:(?:$h16:){0,6}$h16)?::" .
+            ")(?:\\/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))?)";
+
         // Check if domain is IP, possibly enclosed in square brackets.
-        if (preg_match('/^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])'
-           .'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}$/'
-           ,$strDomainPortion) ||
-            preg_match('/^\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])'
-           .'(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]$/'
-           ,$strDomainPortion)) {
+        if (preg_match("/^($IPv4Address|\[$IPv4Address\]|\[$IPv6Address\])$/",
+                        $strDomainPortion)){
             return true;
         } else {
             $arrDomainPortion = explode('.', $strDomainPortion);
diff --git a/inc/HTTPClient.php b/inc/HTTPClient.php
index 1cb16714d6ede03bac090534aa00037b00803330..372769b71c30569f1fef8d0ee818c7dc2f180cbd 100644
--- a/inc/HTTPClient.php
+++ b/inc/HTTPClient.php
@@ -299,8 +299,6 @@ class HTTPClient {
                 $this->error = "Could not connect to $server:$port\n$errstr ($errno)";
                 return false;
             }
-            //set non blocking
-            stream_set_blocking($socket,0);
 
             // keep alive?
             if ($this->keep_alive) {
@@ -310,6 +308,9 @@ class HTTPClient {
             }
         }
 
+        //set blocking
+        stream_set_blocking($socket,1);
+
         // build request
         $request  = "$method $request_url HTTP/".$this->http.HTTP_NL;
         $request .= $this->_buildHeaders($headers);
@@ -319,11 +320,28 @@ class HTTPClient {
 
         $this->_debug('request',$request);
 
+        // select parameters
+        $sel_r = null;
+        $sel_w = array($socket);
+        $sel_e = null;
+
         // send request
         $towrite = strlen($request);
         $written = 0;
         while($written < $towrite){
-            $ret = fwrite($socket, substr($request,$written));
+            // check timeout
+            if(time()-$start > $this->timeout){
+                $this->status = -100;
+                $this->error = sprintf('Timeout while sending request (%.3fs)',$this->_time() - $this->start);
+                unset($this->connections[$connectionId]);
+                return false;
+            }
+
+            // wait for stream ready or timeout (1sec)
+            if(stream_select($sel_r,$sel_w,$sel_e,1) === false) continue;
+
+            // write to stream
+            $ret = fwrite($socket, substr($request,$written,4096));
             if($ret === false){
                 $this->status = -100;
                 $this->error = 'Failed writing to socket';
@@ -333,6 +351,9 @@ class HTTPClient {
             $written += $ret;
         }
 
+        // continue non-blocking
+        stream_set_blocking($socket,0);
+
         // read headers from socket
         $r_headers = '';
         do{
diff --git a/inc/common.php b/inc/common.php
index eab5f1129ba232b21f8cc3ccadf4e4c2e2c9b85a..23d9c715557b7c384e125828094a1d0c0a66562e 100644
--- a/inc/common.php
+++ b/inc/common.php
@@ -639,7 +639,7 @@ function clientIP($single=false){
     // decide which IP to use, trying to avoid local addresses
     $ip = array_reverse($ip);
     foreach($ip as $i){
-        if(preg_match('/^(127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){
+        if(preg_match('/^(::1|[fF][eE]80:|127\.|10\.|192\.168\.|172\.((1[6-9])|(2[0-9])|(3[0-1]))\.)/',$i)){
             continue;
         }else{
             return $i;
diff --git a/inc/html.php b/inc/html.php
index 4d5d557af1543c9f04fc36b16475c7840ea9b34c..3afa4862f19164cf174077dc772137ace3515c96 100644
--- a/inc/html.php
+++ b/inc/html.php
@@ -1440,10 +1440,10 @@ function html_admin(){
     }
 
     // data security check
-    echo '<a style="background: transparent url(data/security.png) left top no-repeat;
-                  display: block; width:380px; height:73px; border:none; float:right"
-           target="_blank"
-           href="http://www.dokuwiki.org/security#web_access_security"></a>';
+    // @todo: could be checked and only displayed if $conf['savedir'] is under the web root
+    echo '<a style="border:none; float:right;" target="_blank"
+            href="http://www.dokuwiki.org/security#web_access_security">
+            <img src="data/security.png" alt="Broken image? Everything\'s alright!" /></a>';
 
     print p_locale_xhtml('admin');
 
diff --git a/inc/media.php b/inc/media.php
index 69441352be92d17c5a09df502827c03b7d6f94b7..3c9340d51cf24de70901feb803ca6a98ccf8d74d 100644
--- a/inc/media.php
+++ b/inc/media.php
@@ -141,7 +141,7 @@ function media_metaform($id,$auth){
 }
 
 /**
- * Conveinience function to check if a media file is still in use
+ * Convenience function to check if a media file is still in use
  *
  * @author Michael Klier <chi@chimeric.de>
  */
@@ -160,19 +160,26 @@ function media_inuse($id) {
     }
 }
 
+define('DOKU_MEDIA_DELETED', 1);
+define('DOKU_MEDIA_NOT_AUTH', 2);
+define('DOKU_MEDIA_INUSE', 4);
+define('DOKU_MEDIA_EMPTY_NS', 8);
+
 /**
  * Handles media file deletions
  *
  * If configured, checks for media references before deletion
  *
  * @author Andreas Gohr <andi@splitbrain.org>
- * @return mixed false on error, true on delete or array with refs
+ * @return int One of: 0,
+                       DOKU_MEDIA_DELETED,
+                       DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS,
+                       DOKU_MEDIA_NOT_AUTH,
+                       DOKU_MEDIA_INUSE
  */
 function media_delete($id,$auth){
-    if($auth < AUTH_DELETE) return false;
-    if(!checkSecurityToken()) return false;
-    global $conf;
-    global $lang;
+    if($auth < AUTH_DELETE) return DOKU_MEDIA_NOT_AUTH;
+    if(media_inuse($id)) return DOKU_MEDIA_INUSE;
 
     $file = mediaFN($id);
 
@@ -196,38 +203,22 @@ function media_delete($id,$auth){
     unset($evt);
 
     if($data['unl'] && $data['del']){
-        // current namespace was removed. redirecting to root ns passing msg along
-        send_redirect(DOKU_URL.'lib/exe/mediamanager.php?msg1='.
-                rawurlencode(sprintf(noNS($id),$lang['deletesucc'])));
+        return DOKU_MEDIA_DELETED | DOKU_MEDIA_EMPTY_NS;
     }
 
-    return $data['unl'];
+    return $data['unl'] ? DOKU_MEDIA_DELETED : 0;
 }
 
 /**
  * Handles media file uploads
  *
- * This generates an action event and delegates to _media_upload_action().
- * Action plugins are allowed to pre/postprocess the uploaded file.
- * (The triggered event is preventable.)
- *
- * Event data:
- * $data[0]     fn_tmp: the temporary file name (read from $_FILES)
- * $data[1]     fn: the file name of the uploaded file
- * $data[2]     id: the future directory id of the uploaded file
- * $data[3]     imime: the mimetype of the uploaded file
- * $data[4]     overwrite: if an existing file is going to be overwritten
- *
- * @triggers MEDIA_UPLOAD_FINISH
  * @author Andreas Gohr <andi@splitbrain.org>
  * @author Michael Klier <chi@chimeric.de>
  * @return mixed false on error, id of the new file on success
  */
 function media_upload($ns,$auth){
-    if($auth < AUTH_UPLOAD) return false;
     if(!checkSecurityToken()) return false;
     global $lang;
-    global $conf;
 
     // get file and id
     $id   = $_POST['id'];
@@ -249,8 +240,50 @@ function media_upload($ns,$auth){
         msg(sprintf($lang['mediaextchange'],$fext,$iext));
     }
 
+    $res = media_save(array('name' => $file['tmp_name'],
+                            'mime' => $imime,
+                            'ext'  => $iext), $ns.':'.$id,
+                      $_REQUEST['ow'], $auth, 'move_uploaded_file');
+    if (is_array($res)) {
+        msg($res[0], $res[1]);
+        return false;
+    }
+    return $res;
+}
+
+/**
+ * This generates an action event and delegates to _media_upload_action().
+ * Action plugins are allowed to pre/postprocess the uploaded file.
+ * (The triggered event is preventable.)
+ *
+ * Event data:
+ * $data[0]     fn_tmp: the temporary file name (read from $_FILES)
+ * $data[1]     fn: the file name of the uploaded file
+ * $data[2]     id: the future directory id of the uploaded file
+ * $data[3]     imime: the mimetype of the uploaded file
+ * $data[4]     overwrite: if an existing file is going to be overwritten
+ *
+ * @triggers MEDIA_UPLOAD_FINISH
+ */
+function media_save($file, $id, $ow, $auth, $move) {
+    if($auth < AUTH_UPLOAD) {
+        return array("You don't have permissions to upload files.", -1);
+    }
+
+    if (!isset($file['mime']) || !isset($file['ext'])) {
+        list($ext, $mime) = mimetype($id);
+        if (!isset($file['mime'])) {
+            $file['mime'] = $mime;
+        }
+        if (!isset($file['ext'])) {
+            $file['ext'] = $ext;
+        }
+    }
+
+    global $lang;
+
     // get filename
-    $id   = cleanID($ns.':'.$id,false,true);
+    $id   = cleanID($id,false,true);
     $fn   = mediaFN($id);
 
     // get filetype regexp
@@ -259,40 +292,35 @@ function media_upload($ns,$auth){
     $regex = join('|',$types);
 
     // because a temp file was created already
-    if(preg_match('/\.('.$regex.')$/i',$fn)){
-        //check for overwrite
-        $overwrite = @file_exists($fn);
-        if($overwrite && (!$_REQUEST['ow'] || $auth < AUTH_DELETE)){
-            msg($lang['uploadexist'],0);
-            return false;
-        }
-        // check for valid content
-        $ok = media_contentcheck($file['tmp_name'],$imime);
-        if($ok == -1){
-            msg(sprintf($lang['uploadbadcontent'],".$iext"),-1);
-            return false;
-        }elseif($ok == -2){
-            msg($lang['uploadspam'],-1);
-            return false;
-        }elseif($ok == -3){
-            msg($lang['uploadxss'],-1);
-            return false;
-        }
+    if(!preg_match('/\.('.$regex.')$/i',$fn)) {
+        return array($lang['uploadwrong'],-1);
+    }
 
-        // prepare event data
-        $data[0] = $file['tmp_name'];
-        $data[1] = $fn;
-        $data[2] = $id;
-        $data[3] = $imime;
-        $data[4] = $overwrite;
+    //check for overwrite
+    $overwrite = @file_exists($fn);
+    if($overwrite && (!$ow || $auth < AUTH_DELETE)) {
+        return array($lang['uploadexist'], 0);
+    }
+    // check for valid content
+    $ok = media_contentcheck($file['name'], $file['mime']);
+    if($ok == -1){
+        return array(sprintf($lang['uploadbadcontent'],'.' . $file['ext']),-1);
+    }elseif($ok == -2){
+        return array($lang['uploadspam'],-1);
+    }elseif($ok == -3){
+        return array($lang['uploadxss'],-1);
+    }
 
-        // trigger event
-        return trigger_event('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
+    // prepare event data
+    $data[0] = $file['name'];
+    $data[1] = $fn;
+    $data[2] = $id;
+    $data[3] = $file['mime'];
+    $data[4] = $overwrite;
+    $data[5] = $move;
 
-    }else{
-        msg($lang['uploadwrong'],-1);
-    }
-    return false;
+    // trigger event
+    return trigger_event('MEDIA_UPLOAD_FINISH', $data, '_media_upload_action', true);
 }
 
 /**
@@ -301,8 +329,8 @@ function media_upload($ns,$auth){
  */
 function _media_upload_action($data) {
     // fixme do further sanity tests of given data?
-    if(is_array($data) && count($data)===5) {
-        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4]);
+    if(is_array($data) && count($data)===6) {
+        return media_upload_finish($data[0], $data[1], $data[2], $data[3], $data[4], $data[5]);
     } else {
         return false; //callback error
     }
@@ -314,14 +342,14 @@ function _media_upload_action($data) {
  * @author Andreas Gohr <andi@splitbrain.org>
  * @author Michael Klier <chi@chimeric.de>
  */
-function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite) {
+function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite, $move = 'move_uploaded_file') {
     global $conf;
     global $lang;
 
     // prepare directory
     io_createNamespace($id, 'media');
 
-    if(move_uploaded_file($fn_tmp, $fn)) {
+    if($move($fn_tmp, $fn)) {
         // Set the correct permission here.
         // Always chmod media because they may be saved with different permissions than expected from the php umask.
         // (Should normally chmod to $conf['fperm'] only if $conf['fperm'] is set.)
@@ -336,7 +364,7 @@ function media_upload_finish($fn_tmp, $fn, $id, $imime, $overwrite) {
         }
         return $id;
     }else{
-        msg($lang['uploadfail'],-1);
+        return array($lang['uploadfail'],-1);
     }
 }
 
diff --git a/inc/parserutils.php b/inc/parserutils.php
index 6e349e9841e425905d0b61e54039ec02f875c18f..9b2d99328fe92b9f97f7e27f82f475e3fa67a1e6 100644
--- a/inc/parserutils.php
+++ b/inc/parserutils.php
@@ -258,7 +258,7 @@ function p_get_metadata($id, $key='', $render=true){
             if ($meta == $old_meta || p_save_metadata($id, $meta)) {
                 // store a timestamp in order to make sure that the cachefile is touched
                 $cachefile->storeCache(time());
-            } else {
+            } elseif ($meta != $old_meta) {
                 msg('Unable to save metadata file. Hint: disk full; file permissions; safe_mode setting.',-1);
             }
         }
@@ -291,18 +291,25 @@ function p_get_metadata($id, $key='', $render=true){
  * @return boolean true on success
  *
  * @author Esther Brunner <esther@kaffeehaus.ch>
+ * @author Michael Hamann <michael@content-space.de>
  */
 function p_set_metadata($id, $data, $render=false, $persistent=true){
     if (!is_array($data)) return false;
 
-    global $ID;
+    global $ID, $METADATA_RENDERERS;
 
-    // cache the current page
-    $cache = ($ID == $id);
-    $orig = p_read_metadata($id, $cache);
+    // if there is currently a renderer change the data in the renderer instead
+    if (isset($METADATA_RENDERERS[$id])) {
+        $orig =& $METADATA_RENDERERS[$id];
+        $meta = $orig;
+    } else {
+        // cache the current page
+        $cache = ($ID == $id);
+        $orig = p_read_metadata($id, $cache);
 
-    // render metadata first?
-    $meta = $render ? p_render_metadata($id, $orig) : $orig;
+        // render metadata first?
+        $meta = $render ? p_render_metadata($id, $orig) : $orig;
+    }
 
     // now add the passed metadata
     $protected = array('description', 'date', 'contributor');
@@ -339,7 +346,13 @@ function p_set_metadata($id, $data, $render=false, $persistent=true){
     // save only if metadata changed
     if ($meta == $orig) return true;
 
-    return p_save_metadata($id, $meta);
+    if (isset($METADATA_RENDERERS[$id])) {
+        // set both keys individually as the renderer has references to the individual keys
+        $METADATA_RENDERERS[$id]['current']    = $meta['current'];
+        $METADATA_RENDERERS[$id]['persistent'] = $meta['persistent'];
+    } else {
+        return p_save_metadata($id, $meta);
+    }
 }
 
 /**
@@ -413,7 +426,15 @@ function p_save_metadata($id, $meta) {
  */
 function p_render_metadata($id, $orig){
     // make sure the correct ID is in global ID
-    global $ID;
+    global $ID, $METADATA_RENDERERS;
+
+    // avoid recursive rendering processes for the same id
+    if (isset($METADATA_RENDERERS[$id]))
+        return $orig;
+
+    // store the original metadata in the global $METADATA_RENDERERS so p_set_metadata can use it
+    $METADATA_RENDERERS[$id] =& $orig;
+
     $keep = $ID;
     $ID   = $id;
 
@@ -428,13 +449,14 @@ function p_render_metadata($id, $orig){
         $instructions = p_cached_instructions(wikiFN($id),false,$id);
         if(is_null($instructions)){
             $ID = $keep;
+            unset($METADATA_RENDERERS[$id]);
             return null; // something went wrong with the instructions
         }
 
         // set up the renderer
         $renderer = new Doku_Renderer_metadata();
-        $renderer->meta = $orig['current'];
-        $renderer->persistent = $orig['persistent'];
+        $renderer->meta =& $orig['current'];
+        $renderer->persistent =& $orig['persistent'];
 
         // loop through the instructions
         foreach ($instructions as $instruction){
@@ -442,11 +464,13 @@ function p_render_metadata($id, $orig){
             call_user_func_array(array(&$renderer, $instruction[0]), (array) $instruction[1]);
         }
 
-        $evt->result = array('current'=>$renderer->meta,'persistent'=>$renderer->persistent);
+        $evt->result = array('current'=>&$renderer->meta,'persistent'=>&$renderer->persistent);
     }
     $evt->advise_after();
 
+    // clean up
     $ID = $keep;
+    unset($METADATA_RENDERERS[$id]);
     return $evt->result;
 }
 
diff --git a/lib/exe/indexer.php b/lib/exe/indexer.php
index eec8c968cdc8b08eeef779ec98af3e88886de20b..010ca798704a7894d0f6093aff6c505558129c18 100644
--- a/lib/exe/indexer.php
+++ b/lib/exe/indexer.php
@@ -149,6 +149,7 @@ function runIndexer(){
     }
 
     // try to aquire a lock
+    $run = 0;
     $lock = $conf['lockdir'].'/_indexer.lock';
     while(!@mkdir($lock,$conf['dmode'])){
         usleep(50);
@@ -156,7 +157,8 @@ function runIndexer(){
             // looks like a stale lock - remove it
             @rmdir($lock);
             print "runIndexer(): stale lock removed".NL;
-        }else{
+        }elseif($run++ = 1000){
+            // we waited 5 seconds for that lock
             print "runIndexer(): indexer locked".NL;
             return false;
         }
diff --git a/lib/exe/mediamanager.php b/lib/exe/mediamanager.php
index c79a25c084dc9f2a7302bc27a43615f2fc6bb440..6f2add2bedcf8ce237f31e36b7346c4553844f79 100644
--- a/lib/exe/mediamanager.php
+++ b/lib/exe/mediamanager.php
@@ -82,18 +82,24 @@
 
     // handle deletion
     if($DEL) {
-        $INUSE = media_inuse($DEL);
-        if(!$INUSE) {
-            if(media_delete($DEL,$AUTH)) {
-                msg(sprintf($lang['deletesucc'],noNS($DEL)),1);
-            } else {
-                msg(sprintf($lang['deletefail'],noNS($DEL)),-1);
+        $res = 0;
+        if(checkSecurityToken()) {
+            $res = media_delete($DEL,$AUTH);
+        }
+        if ($res & DOKU_MEDIA_DELETED) {
+            $msg = sprintf($lang['deletesucc'], noNS($DEL));
+            if ($res & DOKU_MEDIA_EMPTY_NS) {
+                // current namespace was removed. redirecting to root ns passing msg along
+                send_redirect(DOKU_URL.'lib/exe/mediamanager.php?msg1='.
+                        rawurlencode($msg));
             }
-        } else {
+            msg($msg,1);
+        } elseif ($res & DOKU_MEDIA_INUSE) {
             if(!$conf['refshow']) {
-                unset($INUSE);
                 msg(sprintf($lang['mediainuse'],noNS($DEL)),0);
             }
+        } else {
+            msg(sprintf($lang['deletefail'],noNS($DEL)),-1);
         }
     }
 
diff --git a/lib/exe/xmlrpc.php b/lib/exe/xmlrpc.php
index d40e338b2046abeb1c7ad6d04576bb9500c66fc1..9749a2e16259c6496d9d55dd159418a662352c68 100644
--- a/lib/exe/xmlrpc.php
+++ b/lib/exe/xmlrpc.php
@@ -605,64 +605,26 @@ class dokuwiki_xmlrpc_server extends IXR_IntrospectionServer {
      */
     function putAttachment($id, $file, $params) {
         $id = cleanID($id);
-        global $conf;
-        global $lang;
-
         $auth = auth_quickaclcheck(getNS($id).':*');
-        if($auth >= AUTH_UPLOAD) {
-            if(!isset($id)) {
-                return new IXR_ERROR(1, 'Filename not given.');
-            }
 
-            $ftmp = $conf['tmpdir'] . '/' . md5($id.clientIP());
-
-            // save temporary file
-            @unlink($ftmp);
-            $buff = base64_decode($file);
-            io_saveFile($ftmp, $buff);
+        if(!isset($id)) {
+            return new IXR_ERROR(1, 'Filename not given.');
+        }
 
-            // get filename
-            list($iext, $imime,$dl) = mimetype($id);
-            $id = cleanID($id);
-            $fn = mediaFN($id);
-
-            // get filetype regexp
-            $types = array_keys(getMimeTypes());
-            $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
-            $regex = join('|',$types);
-
-            // because a temp file was created already
-            if(preg_match('/\.('.$regex.')$/i',$fn)) {
-                //check for overwrite
-                $overwrite = @file_exists($fn);
-                if($overwrite && (!$params['ow'] || $auth < AUTH_DELETE)) {
-                    return new IXR_ERROR(1, $lang['uploadexist'].'1');
-                }
-                // check for valid content
-                $ok = media_contentcheck($ftmp, $imime);
-                if($ok == -1) {
-                    return new IXR_ERROR(1, sprintf($lang['uploadexist'].'2', ".$iext"));
-                } elseif($ok == -2) {
-                    return new IXR_ERROR(1, $lang['uploadspam']);
-                } elseif($ok == -3) {
-                    return new IXR_ERROR(1, $lang['uploadxss']);
-                }
+        global $conf;
 
-                // prepare event data
-                $data[0] = $ftmp;
-                $data[1] = $fn;
-                $data[2] = $id;
-                $data[3] = $imime;
-                $data[4] = $overwrite;
+        $ftmp = $conf['tmpdir'] . '/' . md5($id.clientIP());
 
-                // trigger event
-                return trigger_event('MEDIA_UPLOAD_FINISH', $data, array($this, '_media_upload_action'), true);
+        // save temporary file
+        @unlink($ftmp);
+        $buff = base64_decode($file);
+        io_saveFile($ftmp, $buff);
 
-            } else {
-                return new IXR_ERROR(1, $lang['uploadwrong']);
-            }
+        $res = media_save(array('name' => $ftmp), $id, $params['ow'], $auth, 'rename');
+        if (is_array($res)) {
+            return new IXR_ERROR(-$res[1], $res[0]);
         } else {
-            return new IXR_ERROR(1, "You don't have permissions to upload files.");
+            return $res;
         }
     }
 
@@ -674,55 +636,15 @@ class dokuwiki_xmlrpc_server extends IXR_IntrospectionServer {
     function deleteAttachment($id){
         $id = cleanID($id);
         $auth = auth_quickaclcheck(getNS($id).':*');
-        if($auth < AUTH_DELETE) return new IXR_ERROR(1, "You don't have permissions to delete files.");
-        global $conf;
-        global $lang;
-
-        // check for references if needed
-        $mediareferences = array();
-        if($conf['refcheck']){
-            $mediareferences = ft_mediause($id,$conf['refshow']);
-        }
-
-        if(!count($mediareferences)){
-            $file = mediaFN($id);
-            if(@unlink($file)){
-                addMediaLogEntry(time(), $id, DOKU_CHANGE_TYPE_DELETE);
-                io_sweepNS($id,'mediadir');
-                return 0;
-            }
-            //something went wrong
-               return new IXR_ERROR(1, 'Could not delete file');
-        } else {
+        $res = media_delete($id, $auth);
+        if ($res & DOKU_MEDIA_DELETED) {
+            return 0;
+        } elseif ($res & DOKU_MEDIA_NOT_AUTH) {
+            return new IXR_ERROR(1, "You don't have permissions to delete files.");
+        } elseif ($res & DOKU_MEDIA_INUSE) {
             return new IXR_ERROR(1, 'File is still referenced');
-        }
-    }
-
-    /**
-     * Moves the temporary file to its final destination.
-     *
-     * Michael Klier <chi@chimeric.de>
-     */
-    function _media_upload_action($data) {
-        global $conf;
-
-        if(is_array($data) && count($data)===5) {
-            io_createNamespace($data[2], 'media');
-            if(rename($data[0], $data[1])) {
-                chmod($data[1], $conf['fmode']);
-                media_notify($data[2], $data[1], $data[3]);
-                // add a log entry to the media changelog
-                if ($data[4]) {
-                    addMediaLogEntry(time(), $data[2], DOKU_CHANGE_TYPE_EDIT);
-                } else {
-                    addMediaLogEntry(time(), $data[2], DOKU_CHANGE_TYPE_CREATE);
-                }
-                return $data[2];
-            } else {
-                return new IXR_ERROR(1, 'Upload failed.');
-            }
         } else {
-            return new IXR_ERROR(1, 'Upload failed.');
+            return new IXR_ERROR(1, 'Could not delete file');
         }
     }
 
diff --git a/lib/plugins/plugin/classes/ap_download.class.php b/lib/plugins/plugin/classes/ap_download.class.php
index beba0ab07a8c489440b2c8bb66ffdbfe07bdc061..784095aaf74788684124b12c7cce1c3fd467d901 100644
--- a/lib/plugins/plugin/classes/ap_download.class.php
+++ b/lib/plugins/plugin/classes/ap_download.class.php
@@ -1,7 +1,7 @@
 <?php
 class ap_download extends ap_manage {
 
-    var $overwrite = false;
+    var $overwrite = true;
 
     /**
      * Initiate the plugin download
diff --git a/lib/plugins/plugin/classes/ap_manage.class.php b/lib/plugins/plugin/classes/ap_manage.class.php
index 2982a3ebb0f5bd9d3e6be0771a36abc505eba238..fb148f2633e96c03e61a6091d9fa9d79417b211f 100644
--- a/lib/plugins/plugin/classes/ap_manage.class.php
+++ b/lib/plugins/plugin/classes/ap_manage.class.php
@@ -176,7 +176,7 @@ class ap_manage {
     function dir_delete($path) {
         if (!is_string($path) || $path == "") return false;
 
-        if (is_dir($path)) {
+        if (is_dir($path) && !is_link($path)) {
             if (!$dh = @opendir($path)) return false;
 
             while ($f = readdir($dh)) {