webim-to-jabber transport application

git-svn-id: https://webim.svn.sourceforge.net/svnroot/webim/trunk@496 c66351dc-e62f-0410-b875-e3a5c0b9693f
This commit is contained in:
Evgeny Gryaznov 2009-05-24 22:46:07 +00:00
parent ce538f961f
commit fe9a5a606e
17 changed files with 701 additions and 0 deletions

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="libs/smack.jar"/>
<classpathentry kind="lib" path="libs/smackx.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.mibew.jabber</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,9 @@
#
# Configuration file for Mibew-to-jabber gate
#
jabber.host=jabber.org
jabber.login=webim_notifier
jabber.password=123
jabber.admin=yourmail@jabber.org

View File

@ -0,0 +1,150 @@
package org.mibew.api;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.mibew.api.handlers.LoginHandler;
/**
* @author inspirer
*/
public class MibewConnection {
private static final int REQUEST_TIMEOUT = 5000;
private final String fUrl;
private final Map<String,String> fCookies;
/**
* @param url Ex: http://yourserver.com/webim/
* @param login admin
* @param password operators password
*/
public MibewConnection(String url, String login, String password)
throws UnsupportedEncodingException, NoSuchAlgorithmException,
MalformedURLException {
this.fUrl = url;
this.fCookies = new HashMap<String,String>();
this.fCookies.put("webim_lite", URLEncoder.encode(login + "," + Utils.md5(Utils.md5(password)), "UTF-8"));
}
/**
* Connects to the server and tries to login, returns true if successful.
*/
public boolean connect() throws ParserConfigurationException {
try {
MibewResponse response = request("operator/autologin.php", "");
SAXParser sp = SAXParserFactory.newInstance().newSAXParser();
LoginHandler handler = new LoginHandler();
sp.parse(new ByteArrayInputStream(response.getResponse()), handler);
String status = handler.getStatus();
return status.equals("OK");
} catch(Exception e) {
handleError(e.getMessage(), e);
return false;
}
}
public void disconnect() {
}
/**
* Request server.
* @param suburl ex: operator/update.php
* @param urlParameters post content
*/
public final synchronized MibewResponse request(String suburl, String urlParameters) throws IOException {
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) new URL(fUrl+suburl).openConnection();
connection.setConnectTimeout(REQUEST_TIMEOUT);
connection.setReadTimeout(REQUEST_TIMEOUT);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
if(fCookies.size() > 0) {
StringBuilder sb = new StringBuilder();
for(Entry<String,String> cookie : fCookies.entrySet()) {
if(sb.length() > 0) {
sb.append("; ");
}
sb.append(cookie.getKey()+"="+cookie.getValue());
}
connection.addRequestProperty("Cookie", sb.toString());
}
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
// create request
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
// read response
InputStream is = connection.getInputStream();
int len = connection.getContentLength();
ByteArrayOutputStream buffer = new ByteArrayOutputStream(len < 256 ? 256 : len);
byte b[] = new byte[1024];
int size = 0;
while((size=is.read(b)) >= 0) {
buffer.write(b, 0, size);
}
is.close();
// load cookies
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
if(cookies != null) {
for(String cookie : cookies) {
Matcher matcher = COOKIESET.matcher(cookie);
if(matcher.find()) {
String name = matcher.group(1);
String value = matcher.group(2);
fCookies.put(name, value);
}
}
}
return new MibewResponse(connection.getResponseCode(), buffer.toByteArray());
} catch (Exception e) {
if(e instanceof IOException) {
throw (IOException)e;
}
throw new IOException("cannot connect: " + e.getMessage());
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
protected void handleError(String message, Exception ex) {
System.err.println(message);
}
private static Pattern COOKIESET = Pattern.compile("\\A(\\w+)=([^;]+);");
}

View File

@ -0,0 +1,50 @@
package org.mibew.api;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
/**
* @author inspirer
*/
public class MibewResponse {
public int code;
public byte[] response;
public MibewResponse(int code, byte[] response) {
this.code = code;
this.response = response;
}
public int getCode() {
return code;
}
public byte[] getResponse() {
return response;
}
public String getResponseText() {
try {
Reader r = new InputStreamReader(new ByteArrayInputStream(response), "UTF-8");
StringBuilder sb = new StringBuilder();
char[] c = new char[1024];
int size = 0;
while((size = r.read(c)) != -1) {
sb.append(c, 0, size);
}
return sb.toString();
} catch(IOException ex) {
return "<exception is thrown: "+ex.toString()+">";
}
}
@Override
public String toString() {
return
"Response code: " + code + "\n" +
"Text: " + getResponseText();
}
}

View File

@ -0,0 +1,38 @@
package org.mibew.api;
/**
* @author inspirer
*/
public class MibewThread {
public final long fId;
public String fState;
public String fClientName = "";
public String fAgent = "";
public String fAddress = "";
public String fFirstMessage = "";
public boolean fCanOpen = false;
public boolean fCanView = false;
public boolean fCanBan = false;
public String fStateText;
public MibewThread(long id, String state) {
fId = id;
fState = state;
}
public void updateFrom(MibewThread updated) {
if(fId != updated.fId) {
throw new IllegalArgumentException("different threads");
}
fState = updated.fState;
fClientName = updated.fClientName;
fAgent = updated.fAgent;
fAddress = updated.fAddress;
fFirstMessage = updated.fFirstMessage;
fCanOpen = updated.fCanOpen;
fCanView = updated.fCanView;
fCanBan = updated.fCanBan;
fStateText = updated.fStateText;
}
}

View File

@ -0,0 +1,85 @@
package org.mibew.api;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.mibew.api.handlers.UpdateHandler;
/**
* @author inspirer
*/
public class MibewTracker {
private final MibewConnection fConnection;
private final MibewTrackerListener fListener;
private long fSince = 0;
private long fLastUpdate = 0;
private final Map<Long,MibewThread> fThreads;
public MibewTracker(MibewConnection conn, MibewTrackerListener listener) {
this.fConnection = conn;
this.fListener = listener;
this.fThreads = new HashMap<Long, MibewThread>();
}
public void track() throws InterruptedException {
for(int i = 0; i < 5; i++) {
try {
MibewResponse response = fConnection.request("operator/update.php", "since="+fSince);
SAXParser sp = SAXParserFactory.newInstance().newSAXParser();
UpdateHandler handler = new UpdateHandler();
sp.parse(new ByteArrayInputStream(response.getResponse()), handler);
handleResponse(response, handler);
} catch(Exception e) {
System.err.println("update exception: " + e.getMessage());
}
Thread.sleep(1000);
}
}
private void handleResponse(MibewResponse response, UpdateHandler handler) {
if(handler.getResponse() == UpdateHandler.UPD_ERROR) {
System.out.println("Update error: " + handler.getMessage());
} else if(handler.getResponse() == UpdateHandler.UPD_THREADS) {
System.out.println("Updated.... " + handler.getRevision());
fSince = handler.getRevision();
fLastUpdate = handler.getTime();
List<MibewThread> threads = handler.getThreads();
if(threads != null && threads.size() > 0) {
processUpdate(threads);
}
} else {
System.out.println("Update error");
System.out.println(response.getResponseText());
}
}
private void processUpdate(List<MibewThread> updated) {
for(MibewThread mt : updated) {
MibewThread existing = fThreads.get(mt.fId);
boolean isClosed = mt.fState.equals("closed");
if(existing == null) {
if(!isClosed) {
fThreads.put(mt.fId, mt);
fListener.threadCreated(mt);
}
} else if(isClosed) {
fThreads.remove(mt.fId);
fListener.threadClosed(existing);
} else {
existing.updateFrom(mt);
fListener.threadChanged(existing);
}
}
}
public long getLastUpdate() {
return fLastUpdate;
}
}

View File

@ -0,0 +1,18 @@
package org.mibew.api;
/**
* @author inspirer
*/
public abstract class MibewTrackerListener {
public void threadCreated(MibewThread thread) {
}
public void threadChanged(MibewThread thread) {
}
public void threadClosed(MibewThread thread) {
}
}

View File

@ -0,0 +1,31 @@
package org.mibew.api;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author inspirer
*/
public class Utils {
private static final String HEX_DIGITS = "0123456789abcdef";
public static String md5(String string) throws NoSuchAlgorithmException, UnsupportedEncodingException {
return md5(string.getBytes("utf-8"));
}
private static String md5(byte[] string) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance( "MD5" );
md.update(string);
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder();
for ( byte b : digest ) {
sb.append(HEX_DIGITS.charAt((b&0xff)>>4));
sb.append(HEX_DIGITS.charAt(b&0xf));
}
return sb.toString();
}
}

View File

@ -0,0 +1,41 @@
package org.mibew.api.handlers;
import java.util.Stack;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* @author inspirer
*/
public class LoginHandler extends DefaultHandler {
private Stack<String> fPath = new Stack<String>();
private String status = "";
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
fPath.push(name);
}
@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
fPath.pop();
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
String current = fPath.peek();
if(fPath.size() != 2 || !current.equals("status") || !fPath.get(0).equals("login")) {
throw new SAXException("unexpected characters");
}
status += new String(ch,start,length);
}
public String getStatus() {
return status;
}
}

View File

@ -0,0 +1,137 @@
package org.mibew.api.handlers;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.mibew.api.MibewThread;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* @author inspirer
*/
public class UpdateHandler extends DefaultHandler {
public static final int UPD_ERROR = 1;
public static final int UPD_THREADS = 2;
private int fResponse = 0;
private String fMessage = "";
private long fRevision;
private long fTime;
private List<MibewThread> fUpdated;
private Stack<String> fPath = new Stack<String>();
private MibewThread fCurrentThread;
@Override
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
try {
if (fPath.size() == 0) {
if (name.equals("error")) {
fResponse = UPD_ERROR;
} else if (name.equals("threads")) {
fResponse = UPD_THREADS;
fTime = Long.parseLong(attributes.getValue("time"));
fRevision = Long.parseLong(attributes.getValue("revision"));
} else {
throw new SAXException("unknown root element: " + name);
}
}
if (fResponse == UPD_THREADS && fPath.size() == 1
&& name.equals("thread")) {
long id = Long.parseLong(attributes.getValue("id"));
String stateid = attributes.getValue("stateid");
fCurrentThread = new MibewThread(id, stateid);
if(!stateid.equals("closed")) {
fCurrentThread.fStateText = attributes.getValue("state");
fCurrentThread.fCanOpen = booleanAttribute(attributes.getValue("canopen"));
fCurrentThread.fCanView = booleanAttribute(attributes.getValue("canview"));
fCurrentThread.fCanBan = booleanAttribute(attributes.getValue("canban"));
}
}
} catch (NumberFormatException ex) {
throw new SAXException(ex.getMessage());
}
fPath.push(name);
}
private boolean booleanAttribute(String value) {
if(value != null && value.equals("true")) {
return true;
}
return false;
}
@Override
public void endElement(String uri, String localName, String name)
throws SAXException {
fPath.pop();
if (fResponse == UPD_THREADS && fPath.size() == 1
&& name.equals("thread")) {
if(fUpdated == null) {
fUpdated = new ArrayList<MibewThread>();
}
fUpdated.add(fCurrentThread);
fCurrentThread = null;
}
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if (fResponse == UPD_ERROR) {
String current = fPath.peek();
if (fPath.size() != 2 || !current.equals("descr")) {
throw new SAXException("unexpected characters");
}
fMessage += new String(ch, start, length);
} else if (fResponse == UPD_THREADS) {
if(fCurrentThread == null || fPath.size() != 3) {
throw new SAXException("unknown characters");
}
String subvar = fPath.peek();
String value = new String(ch, start, length);
if("name".equals(subvar)) {
fCurrentThread.fClientName += value;
} else if("addr".equals(subvar)) {
fCurrentThread.fAddress += value;
} else if("message".equals(subvar)) {
fCurrentThread.fFirstMessage += value;
} else if("agent".equals(subvar)) {
fCurrentThread.fAgent += value;
}
// TODO
} else {
throw new SAXException("unexpected characters: no root");
}
}
public int getResponse() {
return fResponse;
}
public String getMessage() {
return fMessage;
}
public long getTime() {
return fTime;
}
public long getRevision() {
return fRevision;
}
public List<MibewThread> getThreads() {
return fUpdated;
}
}

View File

@ -0,0 +1,63 @@
package org.mibew.jabber;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import javax.xml.parsers.ParserConfigurationException;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.mibew.api.MibewConnection;
import org.mibew.api.MibewThread;
import org.mibew.api.MibewTracker;
import org.mibew.api.MibewTrackerListener;
import org.xml.sax.SAXException;
/**
* @author inspirer
*/
public class Application {
public static void main(String[] args) throws NoSuchAlgorithmException, IOException, XMPPException, InterruptedException, ParserConfigurationException, SAXException {
System.out.println("Mibew Jabber transport application");
Parameters p = new Parameters(args);
if(!p.load()) {
return;
}
XMPPConnection connection = new XMPPConnection(p.fJabberServer);
connection.connect();
connection.login(p.fJabberLogin, p.fJabberPassword);
final Chat chat = connection.getChatManager().createChat(p.fJabberAdmin, new MessageListener() {
public void processMessage(Chat chat, Message message) {
System.out.println("Received message: " + message.getThread() + " " + message.getBody());
}
});
MibewConnection conn = new MibewConnection("http://localhost:8080/webim/", "admin", "");
if(!conn.connect()) {
System.err.println("Wrong server, login or password.");
return;
}
MibewTracker mt = new MibewTracker(conn, new MibewTrackerListener(){
@Override
public void threadCreated(MibewThread thread) {
try {
chat.sendMessage(thread.fId + ": " + thread.fAddress + " " + thread.fClientName);
} catch (XMPPException e) {
e.printStackTrace();
}
}
});
mt.track();
connection.disconnect();
}
}

View File

@ -0,0 +1,54 @@
package org.mibew.jabber;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @author inspirer
*/
public class Parameters {
private final String[] fArguments;
public String fJabberServer;
public String fJabberLogin;
public String fJabberPassword;
public String fJabberAdmin;
public Parameters(String[] args) {
this.fArguments = args;
}
private String getProperty(Properties p, String name) throws IOException {
String result = p.getProperty(name);
if(result == null || result.trim().length() == 0) {
throw new IOException("No '"+name+"' property");
}
return result;
}
public boolean load() {
try {
InputStream is = getClass().getClassLoader().getResourceAsStream("mibew.ini");
if(is != null) {
Properties p = new Properties();
p.load(is);
fJabberServer = getProperty(p, "jabber.host");
fJabberLogin = getProperty(p, "jabber.login");
fJabberPassword = getProperty(p, "jabber.password");
fJabberAdmin = getProperty(p, "jabber.admin");
return true;
}
} catch (IOException e) {
System.err.println(e.getMessage());
return false;
}
System.err.println("Cannot find mibew.ini");
return false;
}
}