/*
Copyright (c) Matteo Mazzoni 2012-2014
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.freedomotic.plugins.devices.freedomchat;
import com.skype.ChatMessage;
import com.skype.ChatMessageAdapter;
import com.skype.Skype;
import com.skype.SkypeException;
import com.freedomotic.api.EventTemplate;
import com.freedomotic.api.Protocol;
import com.freedomotic.app.Freedomotic;
import com.freedomotic.exceptions.UnableToExecuteException;
import com.freedomotic.reactions.Command;
import com.google.inject.Inject;
import com.google.inject.Injector;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
public class FreedomChat extends Protocol {
public static String ACCEPTED = "Just wait and see.";
public static String NOT_ACCEPTED = "";
final int POLLING_WAIT;
private String hostname;
private int port;
private String username;
private String password;
private String acceptancePassword;
private boolean acceptAllCertificates;
private boolean useSkype;
private boolean useXMPP;
private boolean manualHostname;
XMPPConnection conn;
private ChatMessageAdapter chatListener;
private final MessageService msg;
@Inject
Injector injector;
public FreedomChat() {
//every plugin needs a name and a manifest XML file
super("Chat console", "/chat/chat-manifest.xml");
//read a property from the manifest file below which is in
//FREEDOMOTIC_FOLDER/plugins/devices/com.freedomotic.hello/hello-world.xml
POLLING_WAIT = configuration.getIntProperty("time-between-reads", -1);
//POLLING_WAIT is the value of the property "time-between-reads" or 2000 millisecs,
//default value if the property does not exist in the manifest
setPollingWait(POLLING_WAIT); //millisecs interval between hardware device status reads
msg = injector.getInstance(MessageService.class);
}
@Override
protected void onStart() {
setDescription("Chat plugin starting");
useSkype = configuration.getBooleanProperty("enable-skype", false);
useXMPP = configuration.getBooleanProperty("enable-XMPP", false);
if (!useSkype && !useXMPP) {
setDescription("Enable XMPP or Skype before starting");
return;
}
String newDescription = "";
acceptancePassword = configuration.getStringProperty("acceptance-password", "");
boolean skypeInited = false;
if (useSkype) {
skypeInited = initSkype();
}
boolean xmppInited = false;
if (useXMPP) {
xmppInited = initXMPP();
if (xmppInited) {
newDescription = "XMPP: " + username + ",accept. pwd: " + acceptancePassword;
}
}
if (xmppInited || skypeInited) {
if (skypeInited) {
setDescription("Skype enabled " + newDescription);
} else {
setDescription(newDescription);
}
LOG.info("Chat plugin has started");
} else {
setDescription("Chat plugin");
}
}
@Override
protected void onStop() {
teardownXMPP();
teardownSkype();
setDescription("Chat plugin");
LOG.info("Chat plugin has stopped ");
}
@Override
protected void onCommand(Command c) throws IOException, UnableToExecuteException {
// Assume we've created a Connection name "connection".
LOG.log(Level.INFO, "Chat plugin receives a command called {0} with parameters {1}", new Object[]{c.getName(), c.getProperties().toString()});
}
@Override
protected boolean canExecute(Command c) {
//don't mind this method for now
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected void onEvent(EventTemplate event) {
//don't mind this method for now
throw new UnsupportedOperationException("Not supported yet.");
}
public static String unsplit(String[] parts, int index, int length, String splitter) {
if (parts == null) {
return null;
}
if ((index < 0) || (index >= parts.length)) {
return null;
}
if (index + length > parts.length) {
return null;
}
StringBuilder buf = new StringBuilder();
for (int i = index; i < index + length; i++) {
if (parts[i] != null) {
buf.append(parts[i]);
}
buf.append(splitter);
}
// remove the trailing splitter
buf.setLength(buf.length() - splitter.length());
return buf.toString();
}
@Override
protected void onRun() {
}
private static final Logger LOG = Logger.getLogger(FreedomChat.class.getName());
private boolean initXMPP() {
manualHostname = configuration.getBooleanProperty("manual-hostname", false);
if (manualHostname) {
hostname = configuration.getStringProperty("hostname", "jabber.org");
port = configuration.getIntProperty("port", 5222);
}
username = configuration.getStringProperty("username", "");
password = configuration.getStringProperty("password", "");
acceptAllCertificates = configuration.getBooleanProperty("accept-all-certificates", false);
ConnectionConfiguration config;
if (manualHostname) {
config = new ConnectionConfiguration(hostname, port);
} else {
config = new ConnectionConfiguration(StringUtils.parseServer(username));
}
config.setCompressionEnabled(true);
if (acceptAllCertificates) {
try {
TLSUtils.acceptAllCertificates(config);
} catch (KeyManagementException e) {
LOG.log(Level.SEVERE, "Cannot start Chat plugin. Reason: {0}", e.getMessage());
return false;
} catch (NoSuchAlgorithmException e) {
LOG.log(Level.SEVERE, "Cannot start Chat plugin. Reason: {0}", e.getMessage());
return false;
}
}
conn = new XMPPTCPConnection(config);
try {
conn.connect();
conn.login(username, password, "Freedomotic");
// Create a new presence. Pass in false to indicate we're unavailable.
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("Ready");
// Send the packet (assume we have a Connection instance called "con").
conn.sendPacket(presence);
// wait for messages
ChatManager chatmanager = ChatManager.getInstanceFor(conn);
chatmanager.addChatListener(new ChatManagerListener() {
@Override
public void chatCreated(Chat chat, boolean createdLocally) {
if (!createdLocally) {
chat.addMessageListener(new MessageListener() {
@Override
public void processMessage(Chat chat, Message message) {
try {
// the user is in my list, OK accepc messages
LOG.info(chat.getParticipant());
String uname[] = chat.getParticipant().split("/");
if (conn.getRoster().getEntry(uname[0]) != null) {
// Send back the same text the other user sent us.
//chat.sendMessage(message.getBody());
chat.sendMessage(msg.manageMessage(message.getBody()));
} else {
// expect a password in order to add user to friends' list
chat.sendMessage(manageSubscription(chat, message));
}
} catch (Exception ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
});
}
}
private String manageSubscription(Chat chat, Message message) {
if (message.getBody().equals(acceptancePassword)) {
try {
conn.getRoster().createEntry(chat.getParticipant(), "", null);
return ACCEPTED;
} catch (Exception ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
return NOT_ACCEPTED;
}
});
} catch (Exception ex) {
LOG.log(Level.SEVERE, "Cannot start Chat plugin. Reason: {0}", ex.getMessage());
teardownXMPP();
return false;
}
return true;
}
private boolean initSkype() {
Skype.setDaemon(false);
try {
chatListener = new ChatMessageAdapter() {
@Override
public void chatMessageReceived(ChatMessage received) {
try {
if (received.getSender().isAuthorized()) {
if (received.getType().equals(ChatMessage.Type.SAID)) {
received.getSender().send(msg.manageMessage(received.getContent()));
}
} else {
received.getSender().send(manageSubscription(received));
}
} catch (SkypeException ex) {
LOG.log(Level.SEVERE, null, ex);
}
}
private String manageSubscription(ChatMessage received) {
try {
if (received.getContent().equals(acceptancePassword)) {
received.getSender().setAuthorized(true);
return ACCEPTED;
}
} catch (SkypeException ex) {
LOG.log(Level.SEVERE, null, ex);
}
return NOT_ACCEPTED;
}
};
Skype.addChatMessageListener(chatListener);
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage());
teardownSkype();
return false;
}
return true;
}
private void teardownXMPP() {
// Disconnect from the server
if (conn != null && conn.isConnected()) {
try {
conn.disconnect();
} catch (NotConnectedException e) {
// Already disconnected, can ignore
}
}
conn = null;
}
private void teardownSkype() {
try {
Skype.removeChatMessageListener(chatListener);
} catch (Exception ex) {
LOG.log(Level.SEVERE, ex.getMessage());
}
Skype.setDaemon(true);
}
}