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