    001    package com.skype.ipc;
    002
    003    import java.io.ByteArrayInputStream;
    004    import java.io.File;
    005    import java.io.FileInputStream;
    006    import java.io.IOException;
    007    import java.io.InputStream;
    008    import java.security.PrivateKey;
    009    import java.security.cert.X509Certificate;
    010    import java.security.spec.InvalidKeySpecException;
    011
    012    import com.skype.util.PemReader;
    013    //import com.sun.org.apache.xpath.internal.axes.HasPositionalPredChecker;
    014
    015    /***
    016     * Configuration of the ipc wrapper. All these parameters are checked once at the initialization phase, and any modifications
    017     * done afterwards will be ignored.
    018     */
    019    public class ClientConfiguration {
    020
    021            public ClientConfiguration() {
    022                    dispatchAll                = false;
    023                    numRetries                 = 3;
    024                    setup                      = null;
    025                    certificateContents        = null;
    026                    privatekeyContents         = null;
    027                    pemFileName                = null;
    028                    initialLatency             = 600;
    029                    transportInputBufferSize   = 512;
    030                    transportOutputBufferSize  = 512;
    031                    transportLogBaseName       = null;
    032                    noTls                      = false;
    033                    transportFactory           = new TransportFactory();
    034                    setTcpTransport();
    035            }
    036
    037            /***
    038             * use TCP socket for the connection to the runtime with the default settings, port 8963 on the local host
    039             */
    040            public void setTcpTransport() {
    041                    useLocalTransport    = false;
    042                    this.ip              = "127.0.0.1";
    043                    this.port            = 8963;
    044            }
    045
    046            /***
    047             * use TCP socket for the connection to the runtime with custom settings
    048             * @param ip the ip address of the runtime host
    049             * @param port the port used by the runtime
    050             */
    051            public void setTcpTransport(String ip, int port) {
    052                    useLocalTransport    = false;
    053                    this.ip              = ip;
    054                    this.port            = port;
    055            }
    056
    057            /***
    058             * tells if the configuration was set to use a TCP socket with runtime
    059             */
    060            public boolean useTcpTransport() {
    061                    return !useLocalTransport;
    062            }
    063
    064            /***
    065             * get the ip address to be used with a TCP connection
    066             */
    067            public String getIp() {
    068                    return ip;
    069            }
    070
    071            /***
    072             * get the port to be used with a TCP connection
    073             */
    074            public int getPort() {
    075                    return port;
    076            }
    077
    078            /***
    079             * when an event or a property change notification is recieved on an object that
    080             * doesn't exist yet, create the object and forward the notification. The default
    081             * behavior is to discard the notification.
    082             */
    083            public void setDispatchAll() {
    084                    dispatchAll = true;
    085            }
    086
    087            /***
    088             * tells if the event or a property change notification will always be dispatched (default if false)
    089             */
    090            public boolean isDispatchAll() {
    091                    return dispatchAll;
    092            }
    093
    094            /***
    095             * turn off Tls (use it only if the runtime support this)
    096             */
    097            public void dontUseTls() {
    098                    noTls = true;
    099            }
    100
    101            /***
    102             * check if Tls mode is turned off
    103             */
    104            public boolean isWithoutTls() {
    105                    return noTls;
    106            }
    107
    108            /***
    109             * check if Tls mode is turned on (default)
    110             */
    111            public boolean isWithTls() {
    112                    return !noTls;
    113            }
    114
    115            /***
    116             * specify the certificate file to be used for the Tls connection
    117             * @param certificateFilename <x>.pem file (ensure that <x>.der is present as well)
    118             * returns the input filename argument
    119             */
    120            public String setCertificate(String certificateFilename) {
    121                    this.pemFileName = certificateFilename;
    122                    return certificateFilename;
    123            }
    124
    125            /***
    126             * Do not use, internal only
    127             */
    128            public void setCertificateContents(byte[] contents)
    129            {
    130                    certificateContents = contents;
    131            }
    132
    133            /***
    134             * Do not use, internal only
    135             */
    136            public void setPrivateKeyContents(byte[] contents)
    137            {
    138                    privatekeyContents = contents;
    139            }
    140
    141            /***
    142             * Do not use, internal only, loads the certicate
    143             */
    144            public String getCertificate() throws IOException
    145            {
    146                    assert(isWithoutTls());
    147
    148                    if (certificateContents != null && certificateContents.length > 0)
    149                            return new String(certificateContents);
    150
    151                    if ( ! hasFilePath())
    152                            throw new IOException("no PemFile");
    153
    154                    File tokenFile = new File(pemFileName);
    155                    InputStream in = new FileInputStream(tokenFile);
    156                    long fileSize = tokenFile.length();
    157                    byte[] bytes = new byte[(int) fileSize];
    158                    int offset = 0;
    159                    int count = 0;
    160                    while (offset < fileSize) {
    161                            count = in.read(bytes, offset, (int) fileSize - offset);
    162                            if (count >= 0)
    163
    164                                    offset += count;
    165                            else
    166                                    throw new IOException("Unable to read App Token file: " + tokenFile.getName());
    167                    }
    168                    if (in != null)
    169                    in.close();
    170
    171                    String rawString = new String(bytes);
    172                    return rawString.trim();
    173            }
    174
    175            /***
    176             * Do not use, internal only, loads the certicate
    177             */
    178            public X509Certificate getX509Certificate() throws IOException
    179            {
    180                    assert (isWithTls());
    181
    182                    try {
    183                            if (hasFilePath()) {
    184                                    PemReader donkey = new PemReader(pemFileName);
    185                                    return donkey.getCertificate();
    186                            }
    187                            else if (certificateContents.length == 0 || privatekeyContents.length == 0) {
    188                                    throw new IOException("no certificate data.");
    189                            }
    190                            else {
    191                                    ByteArrayInputStream certStream = new ByteArrayInputStream(certificateContents);
    192                                    ByteArrayInputStream keyStream = new ByteArrayInputStream(privatekeyContents);
    193                                    PemReader donkey = new PemReader(certStream, keyStream);
    194                                    return donkey.getCertificate();
    195                            }
    196                    }
    197                    catch (InvalidKeySpecException e) {
    198                            throw new IOException("Invalid certificate data.");
    199                    }
    200            }
    201
    202            /***
    203             * Do not use, internal only, loads the certicate
    204             */
    205            public PrivateKey getPrivateKey() throws IOException
    206            {
    207                    assert (isWithTls());
    208
    209                    if (hasFilePath()) {
    210                            PemReader donkey = new PemReader(pemFileName);
    211                            return donkey.getKey();
    212                    }
    213                    else if (certificateContents.length == 0 || privatekeyContents.length == 0) {
    214                            throw new IOException("no PemFile");
    215                    }
    216                    else {
    217                            ByteArrayInputStream certStream = new ByteArrayInputStream(certificateContents);
    218                            ByteArrayInputStream keyStream = new ByteArrayInputStream(privatekeyContents);
    219                            PemReader donkey = new PemReader(certStream, keyStream);
    220                            return donkey.getKey();
    221                    }
    222            }
    223
    224            /***
    225             * Decide how many attempts at connecting to the runtime shall be done
    226             * @param num with num > 0 and num < 10
    227             * return num if valid or latest valid input or default, ie 3
    228             */
    229            public int setConnectionNumRetries(int num) {
    230                    if (num > 0 && num < 10)
    231                            numRetries = num;
    232                    return numRetries;
    233            }
    234
    235            /***
    236             * check how many attempts at connecting to the runtime will be done
    237             */
    238            public int getConnectionNumRetries() {
    239                    return numRetries;
    240            }
    241
    242            /***
    243             * Decide what initial latency in milliseconds will be used between each retry
    244             * The policy is to increase this latency by 50% after each failed retry.
    245             * The default is 600ms, so that the default pattern is 600 (+600ms) -> 900 (+1500ms) -> 1350 (+2850ms)
    246             * @param ms with ms > 100 and ms < 1000
    247             * return ms if valid or latest valid input or default, ie 600
    248             */
    249            public int setConnectionRetryInitialLatency(int ms) {
    250                    if (ms > 100 && ms < 1000)
    251                            initialLatency = ms;
    252                    return initialLatency;
    253            }
    254
    255            /***
    256             * check the initial latency used in case of retries
    257             */
    258            public int getConnectionRetryInitialLatency() {
    259                    return initialLatency;
    260            }
    261
    262            /***
    263             * Recommend the transport input buffer size (may or may not be used by the transport, indicative only)
    264             * for the connection to the runtime
    265             * @param bytes with bytes >= 256 and bytes < 8096 , bytes is rounded to the next power of 2
    266             * return bytes if valid or latest valid input or default, ie 512
    267             */
    268            public int setTransportInputBufferSize(int bytes) {       // return 2^n so that 2^n >= bytes
    269                    if (bytes >= 256 && bytes <= 8096) {
    270                            int n = 256;
    271                            while (n < bytes) n += n;
    272                            transportInputBufferSize = n;
    273                    }
    274                    return transportInputBufferSize;
    275            }
    276
    277            /***
    278             * check the recommended transport input buffer size
    279             */
    280            public int getTransportInputBufferSize() {
    281                    return transportInputBufferSize;
    282            }
    283
    284            /***
    285             * Recommend the transport output buffer size (may or may not be used by the transport, indicative only)
    286             * for the connection to the runtime
    287             * @param bytes with bytes >= 256 and bytes < 8096 , bytes is rounded to the next power of 2
    288             * return bytes if valid or latest valid input or default, ie 512
    289             */
    290            public int setTransportOutputBufferSize(int bytes) {       // return 2^n so that 2^n >= bytes
    291                    if (bytes >= 256 && bytes <= 8096) {
    292                            int n = 256;
    293                            while (n < bytes) n += n;
    294                            transportOutputBufferSize = n;
    295                    }
    296                    return transportOutputBufferSize;
    297            }
    298
    299            /***
    300             * check the recommended transport output buffer size
    301             */
    302            public int getTransportOutputBufferSize() {                // default 512
    303                    return transportOutputBufferSize;
    304            }
    305
    306            /***
    307             * turn on the recording of the data exchanged between the runtime and the java client
    308             * or turn it off off the file name is empty (default). This is configured once
    309             * @param baseName is the base name for the transport log which consists in 2 files
    310             * - <baseName>_log_in.1, data from the runtime to the client
    311             * - <baseName>_log_out.1,  data from the client to the runtime
    312             */
    313            public void generateTransportLog(String baseName) {
    314                    if (baseName == null || baseName.equals("")) return;
    315                    transportLogBaseName = baseName;
    316            }
    317
    318            /***
    319             * check if a transport log shall be produced
    320             */
    321            public boolean generateTransportLog() {
    322                    return transportLogBaseName != null && !transportLogBaseName.equals("");
    323            }
    324
    325            /***
    326             * get the input transport log file name
    327             */
    328            public String getInputTransportLogName() {
    329                    return transportLogBaseName + "_log_in.1";
    330            }
    331
    332            /***
    333             * get the output transport log file name
    334             */
    335            public String getOutputTransportLogName() {
    336                    return transportLogBaseName + "_log_out.1";
    337            }
    338
    339            /***
    340             * By default only a TCP transport is supported to communicate with the runtime,
    341             * but with setting a TransportFactory 1 further non portable transport could be used,
    342             * provided that they are supported by the runtime as well. For example android or
    343             * linux runtime shall accept unix socket too.
    344             */
    345            public void setTransportFactory(TransportFactory factory) {
    346                    transportFactory = factory;
    347            }
    348
    349            public TransportFactory getTransportFactory() {
    350                    return transportFactory;
    351            }
    352
    353            /***
    354             * By default a runtime only forwards the int alike value in case of property changes
    355             * this function allows forwarding the string values as well which can highly improve
    356             * the performances if your application is always getting the strings on the change
    357             * notification.
    358             */
    359            public void fowardStringChangedValue() {
    360                    if (setup == null)
    361                            setup = new String();
    362                    setup += "SkypeKit/FowardStringChangedValue=1\n";
    363            }
    364
    365
    366            /***
    367             * internal, don't use unless implementing a TransportFactory
    368             */
    369            public String getHandshakeSetup() {
    370                    return setup;
    371            }
    372
    373            private boolean hasFilePath()
    374            {
    375                    return pemFileName != null && ! pemFileName.isEmpty();
    376            }
    377
    378            private int     numRetries;
    379            private int     initialLatency;
    380            private int     transportInputBufferSize;
    381            private int     transportOutputBufferSize;
    382            private String  transportLogBaseName;
    383            private String  pemFileName;
    384            private byte[]  certificateContents;
    385            private byte[]  privatekeyContents;
    386            private boolean noTls;
    387            private boolean dispatchAll;
    388            protected boolean useLocalTransport;
    389            private String  ip;
    390            private int     port;
    391            private TransportFactory transportFactory;
    392            private String  setup;
    393    }
    394