Index: /trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java	(revision 1573)
+++ /trunk/src/org/openstreetmap/josm/actions/GpxExportAction.java	(revision 1574)
@@ -28,9 +28,9 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.gpx.GpxData;
+import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.io.GpxWriter;
-import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -107,4 +107,11 @@
         p.add(warning, GBC.eol().fill(GBC.HORIZONTAL).insets(15,0,0,0));
         addDependencies(author, authorName, email, copyright, predefined, copyrightYear, nameLabel, emailLabel, copyrightLabel, copyrightYearLabel, warning);
+        
+        // if the user name is not the email address, but the osm user name
+        // move it from the email textfield to the author textfield
+        if(!email.getText().contains("@")) {
+            authorName.setText(email.getText());
+            email.setText("");
+        }
 
         p.add(new JLabel(tr("Keywords")), GBC.eol());
@@ -133,5 +140,22 @@
         else
             gpxData = OsmDataLayer.toGpxData(Main.ds, file);
-        // TODO: add copyright, etc.
+        
+        // add author and copyright details to the gpx data
+        if(author.isSelected()) {
+            if(authorName.getText().length() > 0) {
+                gpxData.attr.put(GpxData.META_AUTHOR_NAME, authorName.getText());
+                gpxData.attr.put(GpxData.META_COPYRIGHT_AUTHOR, authorName.getText());
+            }
+            if(email.getText().length() > 0) gpxData.attr.put(GpxData.META_AUTHOR_EMAIL, email.getText());
+            if(copyright.getText().length() > 0) gpxData.attr.put(GpxData.META_COPYRIGHT_LICENSE, copyright.getText());
+            if(copyrightYear.getText().length() > 0) gpxData.attr.put(GpxData.META_COPYRIGHT_YEAR, copyrightYear.getText());
+        }
+        
+        // add the description to the gpx data
+        if(desc.getText().length() > 0) gpxData.attr.put(GpxData.META_DESC, desc.getText());
+        
+        // add keywords to the gpx data
+        if(keywords.getText().length() > 0) gpxData.attr.put(GpxData.META_KEYWORDS, keywords.getText());
+        
         try {
             FileOutputStream fo = new FileOutputStream(file);
Index: /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 1573)
+++ /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 1574)
@@ -19,4 +19,18 @@
  */
 public class GpxData extends WithAttributes {
+    
+    public static final String META_PREFIX = "meta.";
+    public static final String META_AUTHOR_NAME = META_PREFIX + "author.name"; 
+    public static final String META_AUTHOR_EMAIL = META_PREFIX + "author.email";
+    public static final String META_AUTHOR_LINK = META_PREFIX + "author.link";
+    public static final String META_COPYRIGHT_AUTHOR = META_PREFIX + "copyright.author";
+    public static final String META_COPYRIGHT_LICENSE = META_PREFIX + "copyright.license";
+    public static final String META_COPYRIGHT_YEAR = META_PREFIX + "copyright.year";
+    public static final String META_DESC = META_PREFIX + "desc";
+    public static final String META_KEYWORDS = META_PREFIX + "keywords";
+    public static final String META_LINKS = META_PREFIX + "links";
+    public static final String META_NAME = META_PREFIX + "name";
+    public static final String META_TIME = META_PREFIX + "time";
+    
     public File storageFile;
     public boolean fromServer;
@@ -37,6 +51,6 @@
             // TODO: Detect conflicts.
             String k = ent.getKey();
-            if (k.equals("link") && attr.containsKey("link")) {
-                ((Collection<GpxLink>) attr.get("link")).addAll(
+            if (k.equals(META_LINKS) && attr.containsKey(META_LINKS)) {
+                ((Collection<GpxLink>) attr.get(META_LINKS)).addAll(
                     (Collection<GpxLink>) ent.getValue());
             } else {
Index: /trunk/src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/GpxReader.java	(revision 1573)
+++ /trunk/src/org/openstreetmap/josm/io/GpxReader.java	(revision 1574)
@@ -41,5 +41,5 @@
      */
     public GpxData data;
-    public enum state { init, metadata, wpt, rte, trk, ext, author, link, trkseg }
+    public enum state { init, metadata, wpt, rte, trk, ext, author, link, trkseg, copyright}
 
     private class Parser extends DefaultHandler {
@@ -106,4 +106,6 @@
                     currentState = state.link;
                     currentLink = new GpxLink(atts.getValue("href"));
+                } else if (qName.equals("email")) {
+                    currentData.attr.put(GpxData.META_AUTHOR_EMAIL, atts.getValue("id") + "@" + atts.getValue("domain"));
                 }
                 break;
@@ -129,4 +131,12 @@
                     states.push(currentState);
                     currentState = state.ext;
+                } else if (qName.equals("copyright")) {
+                    states.push(currentState);
+                    currentState = state.copyright;
+                    currentData.attr.put(GpxData.META_COPYRIGHT_AUTHOR, atts.getValue("author"));
+                } else if (qName.equals("link")) {
+                    states.push(currentState);
+                    currentState = state.link;
+                    currentLink = new GpxLink(atts.getValue("href"));
                 }
                 break;
@@ -184,19 +194,35 @@
             switch (currentState) {
             case metadata:
-                if (qName.equals("name") || qName.equals("desc") ||
-                        qName.equals("time") || qName.equals("keywords")) {
-                    currentData.attr.put(qName, accumulator.toString());
+                if (qName.equals("name")) {
+                    currentData.attr.put(GpxData.META_NAME, accumulator.toString());
+                } else if (qName.equals("desc")) {
+                    currentData.attr.put(GpxData.META_DESC, accumulator.toString());
+                } else if (qName.equals("time")) {
+                    currentData.attr.put(GpxData.META_TIME, accumulator.toString());
+                } else if (qName.equals("keywords")) {
+                    currentData.attr.put(GpxData.META_KEYWORDS, accumulator.toString());
                 } else if (qName.equals("metadata")) {
                     currentState = states.pop();
                 }
-                //TODO: parse copyright, bounds, extensions
+                //TODO: parse bounds, extensions
                 break;
             case author:
                 if (qName.equals("author")) {
                     currentState = states.pop();
-                } else if (qName.equals("name") || qName.equals("email")) {
-                    currentData.attr.put("author" + qName, accumulator.toString());
+                } else if (qName.equals("name")) {
+                    currentData.attr.put(GpxData.META_AUTHOR_NAME, accumulator.toString());
+                } else if (qName.equals("email")) {
+                    // do nothing, has been parsed on startElement
                 } else if (qName.equals("link")) {
-                    currentData.attr.put("authorlink", currentLink);
+                    currentData.attr.put(GpxData.META_AUTHOR_LINK, currentLink);
+                }
+                break;
+            case copyright:
+                if (qName.equals("copyright")) {
+                    currentState = states.pop();
+                } else if (qName.equals("year")) {
+                    currentData.attr.put(GpxData.META_COPYRIGHT_YEAR, accumulator.toString());
+                } else if (qName.equals("license")) {
+                    currentData.attr.put(GpxData.META_COPYRIGHT_LICENSE, accumulator.toString());
                 }
                 break;
@@ -207,18 +233,14 @@
                     currentLink.type = accumulator.toString();
                 } else if (qName.equals("link")) {
-                    // <link>URL</link>
-                    if (currentLink.uri == null)
-                        currentLink.uri = accumulator.toString();
-
                     currentState = states.pop();
                 }
                 if (currentState == state.author) {
-                    currentData.attr.put("authorlink", currentLink);
-                } else if (currentState != state.link) {
+                    currentData.attr.put(GpxData.META_AUTHOR_LINK, currentLink);
+                } else if (currentState == state.metadata) {
                     Map<String, Object> attr = getAttr();
-                    if (!attr.containsKey("link")) {
-                        attr.put("link", new LinkedList<GpxLink>());
+                    if (!attr.containsKey(GpxData.META_LINKS)) {
+                        attr.put(GpxData.META_LINKS, new LinkedList<GpxLink>());
                     }
-                    ((Collection<GpxLink>) attr.get("link")).add(currentLink);
+                    ((Collection<GpxLink>) attr.get(GpxData.META_LINKS)).add(currentLink);
                 }
                 break;
Index: /trunk/src/org/openstreetmap/josm/io/GpxWriter.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/GpxWriter.java	(revision 1573)
+++ /trunk/src/org/openstreetmap/josm/io/GpxWriter.java	(revision 1574)
@@ -45,5 +45,7 @@
         this.data = data;
         out.println("<?xml version='1.0' encoding='UTF-8'?>");
-        out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\">");
+        out.println("<gpx version=\"1.1\" creator=\"JOSM GPX export\" xmlns=\"http://www.topografix.com/GPX/1/1\"\n" +
+        		"    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" + 
+        		"    xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">");
         indent = "  ";
         writeMetaData();
@@ -56,10 +58,10 @@
 
     private void writeAttr(Map<String, Object> attr) {
-        boolean hasAuthor = false;
+        // FIXME this loop is evil, because it does not assure the
+        // correct element order specified by the xml schema.
+        // for now it works, but future extension could get very complex and unmaintainable 
         for (Map.Entry<String, Object> ent : attr.entrySet()) {
             String k = ent.getKey();
-            if (k.indexOf("author") == 0) {
-                hasAuthor = true;
-            } else if (k.equals("link")) {
+            if (k.equals("link")) {
                 for (GpxLink link : (Collection<GpxLink>) ent.getValue()) {
                     gpxLink(link);
@@ -69,19 +71,53 @@
             }
         }
-
-        if (hasAuthor) {
-            open("author");
-            simpleTag("name", (String) attr.get("authorname"));
-            simpleTag("email", (String) attr.get("authoremail"));
-            gpxLink((GpxLink) attr.get("authorlink"));
+    }
+
+    private void writeMetaData() {
+        Map<String, Object> attr = data.attr;
+        openln("metadata");
+        
+        // write the description
+        if (attr.containsKey(GpxData.META_DESC)) simpleTag("desc", (String)attr.get(GpxData.META_DESC));
+        
+        // write the author details
+        if (attr.containsKey(GpxData.META_AUTHOR_NAME) 
+                || attr.containsKey(GpxData.META_AUTHOR_EMAIL)) {
+            openln("author");
+            // write the name
+            simpleTag("name", (String) attr.get(GpxData.META_AUTHOR_NAME));
+            // write the email address
+            if(attr.containsKey(GpxData.META_AUTHOR_EMAIL)) {
+                String[] tmp = ((String)attr.get(GpxData.META_AUTHOR_EMAIL)).split("@");
+                if(tmp.length == 2) {
+                    inline("email", "id=\"" + tmp[0] + "\" domain=\""+tmp[1]+"\"");
+                }
+            }
+            // write the author link
+            gpxLink((GpxLink) attr.get(GpxData.META_AUTHOR_LINK));
             closeln("author");
         }
 
-        // TODO: copyright
-    }
-
-    private void writeMetaData() {
-        openln("metadata");
-        writeAttr(data.attr);
+        // write the copyright details
+        if(attr.containsKey(GpxData.META_COPYRIGHT_LICENSE) 
+                || attr.containsKey(GpxData.META_COPYRIGHT_YEAR)) {
+            openAtt("copyright", "author=\""+ attr.get(GpxData.META_COPYRIGHT_AUTHOR) +"\"");
+            if(attr.containsKey(GpxData.META_COPYRIGHT_YEAR)) {
+                simpleTag("year", (String) attr.get(GpxData.META_COPYRIGHT_YEAR));
+            }
+            if(attr.containsKey(GpxData.META_COPYRIGHT_LICENSE)) {
+                simpleTag("license", encode((String) attr.get(GpxData.META_COPYRIGHT_LICENSE)));
+            }
+            closeln("copyright");
+        }
+        
+        // write links
+        if(attr.containsKey(GpxData.META_LINKS)) {
+            for (GpxLink link : (Collection<GpxLink>) attr.get(GpxData.META_LINKS)) {
+                gpxLink(link);
+            }
+        }
+        
+        // write keywords 
+        if (attr.containsKey(GpxData.META_KEYWORDS)) simpleTag("keywords", (String)attr.get(GpxData.META_KEYWORDS));
 
         data.recalculateBounds();
