/* * Settings.java * * Created on Mar 22, 2010, 6:03 PM * * @author pquiring * */ import java.io.*; import java.util.*; import javaforce.*; import javaforce.voip.*; /** Keeps current settings and provides methods to load/save settings to an XML file. */ public class Settings { public static Settings current; public static class Line { public int same; //0-5 (-1=disabled) (ignored for lines[0]) public String name, user, auth, pass, host; public boolean disableVideo, srtp, dtls; public int transport; public Line() { same = 0; name = new String(); user = new String(); auth = new String(); pass = new String(); host = new String(); disableVideo = false; srtp = false; dtls = false; transport = 0; //0=UDP 1=TCP 2=TLS } } public int WindowXPos = 0; public int WindowYPos = 0; public Line lines[]; public String sipcontacts[] = new String[0]; public String callLog[] = new String[0]; public String dndCodeOn = "*78", dndCodeOff = "*79"; public boolean swVolForce = false; public boolean resample441k = false; public boolean checkVersion = true; public String audioInput = "<default>", audioOutput = "<default>"; public boolean disableLogging = false; public boolean hideWhenMinimized = true; public boolean exitWhenClosed = false; public boolean alwaysOnTop = false; public boolean keepAudioOpen = false; public String downloadPath = JF.getUserPath() + "/Downloads"; public boolean smallerFont = false; //some JVMs have different fonts public String videoDevice = "<default>"; public String videoResolution = "<default>"; public String videoPosition = "<default>"; public int videoFPS = 5; public boolean usePublish = false; public boolean speakerMode = false; public int speakerThreshold = 1000; //0-32k public int speakerDelay = 250; //ms public boolean disableEnhanced = false; public String audioCodecs = "G729,PCMU"; public String videoCodecs = "VP8,H264,H263-1998,H263-2000,JPEG"; //BUG:H263 incomplete public boolean reinvite = true; //reinvite when returned multiple codecs public boolean autohold = false; //auto hold/unhold when switching between active lines public int volPlaySW = 75, volRecSW = 75; //playback / recording vol levels (software) public int volPlayHW = 100, volRecHW = 100; //playback / recording vol levels (hardware) (obsolete - removed in 1.1.0) public boolean nativeVideo = false; public int sipexpires = 3600; //in seconds : min=300 (5mins) max=3600 (1hr) public boolean sipRange = false; //specify SIP port range (min 32 ports) public int sipmin = 5061, sipmax = 5199; public boolean rtpRange = false; //specify RTP port range (min 128 ports) public int rtpmin = 32768, rtpmax = 65535; public int nat = 0; //None by default public boolean natPrivate = false; public String natHost = "", natUser = "", natPass = ""; public boolean rport = true; public boolean received = true; public String inRingtone = "*RING", outRingtone = "*NA"; public boolean welcome = true; //static = do not save these settings public static boolean aa; public static boolean ac; public static boolean isApplet = false; public static boolean isLinux = false; public static boolean isWindows = false; public static boolean isJavaScript = false; public static boolean hasFFMPEG = false; private void initLines() { lines = new Line[6]; for(int a=0;a<6;a++) lines[a] = new Line(); } public static void loadSettings() { String fn = JF.getUserPath() + "/.jphone.xml"; try { current = new Settings(); XML xml = new XML(); xml.read(new FileInputStream(fn)); xml.writeClass(current); if (current.lines == null || current.lines.length != 6) throw new Exception("invalid config"); //force settings if (!current.hasAudioCodec(RTP.CODEC_G711a) && !current.hasAudioCodec(RTP.CODEC_G711u) && !current.hasAudioCodec(RTP.CODEC_GSM) && !current.hasAudioCodec(RTP.CODEC_G722) && !current.hasAudioCodec(RTP.CODEC_G729a) ) { current.audioCodecs = "G729,PCMU"; } if (!current.hasVideoCodec(RTP.CODEC_JPEG) && !current.hasVideoCodec(RTP.CODEC_H263) && !current.hasVideoCodec(RTP.CODEC_H263_1998) && !current.hasVideoCodec(RTP.CODEC_H263_2000) && !current.hasVideoCodec(RTP.CODEC_H264) && !current.hasVideoCodec(RTP.CODEC_VP8) ) { current.videoCodecs = "VP8,H264,H263-1998,H263-2000,JPEG"; //TODO : add 'H263' when complete } if (current.sipexpires < 300) current.sipexpires = 300; if (current.sipexpires > 3600) current.sipexpires = 3600; if (current.nat == 1) current.nat = 0; //beta value (dyndns dropped) if (current.welcome) { int cnt = 0; for(int a=0;a<6;a++) { if (current.lines[a].user.length() > 0) cnt++; } if (cnt > 0) current.welcome = false; } JFLog.log("loadSettings ok"); } catch (FileNotFoundException e) { JFLog.log("Config file does not exist, using default values."); current = new Settings(); current.initLines(); } catch (Exception e) { JFLog.log(e); current = new Settings(); current.initLines(); } } public static void saveSettings() { String fn = JF.getUserPath() + "/.jphone.xml"; try { XML xml = new XML(); xml.readClass("settings", current); xml.write(new FileOutputStream(fn)); } catch (Exception e) { JF.showError("Error", "Save failed : " + e); } } private static int maxLog = 25; public static void addCallLog(String number) { int len = current.callLog.length; for(int a=0;a<len;a++) { if (current.callLog[a].equals(number)) { //move to top of list for(int b=a;b > 0;b--) current.callLog[b] = current.callLog[b-1]; current.callLog[0] = number; saveSettings(); return; } } if (len == maxLog) len = maxLog-1; String newcallLog[] = new String[len + 1]; for(int a=0;a < len;a++) newcallLog[a+1] = current.callLog[a]; newcallLog[0] = number; current.callLog = newcallLog; saveSettings(); } public static void setContact(String name, String contact) { int len = current.sipcontacts.length; int idx; String fields[]; for(int a=0;a<len;a++) { fields = SIP.split(current.sipcontacts[a]); if (fields[0].equals(name)) { JFLog.log("setting contact : " + fields[0] + " to " + contact); current.sipcontacts[a] = contact; return; } } JFLog.log("adding contact : " + contact); String newContacts[] = new String[len+1]; for(int a=0;a<len;a++) {newContacts[a] = current.sipcontacts[a];} newContacts[len] = contact; current.sipcontacts = newContacts; } public static void delContact(String name) { int len = current.sipcontacts.length; if (len == 0) return; int idx; String fields[]; for(int a=0;a<len;a++) { fields = SIP.split(current.sipcontacts[a]); if (fields[0].equalsIgnoreCase(name)) { int pos = 0; String newlist[] = new String[len-1]; for(int b=0;b<len;b++) { if (b==a) continue; newlist[pos++] = current.sipcontacts[b]; } current.sipcontacts = newlist; return; } } } /** Encodes a password with some simple steps. */ public static String encodePassword(String password) { char ca[] = password.toCharArray(); int sl = ca.length; if (sl == 0) return ""; char tmp; for(int p=0;p<sl/2;p++) { tmp = ca[p]; ca[p] = ca[sl-p-1]; ca[sl-p-1] = tmp; } StringBuffer out = new StringBuffer(); for(int p=0;p<sl;p++) { ca[p] ^= 0xaa; if (ca[p] < 0x10) out.append("0"); out.append(Integer.toString(ca[p], 16)); } //System.out.println("e1=" + out.toString()); Random r = new Random(); char key = (char)(r.nextInt(0xef) + 0x10); char outkey = key; ca = out.toString().toCharArray(); sl = ca.length; for(int p=0;p<sl;p++) { ca[p] ^= key; key ^= ca[p]; } out = new StringBuffer(); for(int a=0;a<4;a++) { out.append(Integer.toString(r.nextInt(0xef) + 0x10, 16)); } out.append(Integer.toString(outkey, 16)); for(int p=0;p<sl;p++) { if (ca[p] < 0x10) out.append("0"); out.append(Integer.toString(ca[p], 16)); } for(int a=0;a<4;a++) { out.append(Integer.toString(r.nextInt(0xef) + 0x10, 16)); } return out.toString(); } /** Encodes a password. */ public static String encodePassword(char password[]) { return encodePassword(new String(password)); } /** Decodes a password. */ public static String decodePassword(String crypto) { int sl = crypto.length(); if (sl < 10) return null; char key = (char)(int)Integer.valueOf(crypto.substring(8,10), 16); char newkey; crypto = crypto.substring(10, sl - 8); int cl = (sl - 18) / 2; char ca[] = new char[cl]; for(int p=0;p<cl;p++) { ca[p] = (char)(int)Integer.valueOf(crypto.substring(p*2, p*2+2), 16); newkey = (char)(key ^ ca[p]); ca[p] ^= key; key = newkey; } crypto = new String(ca); //System.out.println("d1=" + crypto); cl = crypto.length() / 2; ca = new char[cl]; for(int p=0;p<cl;p++) { ca[p] = (char)(int)Integer.valueOf(crypto.substring(p*2, p*2+2), 16); } for(int p=0;p<cl;p++) { ca[p] ^= 0xaa; } char tmp; for(int p=0;p<cl/2;p++) { tmp = ca[p]; ca[p] = ca[cl-p-1]; ca[cl-p-1] = tmp; } return new String(ca); } public static void test() { String tst = "testPassword"; String e = encodePassword(tst); System.out.println("e=" + e); String d = decodePassword(e); System.out.println("d=" + d); } public static String getPassword(String pass) { if (pass.startsWith("crypto(") && pass.endsWith(")")) { if (pass.charAt(8) != ',') return ""; //bad function if (pass.charAt(7) != '1') return ""; //unknown crypto type try { return decodePassword(pass.substring(9, pass.length() - 1)); } catch (Exception e) {} } else { return pass; } return ""; } public boolean hasAudioCodec(Codec codec) { String codecs[] = audioCodecs.split(","); if (codecs == null) return false; for(int a=0;a<codecs.length;a++) { if (codecs[a].equals(codec.name)) return true; } return false; } public String[] getAudioCodecs() { return audioCodecs.split(","); } public boolean hasVideoCodec(Codec codec) { String codecs[] = videoCodecs.split(","); if (codecs == null) return false; for(int a=0;a<codecs.length;a++) { if (codecs[a].equals(codec.name)) return true; } return false; } public String[] getVideoCodecs() { return videoCodecs.split(","); } private static final int CX = 320; private static final int CY = 240; public int[] getVideoResolution() { String res = videoResolution; if ((res == null) || (res.equals("<default>"))) return new int[] {CX,CY}; int idx = res.indexOf("x"); if (idx == -1) return new int[] {CX,CY}; return new int[] {Integer.valueOf(res.substring(0, idx)), Integer.valueOf(res.substring(idx+1))}; } }