package org.voicebridge;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.net.InetAddress;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.muc.*;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.*;
import org.xmpp.packet.*;
public class Config implements MUCEventListener {
private static final Logger Log = LoggerFactory.getLogger(Config.class);
private MultiUserChatManager mucManager;
private HashMap<String, Conference> conferences;
private HashMap<String, Conference> confExtensions;
private ArrayList<ProxyCredentials> registrations;
private ArrayList<String> registrars;
private HashMap<String, ProxyCredentials> sipExtensions;
private static Config singletonConfig;
public static boolean sipPlugin = false;
private String privateHost = JiveGlobals.getProperty("voicebridge.default.private.host", "127.0.0.1");
private String publicHost = JiveGlobals.getProperty("voicebridge.default.public.host", "127.0.0.1");
private String conferenceExten = JiveGlobals.getProperty("voicebridge.default.conf.exten", "default");
private String defaultProxy = JiveGlobals.getProperty("voicebridge.default.proxy.name", null);
private String defaultProtocol = JiveGlobals.getProperty("voicebridge.default.protocol", "udp");
private String defaultSIPPort = JiveGlobals.getProperty("voicebridge.default.sip.port", "5060");
private boolean prefixPhoneNumber = true;
private String internationalPrefix = "00"; // for international calls
private String longDistancePrefix = "0"; // for long Distancee
private String outsideLinePrefix = "9"; // for outside line
private int internalExtenLength = 5;
private Config() {
}
public void initialise()
{
conferences = new HashMap<String, Conference>();
confExtensions = new HashMap<String, Conference>();
sipExtensions = new HashMap<String, ProxyCredentials>();
registrations = new ArrayList<ProxyCredentials>();
registrars = new ArrayList<String>();
MUCEventDispatcher.addListener(this);
try {
Log.info(String.format("VoiceBridge read site configuration"));
mucManager = XMPPServer.getInstance().getMultiUserChatManager();
if (mucManager.getMultiUserChatService("conference") != null)
{
List<MUCRoom> rooms = mucManager.getMultiUserChatService("conference").getChatRooms();
for (MUCRoom room : rooms)
{
createConference(room);
}
}
String username = JiveGlobals.getProperty("voicebridge.default.proxy.username", null);
if (defaultProxy != null)
{
if (username != null)
{
if (JiveGlobals.getBooleanProperty("voicebridge.register.all.users", false))
{
processRegistrations();
} else {
processDefaultRegistration(username);
}
Log.info(String.format("VoiceBridge sip plugin assumed available"));
sipPlugin = true;
} else {
registerWithDefaultProxy();
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
public void terminate()
{
MUCEventDispatcher.removeListener(this);
}
// ------------------------------------------------------------------------
//
// Registrations management
//
// ------------------------------------------------------------------------
private void registerWithDefaultProxy()
{
ProxyCredentials sipAccount = new ProxyCredentials();
try {
String name = defaultProxy;
String username = JiveGlobals.getProperty("voicebridge.default.proxy.username", "admin");
String sipusername = JiveGlobals.getProperty("voicebridge.default.proxy.sipusername", name);
String authusername = JiveGlobals.getProperty("voicebridge.default.proxy.sipauthuser", null);
String displayname = JiveGlobals.getProperty("voicebridge.default.proxy.sipdisplayname", name);
String password = JiveGlobals.getProperty("voicebridge.default.proxy.sippassword", name);
String server = JiveGlobals.getProperty("voicebridge.default.proxy.sipserver");
String stunServer = JiveGlobals.getProperty("voicebridge.default.proxy.stunserver", server);
String stunPort = JiveGlobals.getProperty("voicebridge.default.proxy.stunport");
String voicemail = JiveGlobals.getProperty("voicebridge.default.proxy.voicemail", name);
String outboundproxy = JiveGlobals.getProperty("voicebridge.default.proxy.outboundproxy", server);
sipAccount.setName(name);
sipAccount.setXmppUserName(username);
sipAccount.setUserName(sipusername);
sipAccount.setAuthUserName(authusername);
sipAccount.setUserDisplay(displayname);
sipAccount.setPassword(password.toCharArray());
sipAccount.setHost(server);
sipAccount.setProxy(outboundproxy);
sipAccount.setRealm(server);
sipExtensions.put(username, sipAccount);
InetAddress inetAddress = InetAddress.getByName(sipAccount.getHost());
registrars.add(sipAccount.getHost());
registrations.add(sipAccount);
Log.info(String.format("VoiceBridge adding SIP registration: %s with user %s host %s", sipAccount.getXmppUserName(), sipAccount.getUserName(), sipAccount.getHost()));
} catch (Exception e) {
Log.info("registerWithDefaultProxy " + e);
}
}
private void processDefaultRegistration(String username)
{
String sql = "SELECT username, sipusername, sipauthuser, sipdisplayname, sippassword, sipserver, enabled, status, stunserver, stunport, usestun, voicemail, outboundproxy, promptCredentials FROM ofSipUser WHERE USERNAME = '" + username + "'";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = DbConnectionManager.createScrollablePreparedStatement(con, sql);
ResultSet rs = pstmt.executeQuery();
DbConnectionManager.scrollResultSet(rs, 0);
if (rs.next())
{
ProxyCredentials credentials = read(rs);
credentials.setName(defaultProxy);
try {
InetAddress inetAddress = InetAddress.getByName(credentials.getHost());
registrars.add(credentials.getHost());
registrations.add(credentials);
Log.info(String.format("VoiceBridge adding SIP registration: %s with user %s host %s", credentials.getXmppUserName(), credentials.getUserName(), credentials.getHost()));
} catch (Exception e) {
Log.info(String.format("processDefaultRegistration Bad Address %s ", credentials.getHost()));
}
}
rs.close();
pstmt.close();
con.close();
} catch (SQLException e) {
Log.info("processDefaultRegistration " + e);
}
}
private void processRegistrations()
{
String sql = "SELECT username, sipusername, sipauthuser, sipdisplayname, sippassword, sipserver, enabled, status, stunserver, stunport, usestun, voicemail, outboundproxy, promptCredentials FROM ofSipUser ORDER BY USERNAME";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = DbConnectionManager.getConnection();
pstmt = DbConnectionManager.createScrollablePreparedStatement(con, sql);
ResultSet rs = pstmt.executeQuery();
DbConnectionManager.scrollResultSet(rs, 0);
while (rs.next())
{
ProxyCredentials credentials = read(rs);
try {
InetAddress inetAddress = InetAddress.getByName(credentials.getHost());
registrars.add(credentials.getHost());
registrations.add(credentials);
Log.info(String.format("VoiceBridge adding SIP registration: %s with user %s host %s", credentials.getXmppUserName(), credentials.getUserName(), credentials.getHost()));
} catch (Exception e) {
Log.info(String.format("processRegistrations Bad Address %s ", credentials.getHost()));
}
}
rs.close();
pstmt.close();
con.close();
} catch (SQLException e) {
Log.info("processRegistrations " + e);
}
}
private ProxyCredentials read(ResultSet rs)
{
ProxyCredentials sipAccount = new ProxyCredentials();
try {
String username = rs.getString("username");
String sipusername = rs.getString("sipusername");
String authusername = rs.getString("sipauthuser");
String displayname = rs.getString("sipdisplayname");
String password = rs.getString("sippassword");
String server = rs.getString("sipserver");
String stunServer = rs.getString("stunserver");
String stunPort = rs.getString("stunport");
String voicemail = rs.getString("voicemail");
String outboundproxy = rs.getString("outboundproxy");
sipAccount.setName(username);
sipAccount.setXmppUserName(username);
sipAccount.setUserName(sipusername);
sipAccount.setAuthUserName(authusername);
sipAccount.setUserDisplay(displayname);
sipAccount.setPassword(password.toCharArray());
sipAccount.setHost(server);
sipAccount.setProxy(outboundproxy);
sipAccount.setRealm(server);
sipExtensions.put(username, sipAccount);
} catch (SQLException e) {
Log.info("ProxyCredentials " + e);
}
return sipAccount;
}
public static void updateStatus(String username, String status) throws SQLException {
if (sipPlugin)
{
String sql = "UPDATE ofSipUser SET status = ?, enabled = ? WHERE username = ?";
Connection con = null;
PreparedStatement psmt = null;
try {
con = DbConnectionManager.getConnection();
psmt = con.prepareStatement(sql);
psmt.setString(1, status);
psmt.setInt(2, 1);
psmt.setString(3, username);
psmt.executeUpdate();
} catch (SQLException e) {
Log.info("updateStatus " + e);
} finally {
DbConnectionManager.closeConnection(psmt, con);
}
}
}
public static void createCallRecord(String username, String addressFrom, String addressTo, long datetime, int duration, String calltype) {
if (sipPlugin)
{
Log.info("createCallRecord " + username + " " + addressFrom + " " + addressTo + " " + datetime);
String sql = "INSERT INTO ofSipPhoneLog (username, addressFrom, addressTo, datetime, duration, calltype) values (?, ?, ?, ?, ?, ?)";
Connection con = null;
PreparedStatement psmt = null;
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
psmt = con.prepareStatement(sql);
psmt.setString(1, username);
psmt.setString(2, addressFrom);
psmt.setString(3, addressTo);
psmt.setLong(4, datetime);
psmt.setInt(5, duration);
psmt.setString(6, calltype);
psmt.executeUpdate();
} catch (SQLException e) {
Log.error(e.getMessage(), e);
} finally {
DbConnectionManager.closeConnection(rs, psmt, con);
}
}
}
public static void updateCallRecord(long datetime, int duration) {
if (sipPlugin)
{
Log.info("updateCallRecord " + datetime + " " + duration);
String sql = "UPDATE ofSipPhoneLog SET duration = ? WHERE datetime = ?";
Connection con = null;
PreparedStatement psmt = null;
try {
con = DbConnectionManager.getConnection();
psmt = con.prepareStatement(sql);
psmt.setInt(1, duration);
psmt.setLong(2, datetime);
psmt.executeUpdate();
} catch (SQLException e) {
Log.error(e.getMessage(), e);
} finally {
DbConnectionManager.closeConnection(psmt, con);
}
}
}
public ProxyCredentials getProxyCredentialsByUser(String username)
{
ProxyCredentials sip = null;
if (sipExtensions.containsKey(username))
{
sip = sipExtensions.get(username);
}
return sip;
}
// ------------------------------------------------------------------------
//
// Conference management
//
// ------------------------------------------------------------------------
private void createConference(MUCRoom room)
{
Conference conference = new Conference();
conference.id = room.getName();
conference.pin = room.getPassword();
if (conference.pin != null && conference.pin.length() == 0)
conference.pin = null;
conference.exten = room.getDescription();
int pos = conference.exten.indexOf(":");
if (pos > 0)
conference.exten = conference.exten.substring(0, pos);
else
conference.exten = null;
if (conference.exten != null && conference.exten.length() > 0)
{
confExtensions.put(conference.exten, conference);
}
conferences.put(conference.id, conference);
Log.info(String.format("VoiceBridge create conference: %s with pin %s extension %s", conference.id, conference.pin, conference.exten));
}
private void destroyConference(MUCRoom room)
{
if (conferences.containsKey(room.getName()))
{
Conference conference1 = conferences.remove(room.getName());
conference1 = null;
Conference conference2 = confExtensions.remove(room.getName());
conference2 = null;
Log.info(String.format("VoiceBridge destroy conference: %s", room.getName()));
}
}
public static Config getInstance() {
if (singletonConfig == null) {
singletonConfig = new Config();
}
return singletonConfig;
}
public boolean isValidConference(String id)
{
return conferences.containsKey(id);
}
public boolean isValidConferenceExten(String id)
{
return confExtensions.containsKey(id);
}
public boolean isValidConferencePin(String id, String pin)
{
boolean valid = false;
if (conferences.containsKey(id))
{
Conference conf = conferences.get(id);
valid = conf.pin == null || pin.equals(conf.pin);
}
return valid;
}
public Conference getConferenceByPhone(String phoneNo)
{
Conference conf = null;
if (conferences.containsKey(phoneNo))
{
conf = conferences.get(phoneNo);
} else if (confExtensions.containsKey(phoneNo)) {
conf = confExtensions.get(phoneNo);
}
return conf;
}
public String getMeetingCode(String phoneNo)
{
String id = null;
if (conferences.containsKey(phoneNo))
{
Conference conf = conferences.get(phoneNo);
id = conf.id;
} else if (confExtensions.containsKey(phoneNo)) {
Conference conf = confExtensions.get(phoneNo);
id = conf.id;
}
return id;
}
public String getPassCode(String meetingId, String phoneNo)
{
String pin = null;
if (confExtensions.containsKey(phoneNo))
{
Conference conf = confExtensions.get(phoneNo);
pin = conf.pin;
} else if (conferences.containsKey(meetingId)) {
Conference conf = conferences.get(meetingId);
pin = conf.pin;
}
return pin;
}
public String getPrivateHost()
{
return privateHost;
}
public String getPublicHost()
{
return publicHost;
}
public void setConferenceExten(String conferenceExten)
{
this.conferenceExten = conferenceExten;
}
public String getConferenceExten()
{
return conferenceExten;
}
public void setInternalExtenLength(int internalExtenLength)
{
this.internalExtenLength = internalExtenLength;
}
public int getInternalExtenLength()
{
return internalExtenLength;
}
public void setOutsideLinePrefix(String outsideLinePrefix)
{
this.outsideLinePrefix = outsideLinePrefix;
}
public String getOutsideLinePrefix()
{
return outsideLinePrefix;
}
public void setLongDistancePrefix(String longDistancePrefix)
{
this.longDistancePrefix = longDistancePrefix;
}
public String getLongDistancePrefix()
{
return longDistancePrefix;
}
public void setInternationalPrefix(String internationalPrefix)
{
this.internationalPrefix = internationalPrefix;
}
public String getInternationalPrefix()
{
return internationalPrefix;
}
public void setPrefixPhoneNumber(boolean prefixPhoneNumber)
{
this.prefixPhoneNumber = prefixPhoneNumber;
}
public boolean prefixPhoneNumber()
{
return prefixPhoneNumber;
}
public String getDefaultProxy()
{
return defaultProxy;
}
public String getDefaultProtocol()
{
return defaultProtocol;
}
public String getDefaultSIPPort()
{
return defaultSIPPort;
}
public ArrayList<String> getRegistrars()
{
return registrars;
}
public ArrayList<ProxyCredentials> getRegistrations()
{
return registrations;
}
public String formatPhoneNumber(String phoneNumber, String location)
{
if (phoneNumber == null) {
return null;
}
/*
* It's a softphone number. Leave it as is.
*/
if (phoneNumber.indexOf("sip:") == 0)
{
/*
* There is a problem where Meeting Central gives
* us a phone number with only "sip:" which isn't valid.
* Check for that here.
* XXX
*/
if (phoneNumber.length() < 5) {
return null;
}
return phoneNumber;
}
if (phoneNumber.indexOf("@") >= 0)
{
return "sip:" + phoneNumber;
}
/*
* If number starts with "Id-" it's a callId. Leave it as is.
*/
if (phoneNumber.indexOf("Id-") == 0)
{
return phoneNumber;
}
/*
* Get rid of white space in the phone number
*/
phoneNumber = phoneNumber.replaceAll("\\s", "");
/*
* Get rid of "-" in the phone number
*/
phoneNumber = phoneNumber.replaceAll("-", "");
/*
* For Jon Kaplan who likes to use "." as a phone number separator!
*/
phoneNumber = phoneNumber.replaceAll("\\.", "");
if (phoneNumber.length() == 0)
{
return null;
}
if (prefixPhoneNumber == false) {
return phoneNumber;
}
/*
* Replace leading "+" (from namefinder) with appropriate numbers.
* +1 is a US number and becomes outsideLinePrefix.
* +<anything else> is considered to be an international number and
* becomes internationalPrefix.
*/
if (phoneNumber.charAt(0) == '+')
{
if (phoneNumber.charAt(1) == '1')
{
phoneNumber = outsideLinePrefix + phoneNumber.substring(1);
} else {
phoneNumber = outsideLinePrefix + internationalPrefix + phoneNumber.substring(1);
}
} else if (phoneNumber.charAt(0) == 'x' || phoneNumber.charAt(0) == 'X') {
phoneNumber = phoneNumber.substring(1);
}
if (phoneNumber.length() == internalExtenLength)
{
/*
* This is an internal extension. Determine if it needs
* a prefix of "70".
*/
//phoneNumber = PhoneNumberPrefix.getPrefix(location) + phoneNumber;
} else if (phoneNumber.length() > 7) {
/*
* It's an outside number
*
* XXX No idea what lengths of 8 and 9 would be for...
*/
if (phoneNumber.length() == 10)
{
/*
* It's US or Canada, number needs 91
*/
phoneNumber = outsideLinePrefix + longDistancePrefix + phoneNumber;
} else if (phoneNumber.length() >= 11) {
/*
* If it starts with 9 or 1, it's US or Canada.
* Otherwise, it's international.
*/
if (phoneNumber.length() == 11 && longDistancePrefix.length() > 0 && phoneNumber.charAt(0) == longDistancePrefix.charAt(0))
{
phoneNumber = outsideLinePrefix + phoneNumber;
} else if (phoneNumber.length() == 11 && outsideLinePrefix.length() > 0 && phoneNumber.charAt(0) == outsideLinePrefix.charAt(0)) {
phoneNumber = outsideLinePrefix + longDistancePrefix + phoneNumber.substring(1);
} else if (phoneNumber.length() == 12 && phoneNumber.substring(0,2).equals(outsideLinePrefix + longDistancePrefix)) {
// nothing to do
} else {
/*
* It's international, number needs outsideLinePrefix plus internationalPrefix
*/
if (phoneNumber.substring(0,3).equals(internationalPrefix))
{
/*
* international prefix is already there, just prepend
* outsideLinePrefix
*/
phoneNumber = outsideLinePrefix + phoneNumber;
} else if (!phoneNumber.substring(0,4).equals(outsideLinePrefix + internationalPrefix)) {
phoneNumber = outsideLinePrefix + internationalPrefix + phoneNumber;
}
}
}
}
return phoneNumber;
}
public void roomCreated(JID roomJID)
{
MUCRoom mucRoom = mucManager.getMultiUserChatService(roomJID).getChatRoom(roomJID.getNode());
if (mucRoom != null)
{
createConference(mucRoom);
}
}
public void roomDestroyed(JID roomJID)
{
MUCRoom mucRoom = mucManager.getMultiUserChatService(roomJID).getChatRoom(roomJID.getNode());
if (mucRoom != null)
{
destroyConference(mucRoom);
}
}
public void occupantJoined(JID roomJID, JID user, String nickname)
{
}
public void occupantLeft(JID roomJID, JID user)
{
}
public void nicknameChanged(JID roomJID, JID user, String oldNickname, String newNickname)
{
}
public void messageReceived(JID roomJID, JID user, String nickname, Message message)
{
}
public void roomSubjectChanged(JID roomJID, JID user, String newSubject)
{
}
public void privateMessageRecieved(JID a, JID b, Message message)
{
}
private class Conference
{
public String pin = null;
public String id = null;
public String exten = null;
}
}