1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
|
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.protocol;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.util.*;
/**
* The ProtocolProviderFactory is what actually creates instances of a
* ProtocolProviderService implementation. A provider factory would register,
* persistently store, and remove when necessary, ProtocolProviders. The way
* things are in the SIP Communicator, a user account is representedy (in a 1:1
* relationship) by an AccountID and a ProtocolProvider. In other words - one
* would have as many protocol providers installed in a given moment as they
* would user account registered through the various services.
*
* @author Emil Ivov
*/
public abstract class ProtocolProviderFactory
{
private static final Logger logger =
Logger.getLogger(ProtocolProviderFactory.class);
/**
* Then name of a property which represenstots a password.
*/
public static final String PASSWORD = "PASSWORD";
/**
* The name of a property representing the name of the protocol for an
* ProtocolProviderFactory.
*/
public static final String PROTOCOL = "PROTOCOL_NAME";
/**
* The name of a property which represents the AccountID of a
* ProtocolProvider and that, together with a password is used to login
* on the protocol network..
*/
public static final String USER_ID = "USER_ID";
/**
* The name that should be displayed to others when we are calling or
* writing them.
*/
public static final String DISPLAY_NAME = "DISPLAY_NAME";
/**
* The name of the property under which we store protocol AccountID-s.
*/
public static final String ACCOUNT_UID = "ACCOUNT_UID";
/**
* The name of the property under which we store protocol the address of
* a protocol centric entity (any protocol server).
*/
public static final String SERVER_ADDRESS = "SERVER_ADDRESS";
/**
* The name of the property under which we store the number of the port
* where the server stored against the SERVER_ADDRESS property is expecting
* connections to be made via this protocol.
*/
public static final String SERVER_PORT = "SERVER_PORT";
/**
* The name of the property under which we store the name of the transport
* protocol that needs to be used to access the server.
*/
public static final String SERVER_TRANSPORT = "SERVER_TRANSPORT";
/**
* The name of the property under which we store protocol the address of
* a protocol proxy.
*/
public static final String PROXY_ADDRESS = "PROXY_ADDRESS";
/**
* The name of the property under which we store the number of the port
* where the proxy stored against the PROXY_ADDRESS property is expecting
* connections to be made via this protocol.
*/
public static final String PROXY_PORT = "PROXY_PORT";
/**
* The name of the property under which we store the name of the transport
* protocol that needs to be used to access the proxy.
*/
public static final String PROXY_TRANSPORT = "PROXY_TRANSPORT";
/**
* The name of the property under which we store the user preference for a
* transport protocol to use (i.e. tcp or udp).
*/
public static final String PREFERRED_TRANSPORT = "PREFERRED_TRANSPORT";
/**
* Initializaed and creates an account corresponding to the specified
* accountProperties and registers the resulting ProtocolProvider in the
* <tt>context</tt> BundleContext parameter. Note that account
* registration is persistent and accounts that are registered during
* a particular sip-communicator session would be automatically reloaded
* during all following sessions until they are removed through the
* removeAccount method.
*
* @param userID tha/a user identifier uniquely representing the newly
* created account within the protocol namespace.
* @param accountProperties a set of protocol (or implementation) specific
* properties defining the new account.
* @return the AccountID of the newly created account.
* @throws java.lang.IllegalArgumentException if userID does not correspond
* to an identifier in the context of the underlying protocol or if
* accountProperties does not contain a complete set of account installation
* properties.
* @throws java.lang.IllegalStateException if the account has already been
* installed.
* @throws java.lang.NullPointerException if any of the arguments is null.
*/
public abstract AccountID installAccount(String userID,
Map accountProperties)
throws IllegalArgumentException,
IllegalStateException,
NullPointerException;
/**
* Returns a copy of the list containing the <tt>AccoudID</tt>s of all
* accounts currently registered in this protocol provider.
* @return a copy of the list containing the <tt>AccoudID</tt>s of all
* accounts currently registered in this protocol provider.
*/
public abstract ArrayList getRegisteredAccounts();
/**
* Returns the ServiceReference for the protocol provider corresponding to
* the specified accountID or null if the accountID is unknown.
* @param accountID the accountID of the protocol provider we'd like to get
* @return a ServiceReference object to the protocol provider with the
* specified account id and null if the account id is unknwon to the
* provider factory.
*/
public abstract ServiceReference getProviderForAccount(
AccountID accountID);
/**
* Removes the specified account from the list of accounts that this
* provider factory is handling. If the specified accountID is unknown to the
* ProtocolProviderFactory, the call has no effect and false is returned.
* This method is persistent in nature and once called the account
* corresponding to the specified ID will not be loaded during future runs
* of the project.
*
* @param accountID the ID of the account to remove.
* @return true if an account with the specified ID existed and was removed
* and false otherwise.
*/
public abstract boolean uninstallAccount(AccountID accountID);
/**
* The method stores the specified account in the configuration service
* under the package name of the source factory. The restore and remove
* account methods are to be used to obtain access to and control the stored
* accounts.
* <p>
* In order to store all account properties, the method would create an
* entry in the configuration service corresponding (beginning with) the
* <tt>sourceFactory</tt>'s package name and add to it a unique identifier
* (e.g. the current miliseconds.)
* <p>
* @param bundleContext a currently valid bundle context.
* @param accountID the AccountID corresponding to the account that we would
* like to store.
*/
protected void storeAccount(BundleContext bundleContext,
AccountID accountID)
{
String sourcePackageName = getFactoryImplPackageName();
//create a unique node name fo the properties node that will contain
//this account's properties.
String accNodeName
= "acc" + Long.toString(System.currentTimeMillis());
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService) bundleContext.getService(confReference);
//set a value for the persistent node so that we could later retrieve it
//as a property
configurationService.setProperty(
sourcePackageName //prefix
+ "." + accNodeName
, accNodeName);
//register the account in the configuration service.
//we register all the properties in the following hierarchy
//net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
configurationService.setProperty(
sourcePackageName //prefix
+ "." + accNodeName // node name for the account id
+ "." + ACCOUNT_UID // propname
, accountID.getAccountUniqueID()); // value
//store the rest of the properties
Iterator accountPropKeys
= accountID.getAccountProperties().keySet().iterator();
while (accountPropKeys.hasNext())
{
String propKey =
(String)accountPropKeys.next();
String propValue =
(String)accountID.getAccountProperties().get(propKey);
//if this is a password - encode it.
if(propKey.equals(PASSWORD))
propValue = new String(Base64.encode(propValue.getBytes()));
configurationService.setProperty(
sourcePackageName //prefix
+ "." + accNodeName // a uniew node name for the account id
+ "." + propKey // propname
, propValue); // value
}
logger.debug("Stored account for id " + accountID.getAccountUniqueID()
+ " for package " + getFactoryImplPackageName());
}
/**
* Saves the password for the specified account after scrambling it a bit
* sot that it is not visible from first sight (Method remains highly
* insecure).
*
* @param bundleContext a currently valid bundle context.
* @param accountID the AccountID for the account whose password we're
* storing.
* @param password the password itself.
*
* @throws java.lang.IllegalArgumentException if no account corresponding
* to <tt>accountID</tt> has been previously stored.
*/
protected void storePassword(BundleContext bundleContext,
AccountID accountID,
String password)
throws IllegalArgumentException
{
String accountPrefix = findAccountPrefix(
bundleContext, accountID);
if (accountPrefix == null)
throw new IllegalArgumentException(
"No previous records found for account ID: "
+ accountID.getAccountUniqueID()
+ " in package" + getFactoryImplPackageName());
//obscure the password
String mangledPassword
= new String(Base64.encode(password.getBytes()));
//get a reference to the config service and store it.
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService) bundleContext.getService(confReference);
configurationService.setProperty(
accountPrefix + "." + PASSWORD, mangledPassword);
}
/**
* Returns the password last saved for the specified account.
*
* @param bundleContext a currently valid bundle context.
* @param accountID the AccountID for the account whose password we're
* looking for..
*
* @return a String containing the password for the specified accountID.
*/
protected String loadPassword(BundleContext bundleContext,
AccountID accountID)
{
String accountPrefix = findAccountPrefix(
bundleContext, accountID);
if (accountPrefix == null)
return null;
//get a reference to the config service and store it.
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService) bundleContext.getService(confReference);
//obscure the password
String mangledPassword
= configurationService.getString(
accountPrefix + "." + PASSWORD);
if(mangledPassword == null)
return null;
return new String(Base64.decode(mangledPassword));
}
/**
* Restores all accounts stored for the package corresponding to
* sourceFactory and and installs everyone of them through the install
* account method.
* <p>
* @param bundleContext a currently valid bundle context.
*/
protected void loadStoredAccounts(BundleContext bundleContext)
{
String sourcePackageName = getFactoryImplPackageName();
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService) bundleContext.getService(confReference);
//first retrieve all accounts that we've registered
List storedAccounts = configurationService.getPropertyNamesByPrefix(
sourcePackageName, true);
logger.debug("Discovered "
+ storedAccounts.size()
+ " stored accounts");
//load all accounts stored in the configuration service
Iterator storedAccountsIter = storedAccounts.iterator();
while (storedAccountsIter.hasNext())
{
String accountRootPropName = (String) storedAccountsIter.next();
logger.debug("Loading account " + accountRootPropName);
//get all properties that we've stored for this account and load
//them into the accountProperties table.
List storedAccPropNames = configurationService.getPropertyNamesByPrefix(
accountRootPropName, true);
Iterator propNamesIter = storedAccPropNames.iterator();
Map accountProperties = new Hashtable();
while(propNamesIter.hasNext())
{
String fullPropertyName = (String)propNamesIter.next();
String storedPropertyValue
= configurationService.getString(fullPropertyName);
//strip the package prefix off the property name.
String propertyName = fullPropertyName.substring(
fullPropertyName.lastIndexOf('.')+1);
//if this is a password - decode it first
if(propertyName.equals(PASSWORD))
{
if(storedPropertyValue == null
|| storedPropertyValue.length() == 0)
{
storedPropertyValue = "";
}
else
{
storedPropertyValue = new String(
Base64.decode(storedPropertyValue));
}
}
accountProperties.put(propertyName, storedPropertyValue);
}
loadAccount(accountProperties);
}
}
/**
* Initializes and creates an account corresponding to the specified
* accountProperties and registers the resulting ProtocolProvider in the
* <tt>context</tt> BundleContext parameter. This method has a persistent
* effect. Once created the resulting account will remain installed until
* removed through the uninstall account method.
*
* @param accountProperties a set of protocol (or implementation)
* specific properties defining the new account.
* @return the AccountID of the newly loaded account
*/
protected abstract AccountID loadAccount(Map accountProperties);
/**
* Removes the account with <tt>accountID</tt> from the set of accounts
* that are persistently stored inside the configuration service.
* <p>
* @param bundleContext a currently valid bundle context.
* @param accountID the AccountID of the account to remove.
* <p>
* @return true if an account has been removed and false otherwise.
*/
protected boolean removeStoredAccount(BundleContext bundleContext,
AccountID accountID)
{
String sourcePackageName = getFactoryImplPackageName();
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService) bundleContext.getService(confReference);
//first retrieve all accounts that we've registered
List storedAccounts = configurationService.getPropertyNamesByPrefix(
sourcePackageName, true);
Iterator storedAccountsIter = storedAccounts.iterator();
//find an account with the corresponding id.
while (storedAccountsIter.hasNext())
{
String accountRootPropertyName = (String) storedAccountsIter.next();
//unregister the account in the configuration service.
//all the properties must have been registered in the following
//hierarchy:
//net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
String accountUID = configurationService.getString(
accountRootPropertyName //node id
+ "." + ACCOUNT_UID); // propname
if (accountUID.equals(accountID.getAccountUniqueID()))
{
//retrieve the names of all properties registered for the
//current account.
List accountPropertyNames
= configurationService.getPropertyNamesByPrefix(
accountRootPropertyName, false);
Iterator propsIter = accountPropertyNames.iterator();
//set all account properties to null in order to remove them.
while (propsIter.hasNext())
{
String propName = (String) propsIter.next();
configurationService.setProperty(propName, null);
}
//and now remove the parent too.
configurationService.setProperty(
accountRootPropertyName, null);
return true;
}
}
return false;
}
/**
* Returns the prefix for all persistently stored properties of the account
* with the specified id.
* @param bundleContext a currently valid bundle context.
* @param accountID the AccountID of the account whose properties we're
* looking for.
* @return a String indicating the ConfigurationService property name
* prefix under which all account properties are stored or null if no
* account corresponding to the specified id was found.
*/
protected String findAccountPrefix(BundleContext bundleContext,
AccountID accountID)
{
String sourcePackageName = getFactoryImplPackageName();
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService) bundleContext.getService(confReference);
//first retrieve all accounts that we've registered
List storedAccounts = configurationService.getPropertyNamesByPrefix(
sourcePackageName, true);
Iterator storedAccountsIter = storedAccounts.iterator();
//find an account with the corresponding id.
while (storedAccountsIter.hasNext())
{
String accountRootPropertyName = (String) storedAccountsIter.next();
//unregister the account in the configuration service.
//all the properties must have been registered in the following
//hierarchy:
//net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
String accountUID = configurationService.getString(
accountRootPropertyName //node id
+ "." + ACCOUNT_UID); // propname
if (accountUID.equals(accountID.getAccountUniqueID()))
{
return accountRootPropertyName;
}
}
return null;
}
/**
* Returns the name of the package that we're currently running in (i.e.
* the name of the package containing the proto factory that extends us).
*
* @return a String containing the package name of the concrete factory
* class that extends us.
*/
private String getFactoryImplPackageName()
{
String className = getClass().getName();
return className.substring(0, className.lastIndexOf('.'));
}
}
|