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