aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhristoterezov <hristo@jitsi.org>2013-09-30 13:46:09 +0300
committerhristoterezov <hristo@jitsi.org>2013-09-30 13:46:09 +0300
commita16097f5cd159d446963f91b0491ee87d4a23f9e (patch)
tree00e3bae1f46ff5c1c660b52c81a2b2200566f2ba
parentf02a12d22dbd330904622eb0cec42d7e3c8186a7 (diff)
downloadjitsi-a16097f5cd159d446963f91b0491ee87d4a23f9e.zip
jitsi-a16097f5cd159d446963f91b0491ee87d4a23f9e.tar.gz
jitsi-a16097f5cd159d446963f91b0491ee87d4a23f9e.tar.bz2
Adds check for image size and content type for the image replacement of links. Adds "show preview" link next to the image link if the preview of the images is disabled. Patch provided by Marin Dzhigarov on dev mailing list (subject: "PATCH: Replacement of Image/Video links").
-rw-r--r--resources/languages/resources.properties3
-rw-r--r--src/net/java/sip/communicator/impl/gui/GuiActivator.java25
-rwxr-xr-xsrc/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java127
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/ShowPreviewDialog.java301
-rw-r--r--src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf1
-rw-r--r--src/net/java/sip/communicator/impl/replacement/directimage/DirectImageActivator.java45
-rw-r--r--src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java137
-rw-r--r--src/net/java/sip/communicator/impl/replacement/directimage/directimage.source.manifest.mf1
-rw-r--r--src/net/java/sip/communicator/impl/replacement/vbox7/ReplacementServiceVbox7Impl.java2
-rw-r--r--src/net/java/sip/communicator/impl/replacement/youtube/ReplacementServiceYoutubeImpl.java49
-rw-r--r--src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java34
-rw-r--r--src/net/java/sip/communicator/service/replacement/ReplacementProperty.java6
-rw-r--r--src/net/java/sip/communicator/service/replacement/directimage/DirectImageReplacementService.java34
-rw-r--r--src/net/java/sip/communicator/service/replacement/replacement.manifest.mf3
-rw-r--r--src/net/java/sip/communicator/util/FTPUtils.java247
15 files changed, 950 insertions, 65 deletions
diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties
index 1d1338f..81c8367 100644
--- a/resources/languages/resources.properties
+++ b/resources/languages/resources.properties
@@ -580,6 +580,8 @@ service.gui.WORK_PHONE=Work
service.gui.PHONE=Phone
service.gui.PHONES=Phones
service.gui.EDIT_NOT_SUPPORTED=Editing this account is not supported
+service.gui.SHOW_PREVIEW=(show preview)
+service.gui.SHOW_PREVIEW_WARNING_DESCRIPTION=Please consider that enabling Image/Video previews might violate your online anonymity and expose your activity on the network.
service.gui.ZID_NAME_SET=ZRTP identifier's name:
service.gui.ZID_NAME_NOT_SET=ZRTP identifier name is not set.
@@ -1583,6 +1585,7 @@ plugin.chatconfig.TITLE=Chat
plugin.chatconfig.replacement.TITLE=Image/Video:
plugin.chatconfig.replacement.ENABLE_SMILEY_STATUS=Enable smiley replacement
plugin.chatconfig.replacement.ENABLE_REPLACEMENT_STATUS=Enable Image/Video replacement
+plugin.chatconfig.replacement.ENABLE_REPLACEMENT_PROPOSAL=Show proposals for Image/Video replacement
plugin.chatconfig.replacement.REPLACEMENT_SOURCES=Sources:
plugin.chatconfig.spellcheck.TITLE=SpellCheck
diff --git a/src/net/java/sip/communicator/impl/gui/GuiActivator.java b/src/net/java/sip/communicator/impl/gui/GuiActivator.java
index 6f6f832..f64a776 100644
--- a/src/net/java/sip/communicator/impl/gui/GuiActivator.java
+++ b/src/net/java/sip/communicator/impl/gui/GuiActivator.java
@@ -8,6 +8,8 @@ package net.java.sip.communicator.impl.gui;
import java.util.*;
+import javax.swing.*;
+
import net.java.sip.communicator.impl.gui.main.account.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
@@ -26,6 +28,7 @@ import net.java.sip.communicator.service.notification.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.globalstatus.*;
import net.java.sip.communicator.service.replacement.*;
+import net.java.sip.communicator.service.replacement.directimage.*;
import net.java.sip.communicator.service.replacement.smilies.*;
import net.java.sip.communicator.service.shutdown.*;
import net.java.sip.communicator.service.systray.*;
@@ -38,8 +41,6 @@ import org.jitsi.service.neomedia.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
-import javax.swing.*;
-
/**
* The GUI Activator class.
*
@@ -86,6 +87,8 @@ public class GuiActivator implements BundleActivator
private static SmiliesReplacementService smiliesService;
+ private static DirectImageReplacementService directImageService;
+
private static GlobalStatusService globalStatusService;
private static AccountManager accountManager;
@@ -648,6 +651,24 @@ public class GuiActivator implements BundleActivator
}
/**
+ * Returns the <tt>DirectImageReplacementService</tt> obtained from the
+ * bundle context.
+ *
+ * @return the <tt>DirectImageReplacementService</tt> implementation
+ * obtained from the bundle context
+ */
+ public static DirectImageReplacementService getDirectImageReplacementSource()
+ {
+ if (directImageService == null)
+ {
+ directImageService
+ = ServiceUtils.getService(bundleContext,
+ DirectImageReplacementService.class);
+ }
+ return directImageService;
+ }
+
+ /**
* Returns the <tt>SecurityAuthority</tt> implementation registered to
* handle security authority events.
*
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java b/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java
index c76e769..02dcec6 100755
--- a/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java
@@ -33,6 +33,7 @@ import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.history.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.replacement.*;
+import net.java.sip.communicator.service.replacement.directimage.*;
import net.java.sip.communicator.service.replacement.smilies.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.skin.*;
@@ -103,7 +104,7 @@ public class ChatConversationPanel
/**
* The document used by the text component.
*/
- private HTMLDocument document;
+ HTMLDocument document;
/**
* The parent container.
@@ -160,6 +161,9 @@ public class ChatConversationPanel
private boolean isSimpleTheme = true;
+ private final ShowPreviewDialog showPreview
+ = new ShowPreviewDialog(ChatConversationPanel.this);
+
/**
* The implementation of the routine which scrolls {@link #chatTextPane} to its
* bottom.
@@ -227,6 +231,8 @@ public class ChatConversationPanel
this.chatTextPane.setCursor(
Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
+ this.addChatLinkClickedListener(showPreview);
+
this.setWheelScrollingEnabled(true);
this.setViewportView(chatTextPane);
@@ -818,6 +824,7 @@ public class ChatConversationPanel
ConfigurationService cfg = GuiActivator.getConfigurationService();
if (cfg.getBoolean(ReplacementProperty.REPLACEMENT_ENABLE, true)
+ ||cfg.getBoolean(ReplacementProperty.REPLACEMENT_PROPOSAL, true)
|| cfg.getBoolean(
ReplacementProperty.getPropertyName("SMILEY"),
true))
@@ -837,9 +844,9 @@ public class ChatConversationPanel
* @param chatString the message.
* @param contentType
*/
- private void processReplacement(final String messageID,
- final String chatString,
- final String contentType)
+ void processReplacement(final String messageID,
+ final String chatString,
+ final String contentType)
{
SwingWorker worker = new SwingWorker()
{
@@ -854,6 +861,8 @@ public class ChatConversationPanel
if (newMessage != null && !newMessage.equals(chatString))
{
+ showPreview.getMsgIDToChatString().put(
+ messageID, newMessage);
synchronized (scrollToBottomRunnable)
{
scrollToBottomIsPending = true;
@@ -884,6 +893,10 @@ public class ChatConversationPanel
= cfg.getBoolean(
ReplacementProperty.REPLACEMENT_ENABLE,
true);
+ boolean isProposalEnabled
+ = cfg.getBoolean(
+ ReplacementProperty.REPLACEMENT_PROPOSAL,
+ true);
Matcher divMatcher = DIV_PATTERN.matcher(chatString);
String openingTag = "";
String msgStore = chatString;
@@ -895,6 +908,7 @@ public class ChatConversationPanel
closingTag = divMatcher.group(3);
}
+ int linkCounter = 0;
for (Map.Entry<String, ReplacementService> entry
: GuiActivator.getReplacementSources().entrySet())
{
@@ -902,13 +916,12 @@ public class ChatConversationPanel
boolean isSmiley
= source instanceof SmiliesReplacementService;
-
- if (!(cfg.getBoolean(
- ReplacementProperty.getPropertyName(
- source.getSourceName()),
- true)
- && (isEnabled || isSmiley)))
- continue;
+ boolean isDirectImage
+ = source instanceof DirectImageReplacementService;
+ boolean isEnabledForSource
+ = cfg.getBoolean(
+ ReplacementProperty.getPropertyName(
+ source.getSourceName()), true);
String sourcePattern = source.getPattern();
Pattern p
@@ -916,7 +929,6 @@ public class ChatConversationPanel
sourcePattern,
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
Matcher m = p.matcher(msgStore);
-
StringBuilder msgBuff = new StringBuilder();
int startPos = 0;
@@ -929,31 +941,86 @@ public class ChatConversationPanel
String temp = source.getReplacement(group);
String group0 = m.group(0);
- if(!temp.equals(group0)
- || source.getSourceName().equals("DIRECTIMAGE"))
+ if(!temp.equals(group0) || isDirectImage)
{
- if(isSmiley)
+ if (isSmiley)
+ {
+ if (cfg.getBoolean(ReplacementProperty.
+ getPropertyName("SMILEY"),
+ true))
+ {
+ msgBuff.append(
+ ChatHtmlUtils.createEndPlainTextTag(
+ contentType));
+ msgBuff.append("<IMG SRC=\"");
+ msgBuff.append(temp);
+ msgBuff.append("\" BORDER=\"0\" ALT=\"");
+ msgBuff.append(group0);
+ msgBuff.append("\"></IMG>");
+ msgBuff.append(
+ ChatHtmlUtils.createStartPlainTextTag(
+ contentType));
+ }
+ else
+ {
+ msgBuff.append(group);
+ }
+ }
+ else if (isEnabled && isEnabledForSource)
+ {
+ if (isDirectImage)
+ {
+ DirectImageReplacementService service
+ = (DirectImageReplacementService)source;
+ if (service.isDirectImage(group)
+ && service.getImageSize(group) != -1)
+ {
+ msgBuff.append(
+ "<IMG HEIGHT=\"90\" "
+ + "WIDTH=\"120\" SRC=\"");
+ msgBuff.append(temp);
+ msgBuff.append("\" BORDER=\"0\" ALT=\"");
+ msgBuff.append(group0);
+ msgBuff.append("\"></IMG>");
+ }
+ else
+ {
+ msgBuff.append(group);
+ }
+ }
+ else
+ {
+ msgBuff.append(
+ "<IMG HEIGHT=\"90\" "
+ + "WIDTH=\"120\" SRC=\"");
+ msgBuff.append(temp);
+ msgBuff.append("\" BORDER=\"0\" ALT=\"");
+ msgBuff.append(group0);
+ msgBuff.append("\"></IMG>");
+ }
+ }
+ else if (isProposalEnabled)
{
+ msgBuff.append(group);
msgBuff.append(
- ChatHtmlUtils.createEndPlainTextTag(
- contentType));
- msgBuff.append("<IMG SRC=\"");
+ "</A> <A href=\"jitsi://"
+ + showPreview.getClass().getName()
+ + "/SHOWPREVIEW?" + messageID + "#"
+ + linkCounter + "\">"
+ + GuiActivator.getResources().
+ getI18NString("service.gui.SHOW_PREVIEW"));
+
+ showPreview.getMsgIDandPositionToLink()
+ .put(
+ messageID + "#" + linkCounter++, group);
+ showPreview.getLinkToReplacement()
+ .put(
+ group, temp);
}
else
{
- msgBuff.append(
- "<IMG HEIGHT=\"90\" WIDTH=\"120\" SRC=\"");
+ msgBuff.append(group);
}
-
- msgBuff.append(temp);
- msgBuff.append("\" BORDER=\"0\" ALT=\"");
- msgBuff.append(group0);
- msgBuff.append("\"></IMG>");
-
- if(isSmiley)
- msgBuff.append(
- ChatHtmlUtils.createStartPlainTextTag(
- contentType));
}
else
{
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ShowPreviewDialog.java b/src/net/java/sip/communicator/impl/gui/main/chat/ShowPreviewDialog.java
new file mode 100644
index 0000000..58ff0c9
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/ShowPreviewDialog.java
@@ -0,0 +1,301 @@
+package net.java.sip.communicator.impl.gui.main.chat;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import javax.swing.*;
+import javax.swing.text.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.plugin.desktoputil.SwingWorker;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.replacement.*;
+import net.java.sip.communicator.service.replacement.directimage.*;
+import net.java.sip.communicator.util.*;
+
+import org.jitsi.service.configuration.*;
+
+public class ShowPreviewDialog
+ extends SIPCommDialog
+ implements ActionListener,
+ ChatLinkClickedListener
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The <tt>Logger</tt> used by the <tt>ShowPreviewDialog</tt> class and
+ * its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(ShowPreviewDialog.class);
+
+ ConfigurationService cfg
+ = GuiActivator.getConfigurationService();
+
+ /**
+ * The Ok button.
+ */
+ private final JButton okButton;
+
+ /**
+ * The cancel button.
+ */
+ private final JButton cancelButton;
+
+ /**
+ * Checkbox that indicates whether or not to show this dialog next time.
+ */
+ private final JCheckBox enableReplacementProposal;
+
+ /**
+ * Checkbox that indicates whether or not to show previews automatically
+ */
+ private final JCheckBox enableReplacement;
+
+ /**
+ * The <tt>ChatConversationPanel</tt> that this dialog is associated with.
+ */
+ private final ChatConversationPanel chatPanel;
+
+ /**
+ * Mapping between messageID and the string representation of the chat
+ * message.
+ */
+ private Map<String, String> msgIDToChatString
+ = new ConcurrentHashMap<String, String>();
+
+ /**
+ * Mapping between the pair (messageID, link position) and the actual link
+ * in the string representation of the chat message.
+ */
+ private Map<String, String> msgIDandPositionToLink
+ = new ConcurrentHashMap<String, String>();
+
+ /**
+ * Mapping between link and replacement for this link that is acquired
+ * from it's corresponding <tt>ReplacementService</tt>.
+ */
+ private Map<String, String> linkToReplacement
+ = new ConcurrentHashMap<String, String>();
+
+ /**
+ * The id of the message that is currently associated with this dialog.
+ */
+ private String currentMessageID = "";
+
+ /**
+ * The position of the link in the current message.
+ */
+ private String currentLinkPosition = "";
+
+ /**
+ * Creates an instance of <tt>ShowPreviewDialog</tt>
+ * @param chatPanel The <tt>ChatConversationPanel</tt> that is associated
+ * with this dialog.
+ */
+ ShowPreviewDialog(final ChatConversationPanel chatPanel)
+ {
+ this.chatPanel = chatPanel;
+
+ okButton = new JButton(
+ GuiActivator.getResources().getI18NString("service.gui.OK"));
+ cancelButton = new JButton(
+ GuiActivator.getResources().getI18NString("service.gui.CANCEL"));
+
+ JPanel mainPanel = new TransparentPanel(new BorderLayout());
+ this.getContentPane().add(mainPanel);
+
+ JTextPane descriptionMsg = new JTextPane();
+ descriptionMsg.setEditable(false);
+ descriptionMsg.setOpaque(false);
+ descriptionMsg.setText(
+ GuiActivator.getResources().getI18NString(
+ "service.gui.SHOW_PREVIEW_WARNING_DESCRIPTION"));
+
+ enableReplacement
+ = new JCheckBox(
+ GuiActivator.getResources().getI18NString(
+ "plugin.chatconfig.replacement.ENABLE_REPLACEMENT_STATUS"));
+ enableReplacement.setOpaque(false);
+ enableReplacement.setSelected(
+ cfg.getBoolean(ReplacementProperty.REPLACEMENT_ENABLE, true));
+ enableReplacementProposal
+ = new JCheckBox(
+ GuiActivator.getResources().getI18NString(
+ "plugin.chatconfig.replacement.ENABLE_REPLACEMENT_PROPOSAL"));
+ enableReplacementProposal.setOpaque(false);
+
+ JPanel checkBoxPanel = new TransparentPanel(new GridLayout(0, 1));
+ checkBoxPanel.add(enableReplacement);
+ checkBoxPanel.add(enableReplacementProposal);
+
+ JPanel buttonsPanel
+ = new TransparentPanel(new FlowLayout(FlowLayout.CENTER));
+ buttonsPanel.add(okButton);
+ buttonsPanel.add(cancelButton);
+
+ JPanel panel = new TransparentPanel(new GridLayout(0, 1));
+ panel.add(descriptionMsg);
+ panel.add(checkBoxPanel);
+ mainPanel.add(panel, BorderLayout.NORTH);
+ mainPanel.add(buttonsPanel, BorderLayout.CENTER);
+
+ okButton.addActionListener(this);
+ cancelButton.addActionListener(this);
+
+ this.setPreferredSize(new Dimension(350, 200));
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent arg0)
+ {
+ if (arg0.getSource().equals(okButton))
+ {
+ cfg.setProperty(ReplacementProperty.REPLACEMENT_ENABLE,
+ enableReplacement.isSelected());
+ cfg.setProperty(ReplacementProperty.REPLACEMENT_PROPOSAL
+ , enableReplacementProposal.isSelected());
+ SwingWorker worker = new SwingWorker()
+ {
+ /**
+ * Called on the event dispatching thread
+ * (not on the worker thread) after the
+ * <code>construct</code> method has returned.
+ */
+ @Override
+ public void finished()
+ {
+ String newChatString = (String)get();
+
+ if (newChatString != null)
+ {
+ try
+ {
+ Element elem =
+ chatPanel.document.getElement(currentMessageID);
+ chatPanel.document.setOuterHTML(
+ elem, newChatString);
+ msgIDToChatString.put(
+ currentMessageID, newChatString);
+ }
+ catch (BadLocationException ex)
+ {
+ logger.error("Could not replace chat message", ex);
+ }
+ catch (IOException ex)
+ {
+ logger.error("Could not replace chat message", ex);
+ }
+ }
+ }
+
+ @Override
+ protected Object construct() throws Exception
+ {
+ String newChatString
+ = msgIDToChatString.get(currentMessageID);
+ try
+ {
+ String originalLink = msgIDandPositionToLink.get
+ (currentMessageID + "#" + currentLinkPosition);
+ String replacementLink
+ = linkToReplacement.get(originalLink);
+ String replacement;
+ DirectImageReplacementService source
+ = GuiActivator.getDirectImageReplacementSource();
+ if (originalLink.equals(replacementLink) &&
+ (!source.isDirectImage(originalLink) ||
+ source.getImageSize(originalLink) == -1))
+ {
+ replacement = originalLink;
+ }
+ else
+ {
+ replacement =
+ "<IMG HEIGHT=\"90\" WIDTH=\"120\" SRC=\""
+ + replacementLink + "\" BORDER=\"0\" ALT=\""
+ + originalLink + "\"></IMG>";
+ }
+
+ String old = originalLink + "</A> <A href=\"jitsi://"
+ + ShowPreviewDialog.this.getClass().getName()
+ + "/SHOWPREVIEW?" + currentMessageID + "#"
+ + currentLinkPosition + "\">"
+ + GuiActivator.getResources().
+ getI18NString("service.gui.SHOW_PREVIEW");
+
+ newChatString = newChatString.replace(old, replacement);
+ }
+ catch (Exception ex)
+ {
+ logger.error("Could not replace chat message", ex);
+ }
+ return newChatString;
+ }
+ };
+ worker.start();
+ this.setVisible(false);
+ }
+ else if (arg0.getSource().equals(cancelButton))
+ {
+ this.setVisible(false);
+ }
+ }
+
+ @Override
+ public void chatLinkClicked(URI url)
+ {
+ String action = url.getPath();
+ if (action.equals("/SHOWPREVIEW"))
+ {
+ enableReplacement.setSelected(
+ cfg.getBoolean(ReplacementProperty.REPLACEMENT_ENABLE, true));
+ enableReplacementProposal.setSelected(
+ cfg.getBoolean(ReplacementProperty.REPLACEMENT_PROPOSAL, true));
+
+ currentMessageID = url.getQuery();
+ currentLinkPosition = url.getFragment();
+
+ this.setVisible(true);
+ this.setLocationRelativeTo(chatPanel);
+ }
+ }
+
+ /**
+ * Returns mapping between messageID and the string representation of
+ * the chat message.
+ * @return mapping between messageID and chat string.
+ */
+ Map<String, String> getMsgIDToChatString()
+ {
+ return msgIDToChatString;
+ }
+
+ /**
+ * Returns mapping between the pair (messageID, link position) and the
+ * actual link in the string representation of the chat message.
+ * @return mapping between (messageID, linkPosition) and link.
+ */
+ Map<String, String> getMsgIDandPositionToLink()
+ {
+ return msgIDandPositionToLink;
+ }
+
+ /**
+ * Returns mapping between link and replacement for this link that was
+ * acquired from it's corresponding <tt>ReplacementService</tt>.
+ * @return mapping between link and it's corresponding replacement.
+ */
+ Map<String, String> getLinkToReplacement()
+ {
+ return linkToReplacement;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
index 590a08b..31824d6 100644
--- a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
+++ b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
@@ -44,6 +44,7 @@ Import-Package: com.apple.eawt,
net.java.sip.communicator.service.protocol.media,
net.java.sip.communicator.service.replacement,
net.java.sip.communicator.service.replacement.smilies,
+ net.java.sip.communicator.service.replacement.directimage,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.service.shutdown,
net.java.sip.communicator.service.systray,
diff --git a/src/net/java/sip/communicator/impl/replacement/directimage/DirectImageActivator.java b/src/net/java/sip/communicator/impl/replacement/directimage/DirectImageActivator.java
index 6a17f5a..6bad78e 100644
--- a/src/net/java/sip/communicator/impl/replacement/directimage/DirectImageActivator.java
+++ b/src/net/java/sip/communicator/impl/replacement/directimage/DirectImageActivator.java
@@ -9,8 +9,10 @@ package net.java.sip.communicator.impl.replacement.directimage;
import java.util.*;
import net.java.sip.communicator.service.replacement.*;
+import net.java.sip.communicator.service.replacement.directimage.*;
import net.java.sip.communicator.util.*;
+import org.jitsi.service.configuration.*;
import org.osgi.framework.*;
/**
@@ -38,6 +40,16 @@ public class DirectImageActivator
private static ReplacementService directImageSource = null;
/**
+ * The service used for accessing configuration properties.
+ */
+ private static ConfigurationService confService = null;
+
+ /**
+ * The bundle context.
+ */
+ private static BundleContext bundleContext = null;
+
+ /**
* Starts the Direct image links replacement source bundle
*
* @param context the <tt>BundleContext</tt> as provided from the OSGi
@@ -46,14 +58,22 @@ public class DirectImageActivator
*/
public void start(BundleContext context) throws Exception
{
+ bundleContext = context;
Hashtable<String, String> hashtable = new Hashtable<String, String>();
hashtable.put(ReplacementService.SOURCE_NAME,
ReplacementServiceDirectImageImpl.DIRECT_IMAGE_CONFIG_LABEL);
directImageSource = new ReplacementServiceDirectImageImpl();
directImageSourceServReg =
- context.registerService(ReplacementService.class.getName(),
+ context.registerService(
+ DirectImageReplacementService.class.getName(),
+ directImageSource, hashtable);
+
+ directImageSourceServReg =
+ context.registerService(
+ ReplacementService.class.getName(),
directImageSource, hashtable);
+
logger.info("Direct Image Link source implementation [STARTED].");
}
@@ -66,6 +86,29 @@ public class DirectImageActivator
public void stop(BundleContext context) throws Exception
{
directImageSourceServReg.unregister();
+ confService = null;
+ bundleContext = null;
logger.info("Direct Image Link source implementation [STOPPED].");
}
+
+ /**
+ * Returns a reference to a ConfigurationService implementation currently
+ * registered in the bundle context or null if no such implementation was
+ * found.
+ *
+ * @return a currently valid implementation of the ConfigurationService.
+ */
+ public static ConfigurationService getConfigService()
+ {
+ if(confService == null)
+ {
+ ServiceReference confReference
+ = bundleContext.getServiceReference(
+ ConfigurationService.class.getName());
+ confService
+ = (ConfigurationService) bundleContext.getService(
+ confReference);
+ }
+ return confService;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java b/src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java
index 43fb252..d984a5f 100644
--- a/src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java
+++ b/src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java
@@ -5,7 +5,12 @@
*/
package net.java.sip.communicator.impl.replacement.directimage;
+import java.net.*;
+
+import org.jitsi.service.configuration.*;
+
import net.java.sip.communicator.service.replacement.*;
+import net.java.sip.communicator.service.replacement.directimage.*;
import net.java.sip.communicator.util.*;
/**
@@ -13,9 +18,10 @@ import net.java.sip.communicator.util.*;
* image links.
*
* @author Purvesh Sahoo
+ * @author Marin Dzhigarov
*/
public class ReplacementServiceDirectImageImpl
- implements ReplacementService
+ implements DirectImageReplacementService
{
/**
* The logger for this class.
@@ -40,14 +46,62 @@ public class ReplacementServiceDirectImageImpl
public static final String SOURCE_NAME = "DIRECTIMAGE";
/**
+ * Maximum allowed size of the image in bytes. The default size is 2MB.
+ */
+ private long imgMaxSize = 2097152;
+
+ /**
+ * Configuration property name for maximum allowed size of the image in
+ * bytes.
+ */
+ private static final String MAX_IMG_SIZE =
+ "net.java.sip.communicator.impl.replacement.directimage.MAX_IMG_SIZE";
+
+ /**
* Constructor for <tt>ReplacementServiceDirectImageImpl</tt>.
*/
public ReplacementServiceDirectImageImpl()
{
+ setMaxImgSizeFromConf();
logger.trace("Creating a Direct Image Link Source.");
}
/**
+ * Gets the max allowed size value in bytes from Configuration service and
+ * sets the value to <tt>imgMaxSize</tt> if succeed. If the configuration
+ * property isn't available or the value can't be parsed correctly
+ * the value of <tt>imgMaxSize</tt> isn't changed.
+ */
+ private void setMaxImgSizeFromConf()
+ {
+ ConfigurationService configService =
+ DirectImageActivator.getConfigService();
+
+ if(configService != null)
+ {
+ String confImgSizeStr =
+ (String) configService.getProperty(MAX_IMG_SIZE);
+ try
+ {
+ if (confImgSizeStr != null)
+ {
+ imgMaxSize = Long.parseLong(confImgSizeStr);
+ }
+ else
+ {
+ configService.setProperty(MAX_IMG_SIZE, imgMaxSize);
+ }
+ }
+ catch (NumberFormatException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to parse max image size: "
+ + confImgSizeStr + ". Going for default.");
+ }
+ }
+ }
+
+ /**
* Returns the thumbnail URL of the image link provided.
*
* @param sourceString the original image link.
@@ -77,4 +131,85 @@ public class ReplacementServiceDirectImageImpl
{
return URL_PATTERN;
}
+
+ @Override
+ /**
+ * Returns the size of the image in bytes.
+ * @param sourceString the image link.
+ * @return the file size in bytes of the image link provided; -1 if the size
+ * isn't available or exceeds the max allowed image size.
+ */
+ public long getImageSize(String sourceString)
+ {
+ long length = -1;
+ try
+ {
+
+ URL url = new URL(sourceString);
+ String protocol = url.getProtocol();
+ if (protocol.equals("http") || protocol.equals("https"))
+ {
+ HttpURLConnection connection =
+ (HttpURLConnection)url.openConnection();
+ length = connection.getContentLengthLong();
+ connection.disconnect();
+ }
+ else if (protocol.equals("ftp"))
+ {
+ FTPUtils ftp = new FTPUtils(sourceString);
+ length = ftp.getSize();
+ ftp.disconnect();
+ }
+
+ if (length > imgMaxSize)
+ {
+ length = -1;
+ }
+ }
+ catch (Exception e)
+ {
+ logger.debug("Failed to get the length of the image in bytes", e);
+ }
+ return length;
+ }
+
+ /**
+ * Returns true if the content type of the resource
+ * pointed by sourceString is an image.
+ * @param sourceString the original image link.
+ * @return true if the content type of the resource
+ * pointed by sourceString is an image.
+ */
+ @Override
+ public boolean isDirectImage(String sourceString)
+ {
+ boolean isDirectImage = false;
+ try
+ {
+ URL url = new URL(sourceString);
+ String protocol = url.getProtocol();
+ if (protocol.equals("http") || protocol.equals("https"))
+ {
+ HttpURLConnection connection =
+ (HttpURLConnection)url.openConnection();
+ isDirectImage = connection.getContentType().contains("image");
+ connection.disconnect();
+ }
+ else if (protocol.equals("ftp"))
+ {
+ if (sourceString.endsWith(".png")
+ || sourceString.endsWith(".jpg")
+ || sourceString.endsWith(".gif"))
+ {
+ isDirectImage = true;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ logger.debug("Failed to retrieve content type information for"
+ + sourceString, e);
+ }
+ return isDirectImage;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/replacement/directimage/directimage.source.manifest.mf b/src/net/java/sip/communicator/impl/replacement/directimage/directimage.source.manifest.mf
index 8687244..ddf3016 100644
--- a/src/net/java/sip/communicator/impl/replacement/directimage/directimage.source.manifest.mf
+++ b/src/net/java/sip/communicator/impl/replacement/directimage/directimage.source.manifest.mf
@@ -7,6 +7,7 @@ System-Bundle: yes
Import-Package: org.osgi.framework,
org.jitsi.service.version,
net.java.sip.communicator.service.replacement,
+ net.java.sip.communicator.service.replacement.directimage,
org.jitsi.service.configuration,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.util
diff --git a/src/net/java/sip/communicator/impl/replacement/vbox7/ReplacementServiceVbox7Impl.java b/src/net/java/sip/communicator/impl/replacement/vbox7/ReplacementServiceVbox7Impl.java
index c5f3131..2d4b272 100644
--- a/src/net/java/sip/communicator/impl/replacement/vbox7/ReplacementServiceVbox7Impl.java
+++ b/src/net/java/sip/communicator/impl/replacement/vbox7/ReplacementServiceVbox7Impl.java
@@ -66,7 +66,7 @@ public class ReplacementServiceVbox7Impl
while (m.find())
{
- thumbUrl = "http://i.vbox7.com/p/" + m.group(1) + "3.jpg";
+ thumbUrl = "https://i.vbox7.com/p/" + m.group(1) + "3.jpg";
}
return thumbUrl;
diff --git a/src/net/java/sip/communicator/impl/replacement/youtube/ReplacementServiceYoutubeImpl.java b/src/net/java/sip/communicator/impl/replacement/youtube/ReplacementServiceYoutubeImpl.java
index e7f63f5..2fdf8ef 100644
--- a/src/net/java/sip/communicator/impl/replacement/youtube/ReplacementServiceYoutubeImpl.java
+++ b/src/net/java/sip/communicator/impl/replacement/youtube/ReplacementServiceYoutubeImpl.java
@@ -5,14 +5,11 @@
*/
package net.java.sip.communicator.impl.replacement.youtube;
-import java.io.*;
-import java.net.*;
+import java.util.regex.*;
import net.java.sip.communicator.service.replacement.*;
import net.java.sip.communicator.util.*;
-import org.json.simple.*;
-
/**
* Implements the {@link ReplacementService} to provide previews for Youtube
* links.
@@ -61,37 +58,31 @@ public class ReplacementServiceYoutubeImpl
*/
public String getReplacement(String sourceString)
{
- try
+ final String pattern = "https?:\\/\\/(?:[0-9A-Z-]+\\.)?(?:youtu\\"
+ + ".be\\/|youtube\\.com\\S*[^\\w\\-\\s])([\\w\\-]{11})(?=[^\\"
+ + "w\\-]|$)(?![?=&+%\\w]*(?:['\"][^<>]*>|<\\/a>))[?=&+%\\w]*";
+ final Pattern compiledPattern
+ = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
+ Matcher matcher = compiledPattern.matcher(sourceString);
+ String thumbUrl = sourceString;
+
+ while (matcher.find())
{
- String url = "http://youtube.com/oembed/?url=" + sourceString;
- URL sourceURL = new URL(url);
- URLConnection conn = sourceURL.openConnection();
-
- BufferedReader in =
- new BufferedReader(new InputStreamReader(conn.getInputStream()));
-
- String inputLine, holder = "";
-
- while ((inputLine = in.readLine()) != null)
- holder = inputLine;
- in.close();
-
- JSONObject wrapper = (JSONObject)JSONValue
- .parseWithException(holder);
-
- String thumbUrl = (String)wrapper.get("thumbnail_url");
-
- if (thumbUrl != null)
+ String videoID = "";
+ try
+ {
+ videoID = matcher.group(1);
+ }
+ catch (Exception e)
{
+ logger.debug("Replacement failed for " + getSourceName(), e);
return thumbUrl;
}
- }
- catch (Throwable e)
- {
- logger.error("Error parsing", e);
+ thumbUrl
+ = "https://img.youtube.com/vi/" + videoID + "/3.jpg";
}
- return sourceString;
+ return thumbUrl;
}
/**
diff --git a/src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java b/src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java
index b28e37f..b3a7c3e 100644
--- a/src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java
+++ b/src/net/java/sip/communicator/plugin/chatconfig/replacement/ReplacementConfigPanel.java
@@ -45,6 +45,11 @@ public class ReplacementConfigPanel
private JCheckBox enableReplacement;
/**
+ * Checkbox to enable/disable proposal messages for image/video replacement.
+ */
+ private JCheckBox enableReplacementProposal;
+
+ /**
* Jtable to list all the available replacement sources.
*/
private JTable table;
@@ -112,6 +117,23 @@ public class ReplacementConfigPanel
}
});
+ mainPanel.add(Box.createVerticalStrut(10));
+
+ enableReplacementProposal =
+ new SIPCommCheckBox(ChatConfigActivator.getResources()
+ .getI18NString(
+ "plugin.chatconfig.replacement.ENABLE_REPLACEMENT_PROPOSAL"));
+
+ mainPanel.add(enableReplacementProposal, BorderLayout.WEST);
+
+ enableReplacementProposal.addActionListener(new ActionListener(){
+
+ public void actionPerformed(ActionEvent arg0)
+ {
+ saveData();
+ }
+ });
+
// the Jtable to list all the available sources
table = new JTable();
table.setShowGrid(false);
@@ -205,6 +227,12 @@ public class ReplacementConfigPanel
true);
this.enableReplacement.setSelected(e);
+ this.enableReplacementProposal.setEnabled(!e);
+ e =
+ configService.getBoolean(ReplacementProperty.REPLACEMENT_PROPOSAL,
+ true);
+ this.enableReplacementProposal.setSelected(e);
+
this.table.setEnabled(e);
}
@@ -224,6 +252,12 @@ public class ReplacementConfigPanel
Boolean.toString(enableReplacement.isSelected()));
boolean e = enableReplacement.isSelected();
+
+ enableReplacementProposal.setEnabled(!e);
+ configService.setProperty(
+ "plugin.chatconfig.replacement.proposal.enable"
+ , Boolean.toString(!e && enableReplacementProposal.isSelected()));
+
table.getSelectionModel().clearSelection();
table.setEnabled(e);
}
diff --git a/src/net/java/sip/communicator/service/replacement/ReplacementProperty.java b/src/net/java/sip/communicator/service/replacement/ReplacementProperty.java
index 32287c5..7fa6684 100644
--- a/src/net/java/sip/communicator/service/replacement/ReplacementProperty.java
+++ b/src/net/java/sip/communicator/service/replacement/ReplacementProperty.java
@@ -13,6 +13,12 @@ package net.java.sip.communicator.service.replacement;
public final class ReplacementProperty
{
/**
+ * The replacement proposal property.
+ */
+ public static final String REPLACEMENT_PROPOSAL =
+ "plugin.chatconfig.replacement.proposal.enable";
+
+ /**
* The replacement property.
*/
public static final String REPLACEMENT_ENABLE =
diff --git a/src/net/java/sip/communicator/service/replacement/directimage/DirectImageReplacementService.java b/src/net/java/sip/communicator/service/replacement/directimage/DirectImageReplacementService.java
new file mode 100644
index 0000000..a8d48d3
--- /dev/null
+++ b/src/net/java/sip/communicator/service/replacement/directimage/DirectImageReplacementService.java
@@ -0,0 +1,34 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.replacement.directimage;
+
+import net.java.sip.communicator.service.replacement.*;
+
+/**
+ *
+ * @author Marin Dzhigarov
+ *
+ */
+public interface DirectImageReplacementService
+ extends ReplacementService
+{
+ /**
+ * Returns the size of the image in bytes.
+ * @param sourceString the image link.
+ * @return the file size in bytes of the image link provided; -1 if the size
+ * isn't available or exceeds the max allowed image size.
+ */
+ public long getImageSize(String sourceString);
+
+ /**
+ * Checks if the resource pointed by sourceString is an image.
+ * @param sourceString the image link.
+ * @return true if the content type of the resource
+ * pointed by sourceString is an image.
+ */
+ public boolean isDirectImage(String sourceString);
+}
diff --git a/src/net/java/sip/communicator/service/replacement/replacement.manifest.mf b/src/net/java/sip/communicator/service/replacement/replacement.manifest.mf
index 27b81e3..c7be7ca 100644
--- a/src/net/java/sip/communicator/service/replacement/replacement.manifest.mf
+++ b/src/net/java/sip/communicator/service/replacement/replacement.manifest.mf
@@ -4,4 +4,5 @@ Bundle-Vendor: jitsi.org
Bundle-Version: 0.0.1
System-Bundle: yes
Export-Package: net.java.sip.communicator.service.replacement,
- net.java.sip.communicator.service.replacement.smilies
+ net.java.sip.communicator.service.replacement.smilies,
+ net.java.sip.communicator.service.replacement.directimage
diff --git a/src/net/java/sip/communicator/util/FTPUtils.java b/src/net/java/sip/communicator/util/FTPUtils.java
new file mode 100644
index 0000000..44ed5d7
--- /dev/null
+++ b/src/net/java/sip/communicator/util/FTPUtils.java
@@ -0,0 +1,247 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license. See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.util;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * Utility class that allows to check the size of ftp file.
+ *
+ * @author Hristo Terezov
+ */
+public class FTPUtils
+{
+ /**
+ * The connection
+ * to the FTP server.
+ */
+ private Socket socket = null;
+
+ /**
+ * The reader from the connection.
+ */
+ private BufferedReader reader = null;
+
+ /**
+ * The writer which is used to send commands to the server.
+ */
+ private BufferedWriter writer = null;
+
+ /**
+ * Default port constant. It is used when the port is not available in URL.
+ */
+ private final int DEFAULT_PORT = 21;
+
+ /**
+ * Constant for the invalid file size.
+ */
+ private final int INVALID_FILE_SIZE = -1;
+
+ /**
+ * The user name for the FTP connection.
+ */
+ private String user = null;
+
+ /**
+ * The password for the FTP connection.
+ */
+ private String pass = null;
+
+ /**
+ * The path to the file.
+ */
+ private String path = null;
+
+ /**
+ * The host name of the FTP server.
+ */
+ private String host = null;
+
+ /**
+ * The port for the FTP connection.
+ */
+ private int port = DEFAULT_PORT;
+
+ /**
+ * Parses the URL, connects to the FTP server and then executes the login.
+ *
+ * @param urlString the URL of the file.
+ * @throws Exception if something with parsing the URL or connection or
+ * login fails.
+ */
+ public FTPUtils(String urlString) throws Exception
+ {
+ parseUrl(urlString);
+ socket = new Socket( host, port);
+ reader = new BufferedReader(
+ new InputStreamReader(socket.getInputStream()));
+ writer = new BufferedWriter(
+ new OutputStreamWriter(socket.getOutputStream()));
+ checkConnectionGreetings();
+ login();
+ }
+
+ /**
+ * Reads the connection greetings messages from the FTP server
+ * checks the response code.
+ *
+ * @throws Exception if the response code is not for success.
+ */
+ private void checkConnectionGreetings() throws Exception
+ {
+ String code = getResponseCode();
+ if(!code.equals("220"))
+ {
+ throw new Exception("Connection Error.");
+ }
+ }
+
+ /**
+ * Reads the response messages from the FTP server and
+ * returns the response code.
+ *
+ * @return the response code.
+ * @throws Exception if <tt>readLine</tt> fails.
+ */
+ private String getResponseCode() throws Exception
+ {
+ String line;
+ while((line = readLine()).charAt(3) != ' ');
+
+ return line.substring(0, 3);
+ }
+
+ /**
+ * Executes the login sequence of FTP commands based on RFC 959.
+ *
+ * @throws Exception if the login fails.
+ */
+ private void login() throws Exception
+ {
+ sendLine("USER " + user);
+ String code = getResponseCode();
+ if(code.equals("331") || code.equals("332"))
+ {
+ sendLine("PASS " + pass);
+ code = getResponseCode();
+ if(!code.equals("230"))
+ {
+ throw new Exception("Login error.");
+ }
+ }
+ else if(!code.equals("230"))
+ {
+ throw new Exception("Login error.");
+ }
+ }
+
+ /**
+ * Sends FTP command for the size of the file and reads and parses
+ * the response from the the FTP server.
+ *
+ * @return the size of the file in bytes.
+ * @throws Exception if <tt>readLine</tt> fails.
+ */
+ public long getSize() throws Exception
+ {
+ sendLine("SIZE " + path);
+
+ String line = readLine();
+ if(!line.startsWith("213 "))
+ {
+ throw new Exception("Size Error.");
+ }
+
+ String fileSizeStr = line.substring(4);
+ long fileSize = INVALID_FILE_SIZE;
+
+ try
+ {
+ fileSize = Long.parseLong(fileSizeStr);
+ }
+ catch (NumberFormatException e)
+ {
+ return INVALID_FILE_SIZE;
+ }
+
+ return fileSize;
+ }
+
+ /**
+ * Parses the URL to host, port, user, password and path to the file parts.
+ *
+ * @param urlString the URL of the file.
+ * @throws Exception if the parsing of the URL fails.
+ */
+ private void parseUrl(String urlString) throws Exception
+ {
+ URL url = new URL(urlString);
+
+ host = url.getHost();
+ port = url.getPort();
+ if (port == -1)
+ {
+ port = DEFAULT_PORT;
+ }
+
+ String tmpUserInfo = url.getUserInfo();
+ if(tmpUserInfo != null)
+ {
+ int separatorIdx = tmpUserInfo.lastIndexOf(':');
+ if (separatorIdx != -1)
+ {
+ pass = tmpUserInfo.substring(separatorIdx+1);
+ user = tmpUserInfo.substring(0, separatorIdx);
+ }
+ }
+
+ if(user == null)
+ {
+ user ="anonymus";
+ }
+
+ if(pass == null)
+ {
+ pass ="anonymus";
+ }
+
+ path = url.getPath();
+ if(path == "")
+ {
+ throw new Exception("Not available path.");
+ }
+ }
+
+ /**
+ * Disconnects from the FTP server.
+ */
+ public void disconnect() throws IOException {
+ sendLine("QUIT");
+ }
+
+ /**
+ * Sends a raw command to the FTP server.
+ *
+ * @param line the message that will be send to the FTP server.
+ * @throws IOException if sending fails.
+ */
+ private void sendLine(String line) throws IOException {
+ writer.write(line + "\r\n");
+ writer.flush();
+ }
+
+ /**
+ * Reads a line from the response of the FTP server.
+ *
+ * @return line from the response of the FTP server.
+ * @throws IOException if reading fails.
+ */
+ private String readLine() throws IOException {
+ String line = reader.readLine();
+ return line;
+ }
+} \ No newline at end of file