package com.cari.voip.keyboard.stack;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import com.cari.voip.keyboard.stack.events.Packet;
import com.cari.voip.keyboard.stack.events.PacketFilter;
import com.cari.voip.keyboard.stack.events.PacketListener;
import com.cari.voip.keyboard.stack.events.ReplyListener;
import com.cari.voip.keyboard.stack.events.TrapEventListener;
import com.cari.voip.keyboard.stack.parsers.PacketParseException;
import com.cari.voip.keyboard.stack.parsers.PacketParser;
public class CCKPConnection {
public static final int DEFAULT_SERVER_PORT = 6608;
private ConnectionConfiguration connectionConfig = null;
private boolean isConnected = false;
private boolean isAuthed = false;
private boolean isRunning = false;
private Socket socket;
Writer writer;
Reader reader;
PacketParser parser;
PacketWriter packetWriter;
PacketReader packetReader;
private Semaphore connectionSemaphore;
private Runnable authRunnable = null;
private Thread authThread= null;
private String authReplyTxt = null;
private String authExceptionMessage = null;
String connectionID;
protected final Collection<ConnectionListener> connectionListeners =
new CopyOnWriteArrayList<ConnectionListener>();
public CCKPConnection(String host,int port){
this.connectionConfig = new ConnectionConfiguration();
this.connectionConfig.setServerHost(host);
this.connectionConfig.setServerTcpPort(port);
this.packetReader = new PacketReader(this);
this.packetWriter = new PacketWriter(this);
}
public boolean isConnect(){
return this.isConnected;
}
public boolean isAuthed(){
return this.isAuthed;
}
public void login(String id,String pwd) throws CCKPConnectionException {
this.connectionConfig.setPhoneId(id);
this.connectionConfig.setPwd(pwd);
connectUsingConfiguration();
this.isConnected = true;
this.notifySocketConnectOK();
resetReaderAndWriter();
//login auth
auth();
if(this.isAuthed == true){
this.startup();
this.notifyAuthenticationOK();
}
}
private void connectUsingConfiguration() throws CCKPConnectionException{
try {
this.socket = new Socket(this.connectionConfig.getServerHost(),
this.connectionConfig.getServerTcpPort());
} catch (UnknownHostException e) {
throw new CCKPConnectionException("��������ַ"+
this.connectionConfig.getServerHost()+"���ɴ");
} catch (IOException e) {
throw new CCKPConnectionException("������["+
this.connectionConfig.getServerHost()+":"+
this.connectionConfig.getServerTcpPort()+"]�����ӣ�");
}
}
public boolean isConnected(){
return this.isConnected;
}
protected void resetReaderAndWriter() throws CCKPConnectionException {
if(this.reader != null){
try{
this.reader.close();
}
catch(IOException e){
throw new CCKPConnectionException("�������Ӷ���Դ�ͷŴ���");
}
}
if(this.writer != null){
try{
this.writer.close();
}catch(IOException e){
throw new CCKPConnectionException("��������д��Դ�ͷŴ���");
}
}
if(this.isConnected() == false ){
throw new CCKPConnectionException("����ͨ��δ����");
}
try {
this.reader = new BufferedReader(
new InputStreamReader(this.socket.getInputStream(),"UTF-8")
);
this.writer = new BufferedWriter(
new OutputStreamWriter(this.socket.getOutputStream(),"UTF-8")
);
} catch (UnsupportedEncodingException e) {
throw new CCKPConnectionException("�����벻֧��");
} catch (IOException e) {
throw new CCKPConnectionException("����ͨ�Ŵ���������������");
}
}
protected String getStringFromMD5Byte(byte[] md5){
String ret = "";
char hexDigits[] = { // �������ֽ�ת���� 16 ���Ʊ�ʾ���ַ�
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
if(md5 == null || md5.length != 16){
return ret;
}
try{
StringBuilder builder = new StringBuilder();
for(int i = 0 ;i<16;i++){
byte b = md5[i];
builder.append(hexDigits[(b>>>4)&0xf]);
builder.append(hexDigits[b&0xf]);
}
/*
for(int i = 0 ;i<4;i++){
int b1 = (int)(md5[i*4 + 0]) &0xFF;
int b2 = (int)(md5[i*4 + 1]) &0xFF;
int b3 = (int)(md5[i*4 + 2]) &0xFF;
int b4 = (int)(md5[i*4 + 3]) &0xFF;
int j = (b1<<8) +b2 + b3+ b4;
builder.append(String.valueOf(j));
builder.append("-");
}
builder.deleteCharAt(builder.length() - 1);
*/
ret = builder.toString();
}catch( Exception e )
{
//e.printStackTrace();
}
return ret;
}
protected void authbg() throws CCKPConnectionException{
parser = new PacketParser();
parser.setInput(this.reader);
Packet authRequest;
try {
authRequest = parser.getNextPacket();
} catch (PacketParseException e) {
throw new CCKPConnectionException(e.getMessage());
}
if(authRequest != null &&
authRequest.getPacketType() == Packet.TYPE_AUTH_REQUEST){
this.connectionID = (String) authRequest.getProperty(Packet.PROP_CONNECTION_ID);
if(this.connectionID == null){
throw new CCKPConnectionException("���������֤�������ʽ����");
}
}
else{
throw new CCKPConnectionException("�������û����Ӧ");
}
Packet auth = new Packet(Packet.TYPE_AUTH);
String serviceName = null;
//serviceName = this.connectionConfig.getServiceName();
String phoneId = this.connectionConfig.getPhoneId();
auth.setProperty(Packet.PROP_DISPATCHER, (serviceName== null)?phoneId:phoneId+"@"+serviceName);
String pwd = this.connectionConfig.getPwd();
if(pwd == null){
pwd = "";
}
try
{
java.security.MessageDigest md = java.security.MessageDigest.getInstance( "MD5" );
md.update( pwd.getBytes() );
byte dg[] = md.digest(); // MD5 �ļ�������һ�� 128 λ�ij�������
pwd = getStringFromMD5Byte(dg);
}catch( Exception e )
{
pwd = "error";
}
auth.setProperty(Packet.PROP_PASSWORD, pwd);//+this.connectionID);
try {
String trafer = auth.toString();
this.writer.write(trafer);
this.writer.flush();
} catch (IOException e) {
throw new CCKPConnectionException("��������֤��Ϣ����������������");
}
Packet authReply;
try {
authReply = parser.getNextPacket();
} catch (PacketParseException e) {
throw new CCKPConnectionException(e.getMessage());
}
if(authReply != null &&
authReply.getPacketType() == Packet.TYPE_AUTH_REPLY){
String replyTxt = (String) authReply.getProperty(Packet.PROP_REPLY_TEXT);
if(replyTxt == null){
throw new CCKPConnectionException("���������֤��Ӧ����ʽ����");
}
String replyTxtLowerCase = replyTxt.toLowerCase();
if(replyTxtLowerCase.startsWith(Packet.PROP_REPLY_OK)){
this.isAuthed = true;
}
else if(replyTxtLowerCase.startsWith(Packet.PROP_REPLY_ERROR)){
this.isAuthed = false;
/*int k = replyTxt.indexOf(',');
if(k > 0){
this.authReplyTxt = replyTxt.substring(k+1);
}
String discription = (String) authReply.getProperty(Packet.PROP_ERR_DISPCRIPTION);
if(discription != null){
this.authReplyTxt = discription;
}
*/
this.authReplyTxt = "�ǵ����û���������";
}
else{
throw new CCKPConnectionException("���������֤��Ӧָʾ����");
}
}
else{
throw new CCKPConnectionException("�������û����֤����");
}
}
protected void auth() throws CCKPConnectionException{
if(this.authRunnable == null){
this.authRunnable = new Runnable(){
public void run(){
try {
authbg();
} catch (CCKPConnectionException e) {
authExceptionMessage = e.getLocalizedMessage();
}
catch(Exception e){
//do nothing;
}
if(connectionSemaphore != null){
connectionSemaphore.release();
}
}
};
}
if(this.authThread == null){
this.authThread = new Thread(this.authRunnable);
this.authThread.setName("auth thread");
this.authThread.setDaemon(true);
}
this.authReplyTxt = null;
this.authExceptionMessage = null;
this.connectionID = null;
this.isAuthed = false;
this.connectionSemaphore = new Semaphore(0);
this.authThread.start();
try {
int waitTime = CCKPConfiguration.getPacketReplyTimeout();
this.connectionSemaphore.tryAcquire(6*waitTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
//ignore;
}
try{
this.authThread.interrupt();
}
catch(Exception e){
//do nothing;
}
this.authThread = null;
if(this.connectionID == null){
throw new CCKPConnectionException(
(this.authExceptionMessage==null)?"��������û����Ӧ":this.authExceptionMessage);
}
if(this.isAuthed == false){
throw new CCKPConnectionException(
(this.authExceptionMessage==null)?
((this.authReplyTxt == null)?"��������û����Ӧ":this.authReplyTxt)
:this.authExceptionMessage);
}
}
public void startup(){
if(this.isRunning == false){
this.packetWriter.startup();
this.packetReader.startup();
}
else{
this.packetWriter.resetWriter();
this.packetReader.resetParser();
}
this.isRunning = true;
}
protected void shutdown(){
if(this.isRunning){
for(ConnectionListener listener : this.connectionListeners){
try{
listener.connectionClosed();
}
catch(Exception e){
e.printStackTrace();
}
}
this.packetReader.shutdown();
this.packetWriter.shutdown();
}
this.isRunning = false;
// Wait 150 ms for processes to clean-up, then shutdown.
try {
Thread.sleep(150);
}
catch (Exception e) {
// Ignore.
}
if(this.reader != null){
try {
this.reader.close();
} catch (Throwable e) {
//do nothing
}
this.reader = null;
}
if(this.writer != null){
try {
this.writer.close();
} catch (Throwable e) {
//do nothing
}
this.writer = null;
}
if(this.socket != null){
try {
this.socket.close();
} catch (Throwable e) {
//do nothing
}
this.socket = null;
}
this.isAuthed = false;
this.isConnected = false;
}
public void disconnect(){
this.shutdown();
}
public void cleanup(){
this.packetReader.cleanup();
this.packetReader = null;
this.packetWriter.cleanup();
this.packetWriter = null;
this.connectionListeners.clear();
}
public void sendPacket(Packet packet) throws CCKPConnectionException{
if(packet == null){
return;
}
if(this.isRunning == false){
throw new CCKPConnectionException("����read��write�������");
}
this.packetWriter.sendPacket(packet);
}
public void addConnectionListener(ConnectionListener connectionListener){
if(connectionListener == null){
return;
}
this.connectionListeners.add(connectionListener);
}
public void removeConnectionListener(ConnectionListener connectionListener){
if(connectionListener == null){
return;
}
this.connectionListeners.remove(connectionListener);
}
public void addTrapEventListeners(TrapEventListener trapEventListener,PacketFilter packetFilter){
if(trapEventListener == null){
return;
}
this.packetReader.addTrapEventListeners(trapEventListener, packetFilter);
}
public void removeTrapEventListeners(TrapEventListener trapEventListener){
if(trapEventListener == null){
return;
}
this.packetReader.removeTrapEventListeners(trapEventListener);
}
public void addReplyListeners(ReplyListener replyListener,PacketFilter packetFilter){
if(replyListener == null){
return;
}
this.packetReader.addReplyListeners(replyListener, packetFilter);
}
public void removeReplyListeners(ReplyListener replyListener){
if(replyListener == null){
return;
}
this.packetReader.removeReplyListeners(replyListener);
}
public void addPacketParseExceptionListener(PacketParseExceptionListener listener){
if(listener == null){
return;
}
this.packetReader.addPacketParseExceptionListener(listener);
}
public void remove(PacketParseExceptionListener listener){
if(listener == null){
return;
}
this.packetReader.removePacketParseExceptionListener(listener);
}
public void addPacketSendListener(PacketListener packetListener,PacketFilter packetFilter){
if(packetListener == null){
return;
}
this.packetWriter.addPacketSendListener(packetListener, packetFilter);
}
public void removePakcetSendListener(PacketListener packetListener){
if(packetListener == null){
return;
}
this.packetWriter.removePakcetSendListener(packetListener);
}
public void addPacketWriteListener(PacketListener packetListener,PacketFilter packetFilter){
if(packetListener == null){
return;
}
this.packetWriter.addPacketWriteListener(packetListener, packetFilter);
}
public void removePakcetWriteListener(PacketListener packetListener){
if(packetListener == null){
return;
}
this.packetWriter.removePakcetWriteListener(packetListener);
}
public void notifySocketConnectOK() throws CCKPConnectionException{
for(ConnectionListener listener: this.connectionListeners){
try{
listener.socketConnectSuccessful();
}
catch(Exception e){
throw new CCKPConnectionException("�û��˳�");
}
}
}
public void notifyAuthenticationOK() throws CCKPConnectionException{
for(ConnectionListener listener: this.connectionListeners){
try{
listener.authenticationSuccessful();
}
catch(Exception e){
throw new CCKPConnectionException("�û��˳�");
}
}
}
public void notifyConnectionError(Exception e){
//e.printStackTrace();
this.shutdown();
for(ConnectionListener listener: this.connectionListeners){
try{
listener.connectionClosedOnError(e);
}
catch(Exception e2){
e2.printStackTrace();
}
}
}
}