/* Copyright (c) 2008 Bluendo S.r.L. * See about.html for details about license. * * $Id: Config.java 1599 2009-06-19 13:13:04Z luca $ */ package it.yup.xmpp; // #mdebug //@import it.yup.util.Logger; // #enddebug import it.yup.util.RMSIndex; import it.yup.xml.BProcessor; import it.yup.xml.Element; import it.yup.xmpp.packets.Presence; import it.yup.util.Utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import javax.microedition.lcdui.AlertType; import javax.microedition.rms.RecordStore; /** * Client Configuration */ public class Config { private static String version = "9.6.0"; public static final String RMS_NAME = "lampirorms"; /** name of the record store (will be signed as deprecated)*/ public static final String RMS_OLD_NAME = "yuprms"; /** config index in the record store */ public static final int RNUM_CONFIG = 1; /** roster index in the record store */ public static final int RNUM_ROSTER = 2; private Hashtable properties = new Hashtable(); /** Connection url used by the GPRS connector */ // public static final String GPRS_CONNECTION_URL = // "socket://www.bluendo.com:5280"; public static final String GPRS_CONNECTION_URL = "socket://localhost:10080"; // public static final String GPRS_CONNECTION_URL = // "socket://bosh.bluendo.com:10080"; /** URL (host/port) of the GPRS/HTTP gateway */ // public static final String HTTP_GW_HOST = "dalek"; public static final String HTTP_GW_HOST = "bosh.bluendo.com"; /** path of the GPRS/HTTP gateway */ public static final String HTTP_GW_PATH = "/httpb"; /** path of the GPRS/HTTP gateway */ public static final String SRV_QUERY_PATH = "http://services.bluendo.com/srv/?domain="; public static final String BLUENDO_SERVER = "jabber.bluendo.com"; /** * time the server should wait before sending a response if no data is * available */ public static final int WAIT_TIME = 30; /** * The time after which iq answer not arrived are considered as expired and remov ed */ public static int MAX_PERM_TIME = 60000; // /** default value keepalive of the plain socket */ // XXX We may keep this but the transport should not read it from Config private static final int SO_KEEPALIVE = 60 * 5 * 1000; /** Config instance */ private static Config instance; public static String TRUE = "t"; public static String FALSE = "f"; // Constants for keys saved in the rms public static String CONFIG = "config"; /* * The db of all the known capabilities */ public static String KNOWN_CAPS = "known_caps"; public static String CAPS_PREFIX = "caps"; /* * All the gateways to which I am registered to */ public static String REGISTERED_GATEWAYS = "reg_gateways"; public static String GROUPS_POSITION= "groups_position"; // The next ones are variables related to the management of multimedia data: // audioTypes and imageTypes are the suffix handled and recognized by the multimedia related // screens. audioType and imgType are constants used to identify a file in the // store. public static int audioType = 1; public static int imgType = 0; public static String audioTypes[] = new String[] { "amr", "amr-xb", "pcm", "ulaw", "gsm", "wav", "au", "raw" }; public static String imageTypes[] = new String[] { "jpeg", "png", "jfif", "bmp" }; // constants for values saved in the record store /** server name */ public static short SERVER = 0x0000; /** user name */ public static short USER = 0x0001; /** password */ public static short PASSWORD = 0x0002; /** mail address */ public static short EMAIL = 0x0003; /** connecting server (after a SRV_RECORD query ) */ public static short CONNECTING_SERVER = 0x0004; /** sw version */ public static short VERSION = 0x0005; /** sw version */ public static short SILENT = 0x0006; /** logged once */ public static short LOGGED_ONCE = 0x0007; /** keeplive for plain sockets */ public static short KEEP_ALIVE = 0x0008; /** flag which is true after the first succesful login and roster update */ public static short CLIENT_INITIALIZED = 0x0009; /** last "show" used in the presence */ public static short LAST_PRESENCE_SHOW = 0x0010; /** last status message */ public static short LAST_STATUS_MESSAGE = 0x0011; /** last compression settings used */ public static short COMPRESSION = 0x0019; /** last TLS settings used */ public static short TLS = 0x0020; /** last priority */ public static short LAST_PRIORITY = 0x0017; /** XMPP resource */ public static short YUP_RESOURCE = 0x0021; /** Has a qwerty keyboard */ public static short QWERTY = 0x0022; /** * Using bit masks * * vibration settings: * <ul> * <li>0x00: none </li> * <li>0x01: only when hidden</li> * <li>0x02: only when shown</li> * <li>0x03: always </li> * </ul> * * tone settings: * <ul> * <li>0x00: none </li> * <li>0x04: only when hidden</li> * <li>0x08: only when shown</li> * <li>0x0C: always </li> * </ul> * * */ public static short VIBRATION_AND_TONE_SETTINGS = 0x0012; /** The theme associated to the Application */ public static short COLOR = 0x0013; /** tone volume */ public static short TONE_VOLUME = 0x0014; /** * UICanvas keys for left and right. String is a comma-separated couple of * integers representing (in order) left and right key */ public static short CANVAS_KEYS = 0x0015; /* * Font Size for roster and chat */ public static short FONT_SIZE = 0x0016; /* * Font Size for roster and chat */ public static short HISTORY_SIZE = 0x0018; /* * The accepted gateways (i.e. the ones whose contacts do not need manual authorization) */ public static short ACCEPTED_GATEWAYS = 0x0022; /* * the album data */ public static short MM_ALBUM = 0x0025; /* * The resolution of the camera in capturing images */ public static final short CAMERA_RESOLUTION = 0x0026; /** the bluendo assistent */ public static final String LAMPIRO_AGENT = "lampiro@golem.jabber.bluendo.com"; /** maximum wait time for a packet (should we let configure this ) */ public static final int TIMEOUT = -1; private Hashtable cachedCaps= new Hashtable(7); /** * Get the configuration using the stored values (if any), or use the * default values */ public synchronized static Config getInstance() { if (instance == null) { instance = new Config(); instance.loadFromStorage(); } return instance; } /** Make the constructur private -> singleton */ private Config() { } /* * The rmsIndex containing all the data */ private RMSIndex rms= new RMSIndex(RMS_NAME); /** * Load the the configuration from the RMS */ private synchronized void loadFromStorage() { try { byte[] b = null; boolean needSave = false; if (RMSIndex.rmExist(RMS_NAME)) { // first check for existence of the "new recordStore" // however old recordstore handling will be removed in future version rms.open(); b = rms.load(Config.CONFIG.getBytes()); // if I cannot load the configuration it is better // to delete it if (b==null){ resetStorage(true); RecordStore.deleteRecordStore(RMS_NAME); } rms.close(); } else if (RMSIndex.rmExist(RMS_OLD_NAME)) { // then check for existence of the "old recordStore" // load and delete it RecordStore recordStore = null; try { recordStore = RecordStore.openRecordStore(RMS_OLD_NAME, false); b = recordStore.getRecord(RNUM_CONFIG); needSave = true; } catch (Exception e) { setDefaults(); } finally { try { if (recordStore != null) { recordStore.closeRecordStore(); RecordStore.deleteRecordStore(RMS_OLD_NAME); } } catch (Exception e) { // #mdebug //@ e.printStackTrace(); //@ System.out.println("In config resetting" + e.getMessage() //@ + e.getClass()); // #enddebug } } } else { setDefaults(); } if (b != null && b.length != 0) { DataInputStream in = new DataInputStream( new ByteArrayInputStream(b)); while (in.available() > 0) { short code = in.readShort(); String val = in.readUTF(); properties.put(String.valueOf(code), val); } in.close(); } if (needSave == true) this.saveToStorage(); String _version = getProperty(Config.VERSION); if (_version == null || _version.compareTo(Config.version) < 0) { // the software has been updated, handle here the "update" logic setDefaults(); } } catch (Exception e) { this.resetStorage(true); XMPPClient.getInstance().showAlert( AlertType.ERROR, "Config Error", "Error while loading config:\n" + e.getMessage() + "\nConfig has been reset.", null); } } private void setDefaults() { setProperty(Config.VERSION, Config.version); setDefault(Config.USER, ""); setDefault(Config.SERVER, ""); setDefault(Config.EMAIL, ""); setDefault(Config.CONNECTING_SERVER, ""); setDefault(Config.SILENT, "y"); setDefault(Config.LOGGED_ONCE, "0"); setDefault(Config.KEEP_ALIVE, "" + SO_KEEPALIVE); setDefault(Config.CLIENT_INITIALIZED, Config.FALSE); setDefault(Config.LAST_STATUS_MESSAGE, "Lampiro (http://lampiro.bluendo.com)"); setDefault(Config.LAST_PRESENCE_SHOW, Presence.SHOW_ONLINE); saveToStorage(); } /** * Reset the options. If hard is set to true even the login credentials are * reset * * @param hard */ public void resetStorage(boolean hard) { Config cfg; String user = null; String password = null; String server = null; String email = null; String connectingServer = null; if (hard == false) { cfg = Config.getInstance(); try { user = cfg.getProperty(Config.USER); server = cfg.getProperty(Config.SERVER); password = cfg.getProperty(Config.PASSWORD); email = cfg.getProperty(Config.EMAIL); connectingServer = cfg.getProperty(Config.CONNECTING_SERVER); } catch (Exception e) { resetStorage(true); return; } } properties.clear(); if (hard == false) { cfg = Config.getInstance(); cfg.setProperty(Config.USER, user); cfg.setProperty(Config.PASSWORD, password); cfg.setProperty(Config.SERVER, server); cfg.setProperty(Config.EMAIL, email); cfg.setProperty(Config.CONNECTING_SERVER, connectingServer); } this.saveToStorage(); // so that the new options are automatically reloaded this.loadFromStorage(); } public synchronized void saveToStorage() { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos); Enumeration en = properties.keys(); while (en.hasMoreElements()) { String code = (String) en.nextElement(); out.writeShort(Integer.parseInt(code)); out.writeUTF((String) properties.get(code)); } byte[] data = baos.toByteArray(); this.rms.open(); this.rms.store(Config.CONFIG.getBytes(), data); this.rms.close(); } catch (Exception e) { // #mdebug //@ Logger.log("Error in saving to storage: " + e.getMessage(), //@ Logger.DEBUG); //@ // #enddebug XMPPClient.getInstance().showAlert(AlertType.ERROR, "Config Error", "Error while saving config:\n" + e.getMessage(), null); } } public synchronized byte [] getData (byte key []){ this.rms.open(); byte [] res = this.rms.load(key); this.rms.close(); return res; } public synchronized void setData (byte key[], byte data[]){ this.rms.open(); this.rms.store(key, data); this.rms.close(); } public String getProperty(short code) { return (String) properties.get(String.valueOf(code)); }; public String getProperty(short code, String default_value) { String s = (String) properties.get(String.valueOf(code)); return (s == null) ? default_value : s; }; public void setProperty(short code, String value) { properties.put(String.valueOf(code), value); } /** * Set the default value for a property if none is given * * @param code * @param default_value */ private void setDefault(short code, String default_value) { if (!this.properties.containsKey(String.valueOf(code))) { setProperty(code, default_value); } } public synchronized void saveCapabilities(String node, String ver, Element query) { String combi = node + ver; // should not happen if (cachedCaps.contains(combi)) return; byte[] capsRaw = this.getData(KNOWN_CAPS.getBytes()); if (capsRaw == null) capsRaw = new byte[0]; //read the main record DataInputStream is = new DataInputStream(new ByteArrayInputStream( capsRaw)); int capCount = 0; try { while (is.available() > 0) { is.readUTF(); is.readUTF(); capCount++; } } catch (IOException e) { // #mdebug //@ Logger.log("Error in getting capabilities: received packet: " //@ + e.getClass(), Logger.DEBUG); // #enddebug // reset the capabilities this.setData(KNOWN_CAPS.getBytes(), "".getBytes()); return; } // save the new cap String newCapKey = CAPS_PREFIX + capCount; // #mdebug //@ // #enddebug byte[] newCapData = BProcessor.toBinary(query); this.setData(newCapKey.getBytes(), newCapData); // add the new cap ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream os = new DataOutputStream(baos); try { os.write(capsRaw); os.writeUTF(combi); os.writeUTF(newCapKey); } catch (IOException e) { // should not ever appear // #mdebug //@ Logger.log("Error in saving new capability" //@ + e.getClass(), Logger.DEBUG); // #enddebug } this.setData(KNOWN_CAPS.getBytes(), baos.toByteArray()); this.cachedCaps.put(combi,query); } public synchronized Element getCapabilities(String node, String ver) { String combi = node + ver; Element cachedCap = (Element) cachedCaps.get(combi); if (cachedCap != null) return cachedCap; // load the capabilities byte[] capsRaw = this.getData(KNOWN_CAPS.getBytes()); if (capsRaw == null) return null; //read the main record DataInputStream is = new DataInputStream(new ByteArrayInputStream( capsRaw)); String capCode = null; String ithCap=null; String tempCode; try { while (is.available() > 0) { ithCap = is.readUTF(); tempCode = is.readUTF(); if (ithCap.equals(combi)){ capCode = tempCode; break; } } } catch (IOException e) { // #mdebug //@ Logger.log("Error in getting capabilities: received packet: " //@ + e.getClass(), Logger.DEBUG); // #enddebug return null ; } if (capCode == null) return null; byte capData[] = this.getData(Utils.getBytesUtf8( capCode)); Element decodedPacket = BProcessor.parse(capData); cachedCaps.put(combi, decodedPacket); return decodedPacket; } }