diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DateElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DateElement.java
new file mode 100644
index 000000000..2a13543b0
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DateElement.java
@@ -0,0 +1,77 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.file_metadata.element.child;
+
+import java.util.Date;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.packet.XmlEnvironment;
+import org.jivesoftware.smack.util.EqualsUtil;
+import org.jivesoftware.smack.util.HashCode;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+import org.jxmpp.util.XmppDateTime;
+
+/**
+ * Timestamp specifying the last modified time of the file.
+ */
+public class DateElement implements NamedElement {
+
+ public static final String ELEMENT = "date";
+
+ private final Date date;
+
+ public DateElement(Date date) {
+ this.date = date;
+ }
+
+ /**
+ * Return the last modification date of the file.
+ *
+ * @return last modification date
+ */
+ public Date getDate() {
+ return date;
+ }
+
+ @Override
+ public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
+ XmlStringBuilder sb = new XmlStringBuilder(this).rightAngleBracket();
+ sb.append(XmppDateTime.formatXEP0082Date(getDate()));
+ return sb.closeElement(this);
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCode.builder()
+ .append(getElementName())
+ .append(getDate())
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsUtil.equals(this, obj, (equalsBuilder, other) ->
+ equalsBuilder.append(getElementName(), other.getElementName())
+ .append(getDate(), other.getDate()));
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DescElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DescElement.java
new file mode 100644
index 000000000..5fb426b5f
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DescElement.java
@@ -0,0 +1,94 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.file_metadata.element.child;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.packet.XmlEnvironment;
+import org.jivesoftware.smack.util.EqualsUtil;
+import org.jivesoftware.smack.util.HashCode;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * A human readable description of the file. Multiple {@link DescElement DescElements} MAY be included
+ * if different xml:lang values are specified.
+ */
+public class DescElement implements NamedElement {
+
+ public static final String ELEMENT = "desc";
+
+ private final String description;
+ private final String lang;
+
+ public DescElement(String description) {
+ this(description, null);
+ }
+
+ public DescElement(String description, String lang) {
+ this.description = StringUtils.requireNotNullNorEmpty(description, "Description MUST NOT be null nor empty");
+ this.lang = lang;
+ }
+
+ /**
+ * Return the description of the file.
+ *
+ * @return description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Return the language of the description or null.
+ *
+ * @return language or null
+ */
+ public String getLanguage() {
+ return lang;
+ }
+
+ @Override
+ public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
+ return new XmlStringBuilder(this)
+ .optXmlLangAttribute(getLanguage())
+ .rightAngleBracket()
+ .append(getDescription())
+ .closeElement(this);
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCode.builder()
+ .append(getElementName())
+ .append(getLanguage())
+ .append(getDescription())
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsUtil.equals(this, obj, (equalsBuilder, other) ->
+ equalsBuilder.append(getElementName(), other.getElementName())
+ .append(getLanguage(), other.getLanguage())
+ .append(getDescription(), other.getDescription()));
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DimensionsElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DimensionsElement.java
new file mode 100644
index 000000000..1cdb92ee2
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/DimensionsElement.java
@@ -0,0 +1,75 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.file_metadata.element.child;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.packet.XmlEnvironment;
+import org.jivesoftware.smack.util.EqualsUtil;
+import org.jivesoftware.smack.util.HashCode;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * Horizontal and vertical dimensions of image or video files, in pixels.
+ */
+public class DimensionsElement implements NamedElement {
+
+ public static final String ELEMENT = "dimensions";
+
+ private final String dimensions;
+
+ public DimensionsElement(String dimensions) {
+ this.dimensions = StringUtils.requireNotNullNorEmpty(dimensions, "Dimensions MUST NOT be null nor empty.");
+ }
+
+ /**
+ * Return the dimensions of the file in width by height (eg. 1920x1080).
+ *
+ * @return dimensions
+ */
+ public String getDimensions() {
+ return dimensions;
+ }
+
+ @Override
+ public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
+ return new XmlStringBuilder(this)
+ .rightAngleBracket()
+ .append(getDimensions())
+ .closeElement(this);
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCode.builder()
+ .append(getElementName())
+ .append(getDimensions())
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsUtil.equals(this, obj, (equalsBuilder, other) ->
+ equalsBuilder.append(getElementName(), other.getElementName())
+ .append(getDimensions(), other.getDimensions()));
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/LengthElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/LengthElement.java
new file mode 100644
index 000000000..6c29618e9
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/LengthElement.java
@@ -0,0 +1,77 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.file_metadata.element.child;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.packet.XmlEnvironment;
+import org.jivesoftware.smack.util.EqualsUtil;
+import org.jivesoftware.smack.util.HashCode;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * Length of an audio or video file, in milliseconds.
+ */
+public class LengthElement implements NamedElement {
+
+ public static final String ELEMENT = "length";
+
+ private final long length;
+
+ public LengthElement(long length) {
+ if (length < 0) {
+ throw new IllegalArgumentException("Length cannot be negative.");
+ }
+ this.length = length;
+ }
+
+ /**
+ * Return the length of the audio or video file.
+ *
+ * @return length in milliseconds
+ */
+ public long getLength() {
+ return length;
+ }
+
+ @Override
+ public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
+ return new XmlStringBuilder(this)
+ .rightAngleBracket()
+ .append(Long.toString(getLength()))
+ .closeElement(this);
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCode.builder()
+ .append(getElementName())
+ .append(getLength())
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsUtil.equals(this, obj, (equalsBuilder, other) ->
+ equalsBuilder.append(getElementName(), other.getElementName())
+ .append(getLength(), other.getLength()));
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/MediaTypeElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/MediaTypeElement.java
new file mode 100644
index 000000000..14ecc76a0
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/MediaTypeElement.java
@@ -0,0 +1,78 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.file_metadata.element.child;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.packet.XmlEnvironment;
+import org.jivesoftware.smack.util.EqualsUtil;
+import org.jivesoftware.smack.util.HashCode;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * The media type of the file content, which SHOULD be a valid MIME-TYPE as registered with the
+ * Internet Assigned Numbers Authority (IANA).
+ *
+ * @see Media Types
+ */
+public class MediaTypeElement implements NamedElement {
+
+ public static final String ELEMENT = "media-type";
+
+ private final String mediaType;
+
+ public MediaTypeElement(String mediaType) {
+ this.mediaType = StringUtils.requireNotNullNorEmpty(mediaType, "Media-Type MUST NOT be null nor empty");
+ }
+
+ /**
+ * Return the media type of the file.
+ *
+ * @return media type
+ */
+ public String getMediaType() {
+ return mediaType;
+ }
+
+ @Override
+ public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
+ return new XmlStringBuilder(this)
+ .rightAngleBracket()
+ .append(getMediaType())
+ .closeElement(this);
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCode.builder()
+ .append(getElementName())
+ .append(getMediaType())
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsUtil.equals(this, obj, (equalsBuilder, other) ->
+ equalsBuilder.append(getElementName(), other.getElementName())
+ .append(getMediaType(), other.getMediaType()));
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/NameElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/NameElement.java
new file mode 100644
index 000000000..7a89be18a
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/NameElement.java
@@ -0,0 +1,94 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.file_metadata.element.child;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.packet.XmlEnvironment;
+import org.jivesoftware.smack.util.EqualsUtil;
+import org.jivesoftware.smack.util.HashCode;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * The name of the file.
+ */
+public class NameElement implements NamedElement {
+
+ public static final String ELEMENT = "name";
+
+ private final String name;
+
+ public NameElement(String name) {
+ this.name = StringUtils.requireNotNullNorEmpty(name, "Name MUST NOT be null nor empty");
+ }
+
+ /**
+ * Return the name of the file.
+ *
+ * @return escaped name
+ */
+ public String getName() {
+ try {
+ return URLEncoder.encode(name, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError(e); // UTF-8 MUST be supported
+ }
+ }
+
+ /**
+ * Return the text value of this element in its raw form.
+ * Note: Use {@link #getName()} instead when interpreting the name inside a directory structure.
+ *
+ * @see XEP-0446 ยง3. Security Considerations
+ * @return raw unescaped name
+ */
+ public String getRawName() {
+ return name;
+ }
+
+ @Override
+ public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
+ return new XmlStringBuilder(this)
+ .rightAngleBracket()
+ .append(getName())
+ .closeElement(this);
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCode.builder()
+ .append(getElementName())
+ .append(getRawName())
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsUtil.equals(this, obj, (equalsBuilder, other) ->
+ equalsBuilder.append(getElementName(), other.getElementName())
+ .append(getRawName(), other.getRawName()));
+ }
+
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/SizeElement.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/SizeElement.java
new file mode 100644
index 000000000..ee88f5faf
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/SizeElement.java
@@ -0,0 +1,72 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.file_metadata.element.child;
+
+import org.jivesoftware.smack.packet.NamedElement;
+import org.jivesoftware.smack.packet.XmlEnvironment;
+import org.jivesoftware.smack.util.EqualsUtil;
+import org.jivesoftware.smack.util.HashCode;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * The length of the file's content, in bytes.
+ */
+public class SizeElement implements NamedElement {
+
+ public static final String ELEMENT = "size";
+
+ private final long size;
+
+ public SizeElement(long size) {
+ if (size < 0) {
+ throw new IllegalArgumentException("Size MUST NOT be negative.");
+ }
+ this.size = size;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ @Override
+ public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
+ return new XmlStringBuilder(this)
+ .rightAngleBracket()
+ .append(Long.toString(getSize()))
+ .closeElement(this);
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCode.builder()
+ .append(getElementName())
+ .append(getSize())
+ .build();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsUtil.equals(this, obj, (equalsBuilder, other) ->
+ equalsBuilder.append(getElementName(), other.getElementName())
+ .append(getSize(), other.getSize()));
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/package-info.java
new file mode 100644
index 000000000..f0f904bd8
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/element/child/package-info.java
@@ -0,0 +1,20 @@
+/**
+ *
+ * Copyright 2020 Paul Schaub
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Child elements of the file metadata element.
+ */
+package org.jivesoftware.smackx.file_metadata.element.child;
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/provider/FileMetadataElementProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/provider/FileMetadataElementProvider.java
index e4212b0ce..eb98273bd 100644
--- a/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/provider/FileMetadataElementProvider.java
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/file_metadata/provider/FileMetadataElementProvider.java
@@ -16,9 +16,6 @@
*/
package org.jivesoftware.smackx.file_metadata.provider;
-import java.io.IOException;
-import java.text.ParseException;
-
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.provider.ExtensionElementProvider;
@@ -31,6 +28,9 @@ import org.jivesoftware.smackx.hashes.provider.HashElementProvider;
import org.jivesoftware.smackx.thumbnails.element.ThumbnailElement;
import org.jivesoftware.smackx.thumbnails.provider.ThumbnailElementProvider;
+import java.io.IOException;
+import java.text.ParseException;
+
public class FileMetadataElementProvider extends ExtensionElementProvider {
@Override
@@ -41,60 +41,60 @@ public class FileMetadataElementProvider extends ExtensionElementProvider