/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* 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 net.java.sip.communicator.impl.protocol.jabber.extensions.thumbnail;
import java.text.*;
import java.util.*;
import org.jitsi.util.Logger;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.jivesoftware.smack.util.*;
import org.jivesoftware.smackx.packet.*;
import org.jivesoftware.smackx.packet.StreamInitiation.File;
import org.jivesoftware.smackx.provider.*;
import org.xmlpull.v1.*;
/**
* The <tt>FileElement</tt> extends the smackx <tt>StreamInitiation.File</tt>
* in order to provide a file that supports thumbnails.
*
* @author Yana Stamcheva
*/
public class FileElement
extends File
implements IQProvider
{
private static final Logger logger = Logger.getLogger(FileElement.class);
private static final List<DateFormat> DATE_FORMATS
= new ArrayList<DateFormat>();
/**
* The element name of this <tt>IQProvider</tt>.
*/
public static final String ELEMENT_NAME = "si";
/**
* The namespace of this <tt>IQProvider</tt>.
*/
public static final String NAMESPACE = "http://jabber.org/protocol/si";
static
{
// DATE_FORMATS
DateFormat fmt;
// XEP-0091
DATE_FORMATS.add(DelayInformation.XEP_0091_UTC_FORMAT);
fmt = new SimpleDateFormat("yyyyMd'T'HH:mm:ss'Z'");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
DATE_FORMATS.add(fmt);
// XEP-0082
fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
DATE_FORMATS.add(fmt);
fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
DATE_FORMATS.add(fmt);
DATE_FORMATS.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"));
DATE_FORMATS.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
}
private ThumbnailElement thumbnail;
/**
* An empty constructor used to initialize this class as an
* <tt>IQProvider</tt>.
*/
public FileElement()
{
this("", 0);
}
/**
* Creates a <tt>FileElement</tt> by specifying a base file and a thumbnail
* to extend it with.
*
* @param baseFile the file used as a base
* @param thumbnail the thumbnail to add
*/
public FileElement(File baseFile, ThumbnailElement thumbnail)
{
this(baseFile.getName(), baseFile.getSize());
this.thumbnail = thumbnail;
}
/**
* Creates a <tt>FileElement</tt> by specifying the name and the size of the
* file.
*
* @param name the name of the file
* @param size the size of the file
*/
public FileElement(String name, long size)
{
super(name, size);
}
/**
* Represents this <tt>FileElement</tt> in an XML.
*
* @see File#toXML()
*/
@Override
public String toXML()
{
StringBuilder buffer = new StringBuilder();
buffer.append("<").append(getElementName()).append(" xmlns=\"")
.append(getNamespace()).append("\" ");
if (getName() != null)
{
buffer.append("name=\"").append(
StringUtils.escapeForXML(getName())).append("\" ");
}
if (getSize() > 0)
{
buffer.append("size=\"").append(getSize()).append("\" ");
}
if (getDate() != null)
{
buffer.append("date=\"").append(
StringUtils.formatXEP0082Date(this.getDate())).append("\" ");
}
if (getHash() != null)
{
buffer.append("hash=\"").append(getHash()).append("\" ");
}
if ((this.getDesc() != null && getDesc().length() > 0)
|| isRanged()
|| thumbnail != null)
{
buffer.append(">");
if (getDesc() != null && getDesc().length() > 0)
{
buffer.append("<desc>").append(
StringUtils.escapeForXML(getDesc())).append("</desc>");
}
if (isRanged())
{
buffer.append("<range/>");
}
if (thumbnail != null)
{
buffer.append(thumbnail.toXML());
}
buffer.append("</").append(getElementName()).append(">");
}
else
{
buffer.append("/>");
}
return buffer.toString();
}
/**
* Returns the <tt>ThumbnailElement</tt> contained in this
* <tt>FileElement</tt>.
* @return the <tt>ThumbnailElement</tt> contained in this
* <tt>FileElement</tt>
*/
public ThumbnailElement getThumbnailElement()
{
return thumbnail;
}
/**
* Sets the given <tt>thumbnail</tt> to this <tt>FileElement</tt>.
* @param thumbnail the <tt>ThumbnailElement</tt> to set
*/
public void setThumbnailElement(ThumbnailElement thumbnail)
{
this.thumbnail = thumbnail;
}
/**
* Parses the given <tt>parser</tt> in order to create a
* <tt>FileElement</tt> from it.
* @param parser the parser to parse
* @see IQProvider#parseIQ(XmlPullParser)
*/
public IQ parseIQ(final XmlPullParser parser)
throws Exception
{
boolean done = false;
// si
String id = parser.getAttributeValue("", "id");
String mimeType = parser.getAttributeValue("", "mime-type");
StreamInitiation initiation = new StreamInitiation();
// file
String name = null;
String size = null;
String hash = null;
String date = null;
String desc = null;
ThumbnailElement thumbnail = null;
boolean isRanged = false;
// feature
DataForm form = null;
DataFormProvider dataFormProvider = new DataFormProvider();
int eventType;
String elementName;
String namespace;
while (!done)
{
eventType = parser.next();
elementName = parser.getName();
namespace = parser.getNamespace();
if (eventType == XmlPullParser.START_TAG)
{
if (elementName.equals("file"))
{
name = parser.getAttributeValue("", "name");
size = parser.getAttributeValue("", "size");
hash = parser.getAttributeValue("", "hash");
date = parser.getAttributeValue("", "date");
}
else if (elementName.equals("desc"))
{
desc = parser.nextText();
}
else if (elementName.equals("range"))
{
isRanged = true;
}
else if (elementName.equals("x")
&& namespace.equals("jabber:x:data"))
{
form = (DataForm) dataFormProvider.parseExtension(parser);
}
else if (elementName.equals("thumbnail"))
{
thumbnail = new ThumbnailElement(parser.getText());
}
}
else if (eventType == XmlPullParser.END_TAG)
{
if (elementName.equals("si"))
{
done = true;
}
// The name-attribute is required per XEP-0096, so ignore the
// IQ if the name is not set to avoid exceptions. Particularly,
// the SI response of Empathy contains an invalid, empty
// file-tag.
else if (elementName.equals("file") && name != null)
{
long fileSize = 0;
if(size != null && size.trim().length() !=0)
{
try
{
fileSize = Long.parseLong(size);
}
catch (NumberFormatException e)
{
logger.warn("Received an invalid file size,"
+ " continuing with fileSize set to 0", e);
}
}
FileElement file = new FileElement(name, fileSize);
file.setHash(hash);
if (date != null)
{
// try all known date formats
boolean found = false;
if (date.matches(
".*?T\\d+:\\d+:\\d+(\\.\\d+)?(\\+|-)\\d+:\\d+"))
{
int timeZoneColon = date.lastIndexOf(":");
date = date.substring(0, timeZoneColon)
+ date.substring(
timeZoneColon+1, date.length());
}
for (DateFormat fmt : DATE_FORMATS)
{
try
{
file.setDate(fmt.parse(date));
found = true;
break;
}
catch (ParseException ex)
{
}
}
if (!found)
{
logger.warn(
"Unknown dateformat on incoming file transfer: "
+ date);
}
}
if (thumbnail != null)
file.setThumbnailElement(thumbnail);
file.setDesc(desc);
file.setRanged(isRanged);
initiation.setFile(file);
}
}
}
initiation.setSesssionID(id);
initiation.setMimeType(mimeType);
initiation.setFeatureNegotiationForm(form);
return initiation;
}
}