diff --git a/inc/JpegMeta.php b/inc/JpegMeta.php
index b9f0908d43f66741ae23609d9a86fe905058b259..408f34589d6af2dd5d96823f11ebeb58c044a130 100644
--- a/inc/JpegMeta.php
+++ b/inc/JpegMeta.php
@@ -3,9 +3,10 @@
  * JPEG metadata reader/writer
  *
  * @license    PHP license 2.0 (http://www.php.net/license/2_02.txt)
- * @link       
+ * @link       http://www.zonageek.com/software/php/jpeg/index.php 
  * @author     Sebastian Delmont <sdelmont@zonageek.com>
  * @author     Andreas Gohr <andi@splitbrain.org>
+ * @todo       Add support for Maker Notes, Extend for GIF and PNG metadata
  */
 
 // This class is a modified and enhanced version of the JPEG class by
@@ -163,6 +164,40 @@ class JpegMeta
         return trim($info);
     }
 
+    /**
+     * Convinience function to set nearly all available Data
+     * through one function
+     *
+     * @author Andreas Gohr <andi@splitbrain.org>
+     */
+    function setField($field, $value)
+    {
+        if(strtolower(substr($field,0,5)) == 'iptc.'){
+            return $this->setIPTCField(substr($field,5),$value);
+        }elseif(strtolower(substr($field,0,5)) == 'exif.'){
+            return $this->setExifField(substr($field,5),$value);
+        }else{
+            return $this->setExifField($field,$value);
+        }
+    }
+
+    /**
+     * Convinience function to delete nearly all available Data
+     * through one function
+     *
+     * @author Andreas Gohr <andi@splitbrain.org>
+     */
+    function deleteField($field)
+    {
+        if(strtolower(substr($field,0,5)) == 'iptc.'){
+            return $this->deleteIPTCField(substr($field,5));
+        }elseif(strtolower(substr($field,0,5)) == 'exif.'){
+            return $this->deleteExifField(substr($field,5));
+        }else{
+            return $this->deleteExifField($field);
+        }
+    }
+
     /**
      * Return a date field
      *
@@ -770,18 +805,19 @@ class JpegMeta
      * Save changed Metadata
      *
      * @author Sebastian Delmont <sdelmont@zonageek.com>
+     * @author Andreas Gohr <andi@splitbrain.org>
      */
     function save($fileName = "") {
-      if ($fileName == "") {
-        $tmpName = $this->_fileName . ".tmp";
-        $this->_writeJPEG($tmpName);
-        if (file_exists($tmpName)) {
-          rename($tmpName, $this->_fileName);
-        }
-      }
-      else {
-        $this->_writeJPEG($fileName);
-      }
+        if ($fileName == "") {
+            $tmpName = tempnam(dirname($this->_fileName),'_metatemp_');
+            $this->_writeJPEG($tmpName);
+            if (@file_exists($tmpName)) {
+                return rename($tmpName, $this->_fileName);
+            }
+        } else {
+            return $this->_writeJPEG($fileName);
+        }
+        return false;
     }
     
     /*************************************************************/
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index 16fc1b73897faa7fccfc694ad260757ab090e623..fcc4ed696eb214e5d7cf2540974800389ea29104 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -144,6 +144,9 @@ $lang['spell_noerr'] = 'No Mistakes found';
 $lang['spell_nosug'] = 'No Suggestions';
 $lang['spell_change']= 'Change';
 
+$lang['metaedit']    = 'Edit Metadata';
+$lang['metasaveerr'] = 'Writing metadata failed';
+$lang['metasaveok']  = 'Metadata saved';
 $lang['img_backto']  = 'Back to';
 $lang['img_title']   = 'Title';
 $lang['img_caption'] = 'Caption';
diff --git a/inc/template.php b/inc/template.php
index 23123b4522362cfb1557febd92501e85e9c822b2..b48a591f8926062125112c111e6b5b8f1d2d291a 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -665,12 +665,21 @@ function tpl_mediafilelist(){
       $t = $item['meta']->getField('IPTC.Headline');
       if($t) print '<b>'.$t.'</b><br />';
 
-      $t = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment','EXIF.TIFFImageDescription','EXIF.TIFFUserComment'));
+      $t = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
+                                         'EXIF.TIFFImageDescription',
+                                         'EXIF.TIFFUserComment'));
       if($t) print $t.'<br />';
 
       $t = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category'));
       if($t) print '<i>'.$t.'</i><br />';
 
+      //add edit button
+      if($AUTH >= AUTH_UPLOAD && $item['meta']->getField('File.Mime') == 'image/jpeg'){
+        print '<a href="'.DOKU_BASE.'lib/exe/media.php?edit='.urlencode($item['id']).'">';
+        print '<img src="'.DOKU_BASE.'lib/images/edit.gif" alt="'.$lang['metaedit'].'" title="'.$lang['metaedit'].'" />';
+        print '</a>';
+      }
+
       ptln('</div>',6);
     }else{
       ptln ('('.filesize_h($item['size']).')',6);
@@ -771,7 +780,7 @@ function tpl_pagetitle($id=null){
  * and _iptcTagNames() in inc/jpeg.php (You need to prepend IPTC
  * to the names of the latter one)
  *
- * Only allowed in: detail.php
+ * Only allowed in: detail.php, mediaedit.php
  *
  * @author Andreas Gohr <andi@splitbrain.org>
  */
diff --git a/lib/exe/media.php b/lib/exe/media.php
index c8db371534d2486d3422be3cdef7bc0186a01d3d..cddf258db29d3ae6edc96bfc87525f91d6cd9d5b 100644
--- a/lib/exe/media.php
+++ b/lib/exe/media.php
@@ -14,8 +14,12 @@
 
   //get namespace to display (either direct or from deletion order)
   if($_REQUEST['delete']){
-		$DEL = cleanID($_REQUEST['delete']);
+    $DEL = cleanID($_REQUEST['delete']);
     $NS  = getNS($DEL);
+  }elseif($_REQUEST['edit']){
+    $IMG = cleanID($_REQUEST['edit']);
+    $SRC = mediaFN($IMG);
+    $NS  = getNS($IMG);
   }else{
     $NS = $_REQUEST['ns'];
     $NS = cleanID($NS);
@@ -35,9 +39,9 @@
   //handle deletion
   $mediareferences = array();
   if($DEL && $AUTH >= AUTH_DELETE){
-	if($conf['refcheck']){
-  	  search($mediareferences,$conf['datadir'],'search_reference',array('query' => $DEL));
-	}
+    if($conf['refcheck']){
+      search($mediareferences,$conf['datadir'],'search_reference',array('query' => $DEL));
+    }
     if(!count($mediareferences)){
       media_delete($DEL);
     }elseif(!$conf['refshow']){
@@ -45,6 +49,11 @@
     }
   }
 
+  //handle metadatasaving
+  if($UPLOADOK && $SRC && $_REQUEST['save']){
+    media_metasave($SRC,$_REQUEST['meta']);
+  }
+
   //handle upload
   if($_FILES['upload']['tmp_name'] && $UPLOADOK){
     media_upload($NS,$AUTH);
@@ -54,6 +63,8 @@
   header('Content-Type: text/html; charset=utf-8');
   if($conf['refshow'] && count($mediareferences)){
     include(template('mediaref.php'));
+  }elseif($IMG){
+    include(template('mediaedit.php'));
   }else{
     include(template('media.php'));
   }
@@ -87,7 +98,7 @@ function media_delete($delid){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function media_upload($NS,$AUTH){
-	require_once(DOKU_INC.'inc/confutils.php');
+  require_once(DOKU_INC.'inc/confutils.php');
   global $lang;
   global $conf;
 
@@ -101,24 +112,24 @@ function media_upload($NS,$AUTH){
   $fn   = mediaFN($id);
 
   // get filetype regexp
-	$types = array_keys(getMimeTypes());
-	$types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
+  $types = array_keys(getMimeTypes());
+  $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
   $regex = join('|',$types);
 
   // we set the umask here but this doesn't really help
   // because a temp file was created already
   umask($conf['umask']);
   if(preg_match('/\.('.$regex.')$/i',$fn)){
-		//check for overwrite
-		if(@file_exists($fn) && (!$_POST['ow'] || $AUTH < AUTH_DELETE)){
-			msg($lang['uploadexist'],0);
-			return false;
-		}
-  	// prepare directory
-  	io_makeFileDir($fn);
+    //check for overwrite
+    if(@file_exists($fn) && (!$_POST['ow'] || $AUTH < AUTH_DELETE)){
+      msg($lang['uploadexist'],0);
+      return false;
+    }
+    // prepare directory
+    io_makeFileDir($fn);
     if(move_uploaded_file($file['tmp_name'], $fn)) {
-			// set the correct permission here
-			chmod($fn, 0777 - $conf['umask']);
+      // set the correct permission here
+      chmod($fn, 0777 - $conf['umask']);
       msg($lang['uploadsucc'],1);
       return true;
     }else{
@@ -146,4 +157,31 @@ function media_html_list_namespaces($item){
   return $ret;
 }
 
+/**
+ * Saves image meta data
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function media_metasave($src,$data){
+	global $lang;
+
+  $meta = new JpegMeta($src);
+  $meta->_parseAll();
+
+	foreach($data as $key => $val){
+		$val=trim($val);
+		if(empty($val)){
+			$meta->deleteField($key);
+		}else{
+			$meta->setField($key,$val);
+		}
+	}
+
+	if($meta->save()){
+		msg($lang['metasaveok'],1);
+	}else{
+		msg($lang['metasaveerr'],-1);
+	}
+}
+
 ?>
diff --git a/lib/images/edit.gif b/lib/images/edit.gif
new file mode 100644
index 0000000000000000000000000000000000000000..a2a23de7b31c4f48a051d25272f9b24f8d7ac281
Binary files /dev/null and b/lib/images/edit.gif differ
diff --git a/lib/tpl/default/design.css b/lib/tpl/default/design.css
index 48453a736229b36ff37644f1c959e3606bedc620..ee03815ff731dad23404d8146b5635401f8b95fc 100644
--- a/lib/tpl/default/design.css
+++ b/lib/tpl/default/design.css
@@ -733,3 +733,25 @@ div.mediaselect-right li, div.uploadform {
   clear: both;
 }
 
+div.mediaedit div.data label{
+	display: block;
+  text-align: right; 
+  width: 20%;
+  float: left;
+  margin-right: 0.5em;
+}
+
+div.mediaedit div.data input, div.mediaedit div.data textarea{
+  width: 75%;
+  padding: 0.1em;
+  margin: 0.1em;
+}
+
+div.mediaedit div.data input.button {
+  width: 10em;
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+
diff --git a/lib/tpl/default/mediaedit.php b/lib/tpl/default/mediaedit.php
new file mode 100644
index 0000000000000000000000000000000000000000..53a969e1b666d195f40f73dc1b9f98ffaac2d0e6
--- /dev/null
+++ b/lib/tpl/default/mediaedit.php
@@ -0,0 +1,90 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<?php
+/**
+ * DokuWiki Default Template
+ *
+ * This is the template for editing image meta data.
+ * It is displayed in the media popup.
+ *
+ * You should leave the doctype at the very top - It should
+ * always be the very first line of a document.
+ *
+ * @link   http://wiki.splitbrain.org/wiki:tpl:templates
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $conf['lang']?>" lang="<?php echo $conf['lang']?>" dir="ltr">
+<head>
+  <title><?php echo hsc($lang['mediaselect'])?> [<?php echo hsc($conf['title'])?>]</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+  <?php tpl_metaheaders()?>
+
+  <link rel="shortcut icon" href="<?php echo DOKU_BASE?>images/favicon.ico" />
+  <link rel="stylesheet" media="screen" type="text/css" href="<?php echo DOKU_TPL?>layout.css" />
+  <link rel="stylesheet" media="screen" type="text/css" href="<?php echo DOKU_TPL?>design.css" />
+</head>
+
+<body>
+<div class="dokuwiki">
+  <?php html_msgarea()?>
+
+  <h1><?php echo hsc($lang['metaedit'])?> <code><?php echo hsc(noNS($IMG))?></code></h1>
+
+  <div class="mediaedit">
+		<?php/* everything in meta array is tried to save and read */?>
+
+ 		<div class="data">
+			<form action="<?php echo DOKU_BASE?>lib/exe/media.php" accept-charset="utf-8" method="post">
+				<input type="hidden" name="edit" value="<?php echo hsc($IMG)?>" />
+				<input type="hidden" name="save" value="1" />
+
+				<label for="title"><?php echo $lang['img_title']?></label>
+				<input type="text" name="meta[Iptc.Headline]" id="title" class="edit"
+         value="<?php echo hsc(tpl_img_getTag('IPTC.Headline'))?>" /><br />
+
+				<label for="caption"><?php echo $lang['img_caption']?></label>
+				<textarea name="meta[Iptc.Caption]" id="caption" class="edit" rows="5"><?php
+          echo hsc(tpl_img_getTag(array('IPTC.Caption',
+                                        'EXIF.UserComment',
+                                        'EXIF.TIFFImageDescription',
+                                        'EXIF.TIFFUserComment')));
+        ?></textarea><br />
+
+				<label for="artist"><?php echo $lang['img_artist']?></label>
+				<input type="text" name="meta[Iptc.Byline]" id="artist" class="edit"
+         value="<?php echo hsc(tpl_img_getTag(array('Iptc.Byline',
+                                                    'Exif.TIFFArtist',
+                                                    'Exif.Artist',
+                                                    'Iptc.Credit')))?>" /><br />
+
+				<label for="copy"><?php echo $lang['img_copyr']?></label>
+				<input type="text" name="meta[Iptc.CopyrightNotice]" id="copy" class="edit"
+         value="<?php echo hsc(tpl_img_getTag(array('Iptc.CopyrightNotice','Exif.TIFFCopyright','Exif.Copyright')))?>" /><br />
+
+
+				<label for="keywords"><?php echo $lang['img_keywords']?></label>
+        <textarea name="meta[Iptc.Keywords]" id="keywords" class="edit"><?php
+          echo hsc(tpl_img_getTag(array('IPTC.Keywords',
+                                        'EXIF.Category')));
+        ?></textarea><br />
+
+
+				<input type="submit" value="<?php echo $lang['btn_save']?>" title="ALT+S"
+         accesskey="s" class="button" />
+
+			</form>
+ 		</div>
+
+
+    <div class="footer">
+      <hr>
+        <?php tpl_button('backtomedia')?>
+    </div>
+  </div>
+
+</div>
+</body>
+</html>
+