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").

cusax-fix
hristoterezov 12 years ago
parent f02a12d22d
commit a16097f5cd

@ -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

@ -8,6 +8,8 @@
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.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.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;
@ -647,6 +650,24 @@ public static SmiliesReplacementService getSmiliesReplacementSource()
return smiliesService;
}
/**
* 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.

@ -33,6 +33,7 @@
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 ChatConversationPanel(ChatConversationContainer chatContainer)
this.chatTextPane.setCursor(
Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
this.addChatLinkClickedListener(showPreview);
this.setWheelScrollingEnabled(true);
this.setViewportView(chatTextPane);
@ -818,6 +824,7 @@ private void finishMessageAdd(String message, String contentType)
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 @@ private void finishMessageAdd(String message, String contentType)
* @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 void finished()
if (newMessage != null && !newMessage.equals(chatString))
{
showPreview.getMsgIDToChatString().put(
messageID, newMessage);
synchronized (scrollToBottomRunnable)
{
scrollToBottomIsPending = true;
@ -884,6 +893,10 @@ public Object construct() throws Exception
= 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 Object construct() throws Exception
closingTag = divMatcher.group(3);
}
int linkCounter = 0;
for (Map.Entry<String, ReplacementService> entry
: GuiActivator.getReplacementSources().entrySet())
{
@ -902,13 +916,12 @@ public Object construct() throws Exception
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 Object construct() throws Exception
sourcePattern,
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
Matcher m = p.matcher(msgStore);
StringBuilder msgBuff = new StringBuilder();
int startPos = 0;
@ -929,31 +941,86 @@ public Object construct() throws Exception
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
{

@ -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;
}
}

@ -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,

@ -9,8 +9,10 @@
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.*;
/**
@ -37,6 +39,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
*
@ -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 void start(BundleContext context) throws Exception
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;
}
}

@ -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 @@
* image links.
*
* @author Purvesh Sahoo
* @author Marin Dzhigarov
*/
public class ReplacementServiceDirectImageImpl
implements ReplacementService
implements DirectImageReplacementService
{
/**
* The logger for this class.
@ -39,14 +45,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.
*
@ -77,4 +131,85 @@ public String getPattern()
{
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;
}
}

@ -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

@ -66,7 +66,7 @@ public String getReplacement(String sourceString)
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;

@ -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 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;
}
/**

@ -44,6 +44,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.
*/
@ -112,6 +117,23 @@ public void actionPerformed(ActionEvent e)
}
});
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 @@ private void initValues()
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 @@ private void saveData()
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);
}

@ -12,6 +12,12 @@
*/
public final class ReplacementProperty
{
/**
* The replacement proposal property.
*/
public static final String REPLACEMENT_PROPOSAL =
"plugin.chatconfig.replacement.proposal.enable";
/**
* The replacement property.
*/

@ -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);
}

@ -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

@ -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;
}
}
Loading…
Cancel
Save