diff options
author | Emil Ivov <emcho@jitsi.org> | 2008-09-06 10:53:24 +0000 |
---|---|---|
committer | Emil Ivov <emcho@jitsi.org> | 2008-09-06 10:53:24 +0000 |
commit | 88a0036d18b21387db6d0053bce7acf86a471d34 (patch) | |
tree | 63101e39a2ccbdcc65074da3e91bf7121cddead2 | |
parent | 45288c70242e5875cf44984b3b6e9bcca84ca3f8 (diff) | |
download | jitsi-88a0036d18b21387db6d0053bce7acf86a471d34.zip jitsi-88a0036d18b21387db6d0053bce7acf86a471d34.tar.gz jitsi-88a0036d18b21387db6d0053bce7acf86a471d34.tar.bz2 |
Implements support for launch parameters
Implements support for handling SIP URIs as launch parameters
Adds a lock mechanism to prevent from running multiple instances of SIP Communicator
Adds a mechanism for a second instance of SC to pass its launch
parameters to a one that's already running.
Adds a DefaultSecurityAuthority class (in use by the systray and SIP URI handler)
30 files changed, 2907 insertions, 379 deletions
@@ -358,7 +358,7 @@ <replace file="${src}/net/java/sip/communicator/impl/version/NightlyBuildID.java" token="build.id" value="${build.label}"/> </target> - + <!-- SIP Communicator Version --> <target name="version" depends="-pre-version"> <!-- Recompile ant task classes--> @@ -378,7 +378,7 @@ <sip-communicator-version property="sip-communicator.version" /> - <echo message="SIP Communicator version ${sip-communicator.version}" /> + <echo message="SIP Communicator version ${sip-communicator.version}" /> </target> <!--INIT--> @@ -418,7 +418,7 @@ <!--internal-target- prepare to run a single Service Impl Compatibility Kit --> <!-- extract the simple Test class name. --> <basename property="simple.test.names" file="${test.name}"/> - <echo message="single test prepared: ${simple.test.names}" /> + <echo message="single test prepared: ${simple.test.names}" /> </target> <!--internal-target- prepare to run all known Service Impl Compatibility Kit --> @@ -457,7 +457,7 @@ <!-- Tell the slick runner about TestSutes we've preregistered. --> <sysproperty key="net.java.sip.communicator.slick.runner.TEST_LIST" value="${simple.test.names}"/> - + <!-- Tell java.util.logging about our logging preferences --> <sysproperty key="java.util.logging.config.file" value="${lib}/logging.properties"/> @@ -514,6 +514,12 @@ <target name="run" depends="-deploy-os-specific-bundles" description="Starts felix and runs sip-comunicator gui (use latest build)."> + <!-- we allow users to pass command line args using the "args" system + property. However we need to manually set tha prop to an empty + string here or otherwise the application would get an argument with + the value ${args}--> + <property name="args" value=""/> + <!-- forking prevents from debugging --> <java classname="net.java.sip.communicator.launcher.SIPCommunicator" fork="true" @@ -550,6 +556,9 @@ <env key="LD_LIBRARY_PATH" path="${ld.library.path}"/> <env key="PATH" path="${path}"/> <env key="DYLD_LIBRARY_PATH" path="${dyld.library.path}"/> + + <!-- pass to SC args that have been specified by the user --> + <arg line="${args}"/> </java> </target> @@ -623,7 +632,7 @@ </target> - <!-- - - - - - - - - - - - - - BUNDLE BUILDING TARGETS - - - - - - - - - --> + <!-- - - - - - - - - - - - - - BUNDLE BUILDING TARGETS - - - - - - - - --> <!--ALL BUNDLES--> <target name="bundles" depends="bundle-sc-launcher,bundle-util,bundle-configuration,bundle-configuration-slick, @@ -659,7 +668,8 @@ bundle-updatecheckplugin, bundle-dict,bundle-plugin-dictaccregwizz, bundle-plugin-simpleaccreg,bundle-plugin-generalconfig, - bundle-plugin-googletalkaccregwizz"/> + bundle-plugin-googletalkaccregwizz,bundle-argdelegation-service, + bundle-argdelegation"/> <!--BUNDLE-SC-LAUNCHER--> <target name="bundle-sc-launcher"> @@ -754,7 +764,15 @@ </target> <!--BUNDLE-UTIL--> - <target name="bundle-util"> + <target name="bundle-util" depends="version"> + <!-- Create a properties file that the arg handler could use + to determine SC's version --> + <echo file="${dest}/net/java/sip/communicator/util/launchutils/version.properties" + message="APPLICATION_NAME=SIP Communicator${line.separator}" /> + <echo file="${dest}/net/java/sip/communicator/util/launchutils/version.properties" + message="APPLICATION_VERSION=${sip-communicator.version}${line.separator}" + append="true"/> + <!-- Create the util.jar--> <jar compress="false" destfile="${bundles.dest}/util.jar" manifest="${src}/net/java/sip/communicator/util/util.manifest.mf"> <zipfileset dir="${dest}/net/java/sip/communicator/util" @@ -1052,7 +1070,7 @@ javax.swing.event, javax.swing.border"/> <zipfileset src="${lib.noinst}/commons-logging.jar" prefix=""/> </jar> </target> - + <!-- BUNDLE-SSH --> <target name="bundle-ssh"> <!-- Creates a bundle containing the SSH impl of the protocol provider.--> @@ -1407,7 +1425,7 @@ javax.swing.event, javax.swing.border"/> <zipfileset src="${lib.noinst}/aclibico-2.1.jar" prefix=""/> </jar> </target> - + <!-- BUNDLE-RSS-SLICK --> <!-- Creates a bundle containing the slick for the Gibberish protocol provider.--> @@ -1563,7 +1581,7 @@ javax.swing.event, javax.swing.border"/> prefix="net/java/sip/communicator/impl/protocol/dict"/> </jar> </target> - + <!-- BUNDLE-PLUGIN-DICTACCREGWIZZ --> <target name="bundle-plugin-dictaccregwizz"> <!-- Creates a bundle for the plugin Dict Account Registration @@ -1574,7 +1592,7 @@ javax.swing.event, javax.swing.border"/> prefix="net/java/sip/communicator/plugin/dictaccregwizz"/> </jar> </target> - + <!--BUNDLE-UpdateCheckPlugin--> <target name="bundle-updatecheckplugin"> <jar compress="false" destfile="${bundles.dest}/updatechecker.jar" @@ -1583,7 +1601,7 @@ javax.swing.event, javax.swing.border"/> prefix="net/java/sip/communicator/plugin/updatechecker" /> </jar> </target> - + <!--BUNDLE-AutoAwayPlugin--> <target name="bundle-plugin-autoaway"> <jar compress="false" destfile="${bundles.dest}/autoaway.jar" @@ -1625,7 +1643,7 @@ javax.swing.event, javax.swing.border"/> <zipfileset src="${lib.noinst}/KeybindingUtil.jar" prefix=""/> </jar> </target> - + <!--BUNDLE-PLUGIN-KeybindingChooser--> <target name="bundle-plugin-keybindingChooser"> <jar compress="false" destfile="${bundles.dest}/keybindingChooser.jar" @@ -1685,7 +1703,7 @@ javax.swing.event, javax.swing.border"/> </jar> </target> - + <!-- BUNDLE-JFontChooser --> <target name="bundle-jfontchooserlib"> <!-- Creates a bundle containing the jfontchooser lib.--> <jar compress="false" destfile="${bundles.dest}/jfontchooserlib.jar" @@ -1693,4 +1711,24 @@ javax.swing.event, javax.swing.border"/> <zipfileset src="${lib.noinst}/jfontchooser-1.0.5.jar" prefix=""/> </jar> </target> -</project> + + <!-- BUNDLE Arg Delegation Service --> + <target name="bundle-argdelegation-service"> + <!-- Creates a bundle for the notifications.--> + <jar compress="false" destfile="${bundles.dest}/argdelegation-service.jar" + manifest="${src}/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf"> + <zipfileset dir="${dest}/net/java/sip/communicator/service/argdelegation/" + prefix="net/java/sip/communicator/service/argdelegation"/> + </jar> + </target> + + <!-- BUNDLE Arg Delegation Implementation--> + <target name="bundle-argdelegation"> + <!-- Creates a bundle for the notifications.--> + <jar compress="false" destfile="${bundles.dest}/argdelegation.jar" + manifest="${src}/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf"> + <zipfileset dir="${dest}/net/java/sip/communicator/impl/argdelegation/" + prefix="net/java/sip/communicator/impl/argdelegation/"/> + </jar> + </target> +</project>
\ No newline at end of file diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index 7be7bd7..9be3558 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -41,7 +41,8 @@ org.osgi.framework.system.packages= org.osgi.framework; ; version=1.3.0, \ org.xml.sax.helpers; \ javax.crypto; \ javax.crypto.spec; \ - javax.crypto.interfaces; + javax.crypto.interfaces; \ + net.java.sip.communicator.util.launchutils felix.auto.start.10= reference:file:lib/bundle/org.apache.felix.bundlerepository-1.0.0.jar \ reference:file:lib/bundle/org.apache.felix.servicebinder-0.9.0-SNAPSHOT.jar @@ -68,7 +69,8 @@ felix.auto.start.42= \ reference:file:sc-bundles/defaultresources.jar felix.auto.start.45= \ -reference:file:sc-bundles/ui-service.jar \ + reference:file:sc-bundles/ui-service.jar \ + reference:file:sc-bundles/argdelegation-service.jar \ reference:file:sc-bundles/version.jar \ reference:file:sc-bundles/version-impl.jar \ reference:file:sc-bundles/branding.jar @@ -93,8 +95,8 @@ felix.auto.start.50= \ reference:file:sc-bundles/protocol-dict.jar felix.auto.start.55= \ - reference:file:sc-bundles/meta-cl.jar - + reference:file:sc-bundles/meta-cl.jar + felix.auto.start.60= \ reference:file:sc-bundles/history.jar \ @@ -130,14 +132,19 @@ felix.auto.start.67= \ reference:file:sc-bundles/contactinfo.jar \ reference:file:sc-bundles/accountinfo.jar \ reference:file:sc-bundles/chatalerter.jar \ - reference:file:sc-bundles/shutdown.jar \ reference:file:sc-bundles/autoaway.jar \ reference:file:sc-bundles/keybindingChooser.jar \ reference:file:sc-bundles/generalconfig.jar \ reference:file:sc-bundles/dictaccregwizz.jar felix.auto.start.70= \ - reference:file:sc-bundles/simpleaccreg.jar + reference:file:sc-bundles/simpleaccreg.jar + +felix.auto.start.75= \ + reference:file:sc-bundles/argdelegation.jar + +felix.auto.start.80= \ + reference:file:sc-bundles/shutdown.jar # Uncomment the following lines if you want to run the architect viewer # bundle. diff --git a/lib/logging.properties b/lib/logging.properties index 7162d2e..902a3a5 100644 --- a/lib/logging.properties +++ b/lib/logging.properties @@ -43,7 +43,7 @@ net.java.sip.communicator.util.FileHandler.level = FINEST # Limit the message that are printed on the console to FINEST and above (all). -java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.level = WARNING java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter @@ -61,20 +61,7 @@ ymsg.network.level = INFO net.sf.cindy.impl.level = INFO # But we want everything coming from the sip-comm -net.java.sip.communicator.impl.level = INFO -net.java.sip.communicator.impl.protocol.level = INFO -net.java.sip.communicator.impl.shutdown.level = INFO -net.java.sip.communicator.impl.contactlist.level = INFO -net.java.sip.communicator.slick.level = INFO -net.java.sip.communicator.impl.level = INFO -net.java.sip.communicator.service.level = INFO -net.java.sip.communicator.util.level = INFO -net.java.sip.communicator.service.configuration.level = INFO -net.java.sip.communicator.impl.configuration.level = INFO -net.java.sip.communicator.impl.history.level = INFO -net.java.sip.communicator.impl.gui.level = INFO -net.java.sip.communicator.impl.protocol.zeroconf.jmdns.level = WARNING -net.java.sip.communicator.impl.media.level = WARNING +net.java.sip.communicator.level = WARNING # For example, set the com.xyz.foo logger to only log SEVERE # messages: diff --git a/lib/testing.properties b/lib/testing.properties index 47cab6f..194ee4f 100644 --- a/lib/testing.properties +++ b/lib/testing.properties @@ -2,19 +2,7 @@ # environment when running automated testing. # A list of all tests that should be run by the test target of the project. -net.java.sip.communicator.slick.runner.TEST_LIST=ConfigurationServiceLick \ - MetaContactListServiceLick \ - NetworkAddressManagerServiceLick \ - FileAccessServiceLick \ - HistoryServiceLick \ - SlicklessTests \ - MsgHistoryServiceLick \ - CallHistoryServiceLick \ - JabberProtocolProviderSlick \ - YahooProtocolProviderSlick \ - MsnProtocolProviderSlick \ - GibberishProtocolProviderServiceLick \ - RssProtocolProviderServiceLick +net.java.sip.communicator.slick.runner.TEST_LIST=JabberProtocolProviderSlick # MediaServiceLick \ # SipProtocolProviderServiceLick \ diff --git a/src/net/java/sip/communicator/impl/argdelegation/ArgDelegationActivator.java b/src/net/java/sip/communicator/impl/argdelegation/ArgDelegationActivator.java new file mode 100644 index 0000000..99ed0e3 --- /dev/null +++ b/src/net/java/sip/communicator/impl/argdelegation/ArgDelegationActivator.java @@ -0,0 +1,94 @@ +/* + * 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.impl.argdelegation; + +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.version.*; +import net.java.sip.communicator.util.launchutils.*; + +import org.osgi.framework.*; + +/** + * Activates the <tt>ArgDelegationService</tt> and registers a URI delegation + * peer with the util package arg manager so that we would be notified when the + * application receives uri arguments. + * + * @author Emil Ivov + */ +public class ArgDelegationActivator + implements BundleActivator +{ + /** + * A reference to the bundle context that is currently in use. + */ + private static BundleContext bundleContext = null; + + /** + * A reference to the delegation peer implementation that is currently + * handling uri arguments. + */ + private UriDelegationPeerImpl delegationPeer = null; + + /** + * A reference to the <tt>UIService</tt> currently in use in + * SIP Communicator. + */ + private static UIService uiService = null; + + /** + * Starts the arg delegation bundle and registers the delegationPeer with + * the util package URI manager. + * + * @param bc a reference to the currently active bundle context. + */ + @Override + public void start(BundleContext bc) throws Exception + { + bundleContext = bc; + delegationPeer = new UriDelegationPeerImpl(bc); + bc.addServiceListener(delegationPeer); + + //register our instance of delegation peer. + LaunchArgHandler.getInstance().setDelegationPeer(delegationPeer); + + } + + /** + * Unsets the delegation peer instance that we set when we start this + * bundle. + * + * @param bc an instance of the currently valid bundle context. + */ + @Override + public void stop(BundleContext bc) throws Exception + { + uiService = null; + bc.removeServiceListener(delegationPeer); + delegationPeer = null; + LaunchArgHandler.getInstance().setDelegationPeer(null); + } + + /** + * Returns a reference to an UIService implementation currently registered + * in the bundle context or null if no such implementation was found. + * + * @return a reference to an UIService implementation currently registered + * in the bundle context or null if no such implementation was found. + */ + public static UIService getUIService() + { + if(uiService == null) + { + ServiceReference versionServiceReference + = bundleContext.getServiceReference( + UIService.class.getName()); + uiService = (UIService)bundleContext + .getService(versionServiceReference); + } + return uiService; + } +} diff --git a/src/net/java/sip/communicator/impl/argdelegation/UriDelegationPeerImpl.java b/src/net/java/sip/communicator/impl/argdelegation/UriDelegationPeerImpl.java new file mode 100644 index 0000000..9d58951 --- /dev/null +++ b/src/net/java/sip/communicator/impl/argdelegation/UriDelegationPeerImpl.java @@ -0,0 +1,186 @@ +/* + * 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.impl.argdelegation; + +import java.util.*; + +import org.osgi.framework.*; + +import net.java.sip.communicator.service.argdelegation.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.version.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.launchutils.*; + + +/** + * Implements the <tt>UriDelegationPeer</tt> interface from our argument handler + * utility. We use this handler to relay arguments to URI handlers that have + * been registered from other services such as the SIP provider for example. + * + * @author Emil Ivov + */ +public class UriDelegationPeerImpl + implements UriDelegationPeer, ServiceListener +{ + private static final Logger logger = + Logger.getLogger(UriDelegationPeerImpl.class); + + /** + * The list of uriHandlers that we are currently aware of. + */ + private Map<String, UriHandler> uriHandlers + = new Hashtable<String, UriHandler>(); + + /** + * Creates an instance of this peer and scans <tt>bundleContext</tt> for all + * existing <tt>UriHandler</tt>s + * + * @param bundleContext a reference to a currently valid instance of a + * bundle context. + */ + public UriDelegationPeerImpl(BundleContext bundleContext) + { + ServiceReference[] uriHandlerRefs = null; + + synchronized (uriHandlers) + { + try + { + uriHandlerRefs = bundleContext.getServiceReferences( + UriHandler.class.getName(), null); + } + catch (InvalidSyntaxException exc) + { + // this shouldn't happen because we aren't using a filter + // but let's log just the same. + logger.info("An error occurred while retrieving UriHandlers", + exc); + return; + } + + if(uriHandlerRefs == null) + { + //none URI handlers are registered at this point. Some might + //come later. + return; + } + + for (ServiceReference uriHandlerRef : uriHandlerRefs) + { + UriHandler uriHandler = (UriHandler) bundleContext + .getService(uriHandlerRef); + uriHandlers.put(uriHandler.getProtocol(), uriHandler); + } + } + } + + /** + * Listens for <tt>UriHandlers</tt> that are registered in the bundle + * context after we had started so that we could add them to the list + * of currently known handlers. + * + * @param event the event containing the newly (un)registered service. + */ + public void serviceChanged(ServiceEvent event) + { + synchronized (uriHandlers) + { + BundleContext bc = event.getServiceReference().getBundle() + .getBundleContext(); + + Object service = bc.getService(event.getServiceReference()); + + //we are only interested in UriHandler-s + if(!(service instanceof UriHandler) ) + { + return; + } + + if (event.getType() == ServiceEvent.MODIFIED + || event.getType() == ServiceEvent.REGISTERED) + { + UriHandler uriHandler = (UriHandler) bc.getService(event + .getServiceReference()); + + uriHandlers.put(uriHandler.getProtocol(), uriHandler); + } + else if (event.getType() == ServiceEvent.UNREGISTERING) + { + UriHandler uriHandler = (UriHandler) bc.getService(event + .getServiceReference()); + + if(uriHandlers.get(uriHandler.getProtocol()) == uriHandler) + uriHandlers.remove(uriHandler.getProtocol()); + } + } + } + + /** + * Relays <tt>uirArg</tt> to the corresponding handler or shows an error + * message in case no handler has been registered for the corresponding + * protocol. + * + * @param uriArg the uri that we've been passed and that we'd like to + * delegate to the corresponding provider. + */ + public void handleUri(String uriArg) + { + logger.trace("Handling URI: " + uriArg); + //first parse the uri and determine the scheme/protocol + //the parsing is currently a bit oversimplified so we'd probably need + //to revisit it at some point. + int colonIndex = uriArg.indexOf(":"); + + if( colonIndex == -1) + { + //no scheme, we don't know how to handle the URI + ArgDelegationActivator.getUIService().getPopupDialog() + .showMessagePopupDialog( + "Could not determine how to handle: " + uriArg + + ".\nNo protocol scheme found.", + "Error handling URI", + PopupDialog.ERROR_MESSAGE); + return; + } + + String scheme = uriArg.substring(0, colonIndex); + + UriHandler handler = uriHandlers.get(scheme); + + //if handler is null we need to tell the user. + if(handler == null) + { + logger.trace("Couldn't open " + uriArg + + "No handler found for protocol"+ scheme); + ArgDelegationActivator.getUIService().getPopupDialog() + .showMessagePopupDialog( + "\"" + scheme + "\" URIs are currently not supported.", + "Error handling URI", + PopupDialog.ERROR_MESSAGE); + return; + } + + //we're all set. let's do the handling now. + try + { + handler.handleUri(uriArg); + } + //catch every possible exception + catch(Throwable thr) + { + ArgDelegationActivator.getUIService().getPopupDialog() + .showMessagePopupDialog( + "Error handling " + uriArg, + "Error handling URI", + PopupDialog.ERROR_MESSAGE); + logger.error("Failed to handle \""+ uriArg +"\"", thr); + } + } + + +} diff --git a/src/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf b/src/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf new file mode 100644 index 0000000..ba896e7 --- /dev/null +++ b/src/net/java/sip/communicator/impl/argdelegation/argdelegation.manifest.mf @@ -0,0 +1,13 @@ +Bundle-Activator: net.java.sip.communicator.impl.argdelegation.ArgDelegationActivator +Bundle-Name: Argument Delegation +Bundle-Description: A bundle that delegates invocation arguments to register handler services +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Import-Package: org.osgi.framework, + net.java.sip.communicator.util, + net.java.sip.communicator.util.launchutils, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.argdelegation, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.protocol.event, + net.java.sip.communicator.service.gui diff --git a/src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java b/src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java index 1ab0438..5218f53 100644 --- a/src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java +++ b/src/net/java/sip/communicator/impl/contactlist/MclStorageManager.java @@ -91,7 +91,7 @@ public class MclStorageManager * A reference to the file containing the locally stored meta contact list. */ private File contactlistFile = null; - + /** * A reference to the failsafe transaction used with the contactlist file. */ @@ -174,13 +174,13 @@ public class MclStorageManager */ private static final String META_CONTACT_DISPLAY_NAME_NODE_NAME = "display-name"; - + /** * The name of the XML node that contains meta contact detail. */ private static final String META_CONTACT_DETAIL_NAME_NODE_NAME = "detail"; - + /** * The name of the XML attribute that contains detail name. */ @@ -190,7 +190,7 @@ public class MclStorageManager * The name of the XML attribute that contains detail value. */ private static final String DETAIL_VALUE_ATTR_NAME = "value"; - + /** * The name of the XML node that contains information of a proto contact */ @@ -400,7 +400,7 @@ public class MclStorageManager } catch (IllegalStateException e) { logger.error("the contactlist file is missing", e); } - + // really write the modification OutputStream stream = new FileOutputStream(contactlistFile); XMLUtils.indentedWriteXML(contactListDocument, @@ -573,14 +573,14 @@ public class MclStorageManager { if(!isStarted()) return; - + //we don't want to receive meta contact events triggerred by ourselves //so we stop listening. it is possible but very unlikely that other //events, not triggerred by us are received while we're off the channel //but that would be a very bizzare case ..... I guess we got to live //with the risk. this.mclServiceImpl.removeMetaContactListListener(this); - + try { Element root = findMetaContactGroupNode( @@ -591,12 +591,12 @@ public class MclStorageManager // If there is no root, there is definitely something wrong // really broken file will create it again logger.fatal("The contactlist file is recreated cause its broken"); - + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); contactListDocument = builder.newDocument(); - + initVirginDocument(mclServiceImpl, contactListDocument); //write the contact list so that it is there for the parser @@ -608,11 +608,11 @@ public class MclStorageManager //parse the group node and extract all its child groups and contacts processGroupXmlNode(mclServiceImpl, accountID, root , null, null); - + //now save the contact list in case it has changed scheduleContactListStorage(); } - + }catch(Throwable exc) { // catch everything because we MUST NOT disturb the thread @@ -779,7 +779,7 @@ public class MclStorageManager //contain any contacts matching the currently parsed account id. if (protoContacts.size() < 1) continue; - + // Extract contact details. Hashtable details = new Hashtable(); try @@ -787,7 +787,7 @@ public class MclStorageManager List detailsNodes = XMLUtils.findChildren( (Element) currentMetaContactNode , META_CONTACT_DETAIL_NAME_NODE_NAME); - for (int j = 0; j < detailsNodes.size(); j++) + for (int j = 0; j < detailsNodes.size(); j++) { Element e = (Element)detailsNodes.get(j); String name = e.getAttribute(DETAIL_NAME_ATTR_NAME); @@ -795,7 +795,7 @@ public class MclStorageManager Object detailsObj = details.get(name); if(detailsObj == null) - { + { ArrayList ds = new ArrayList(); ds.add(value); details.put(name, ds); @@ -808,7 +808,7 @@ public class MclStorageManager { // catch any exception from loading contacts // that will prevent loading the contact - logger.error("Cannot load details for contact node " + + logger.error("Cannot load details for contact node " + currentMetaContactNode, ex); } @@ -1203,7 +1203,7 @@ public class MclStorageManager * @param evt the MetaContactListEvent containing the corresponding contact */ public void metaContactGroupAdded(MetaContactGroupEvent evt) - { + { //if the group was created as an encapsulator of a non persistent proto //group then we'll ignore it. if (evt.getSourceProtoGroup() != null @@ -1449,7 +1449,7 @@ public class MclStorageManager + evt.getSourceMetaContact(), ex); } } - + /** * Indicates that a MetaContact has been modified. * @param evt the MetaContactModifiedEvent containing the corresponding contact @@ -1457,7 +1457,7 @@ public class MclStorageManager public void metaContactModified(MetaContactModifiedEvent evt) { String name = evt.getModificationName(); - + Element metaContactNode = findMetaContactNode( evt.getSourceMetaContact().getMetaUID()); @@ -1469,26 +1469,26 @@ public class MclStorageManager + evt.getSourceMetaContact()); return; } - + Object oldValue = evt.getOldValue(); Object newValue = evt.getNewValue(); - + boolean isChanged = false; - + if(oldValue == null && newValue != null) { // indicates add - + if(!(newValue instanceof String)) return; - + Element detailElement = contactListDocument.createElement( META_CONTACT_DETAIL_NAME_NODE_NAME); - + detailElement.setAttribute(DETAIL_NAME_ATTR_NAME, name); - detailElement.setAttribute(DETAIL_VALUE_ATTR_NAME, + detailElement.setAttribute(DETAIL_VALUE_ATTR_NAME, (String)newValue); - + metaContactNode.appendChild(detailElement); isChanged = true; } @@ -1506,7 +1506,7 @@ public class MclStorageManager , name); ArrayList nodesToRemove = new ArrayList(); - for (int i = 0; i < nodes.size(); i++) + for (int i = 0; i < nodes.size(); i++) { Element e = (Element)nodes.get(i); if(valuesToRemove.contains( @@ -1515,8 +1515,8 @@ public class MclStorageManager nodesToRemove.add(e); } } - - for (int i = 0; i < nodesToRemove.size(); i++) + + for (int i = 0; i < nodesToRemove.size(); i++) { Element e = (Element)nodesToRemove.get(i); metaContactNode.removeChild(e); @@ -1534,7 +1534,7 @@ public class MclStorageManager , name); Element elementToRemove = null; - for (int i = 0; i < nodes.size(); i++) + for (int i = 0; i < nodes.size(); i++) { Element e = (Element)nodes.get(i); if(e.getAttribute(DETAIL_VALUE_ATTR_NAME).equals(oldValue)) @@ -1543,12 +1543,12 @@ public class MclStorageManager break; } } - + if(elementToRemove == null) return; - + metaContactNode.removeChild(elementToRemove); - + isChanged = true; } } @@ -1562,7 +1562,7 @@ public class MclStorageManager , name); Element changedElement = null; - for (int i = 0; i < nodes.size(); i++) + for (int i = 0; i < nodes.size(); i++) { Element e = (Element)nodes.get(i); if(e.getAttribute(DETAIL_VALUE_ATTR_NAME).equals(oldValue)) @@ -1571,16 +1571,16 @@ public class MclStorageManager break; } } - + if(changedElement == null) return; - - changedElement.setAttribute(DETAIL_VALUE_ATTR_NAME, + + changedElement.setAttribute(DETAIL_VALUE_ATTR_NAME, (String)newValue); - + isChanged = true; } - + if(!isChanged) return; diff --git a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java index 689e941..228b8bf 100644 --- a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java +++ b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java @@ -329,7 +329,7 @@ public class UIServiceImpl /** * Unregisters the given <tt>ExportedWindow</tt> from the list of windows * that could be accessed from other bundles. - * + * * @param window the window to no longer be exported */ public void unregisterExportedWindow(ExportedWindow window) @@ -482,7 +482,7 @@ public class UIServiceImpl /** * Returns the <tt>Chat</tt> corresponding to the given <tt>ChatRoom</tt>. - * + * * @param chatRoom the <tt>ChatRoom</tt> for which the searched chat is * about. * @return the <tt>Chat</tt> corresponding to the given <tt>ChatRoom</tt>. @@ -560,6 +560,25 @@ public class UIServiceImpl } /** + * Returns a default implementation of the <tt>SecurityAuthority</tt> + * interface that can be used by non-UI components that would like to launch + * the registration process for a protocol provider. Initially this method + * was meant for use by the systray bundle and the protocol URI handlers. + * + * @param protocolProvider the <tt>ProtocolProviderService</tt> for which + * the authentication window is about. + * + * @return a default implementation of the <tt>SecurityAuthority</tt> + * interface that can be used by non-UI components that would like to launch + * the registration process for a protocol provider. + */ + public SecurityAuthority getDefaultSecurityAuthority( + ProtocolProviderService protocolProvider) + { + return new DefaultSecurityAuthority(protocolProvider); + } + + /** * Returns the LoginManager. * @return the LoginManager */ @@ -683,7 +702,7 @@ public class UIServiceImpl /** * Returns the corresponding <tt>BorderLayout</tt> constraint from the given * <tt>Container</tt> constraint. - * + * * @param containerConstraints constraints defined in the <tt>Container</tt> * @return the corresponding <tt>BorderLayout</tt> constraint from the given * <tt>Container</tt> constraint. @@ -710,7 +729,7 @@ public class UIServiceImpl return layoutConstraint; } - + private class DefaultPluginComponent implements PluginComponent { private Component component; diff --git a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java index 653a8bd..862fa9a 100755 --- a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java +++ b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java @@ -61,7 +61,7 @@ public class MainFrame private JPanel contactListPanel = new JPanel(new BorderLayout()); private JPanel mainPanel = new JPanel(new BorderLayout(0, 5)); - + private MainMenu menu; private CallManager callManager; @@ -171,7 +171,7 @@ public class MainFrame this.getContentPane().add(northPanel, BorderLayout.NORTH); this.getContentPane().add(mainPanel, BorderLayout.CENTER); } - + /** * Sets frame size and position. */ @@ -341,7 +341,7 @@ public class MainFrame multiUserChat.addInvitationListener(multiUserChatManager); multiUserChat.addInvitationRejectionListener(multiUserChatManager); multiUserChat.addPresenceListener(multiUserChatManager); - + this.getChatRoomsListPanel() .getChatRoomsList() .addChatServer(protocolProvider, multiUserChat); @@ -357,7 +357,7 @@ public class MainFrame { return ((LinkedHashMap)protocolProviders.clone()).keySet().iterator(); } - + /** * Returns the protocol provider associated to the account given * by the account user identifier. @@ -429,10 +429,10 @@ public class MainFrame public void addAccount(ProtocolProviderService protocolProvider) { if (!getStatusPanel().containsAccount(protocolProvider)) { - + logger.trace("Add the following account to the status bar: " + protocolProvider.getAccountID().getAccountAddress()); - + this.getStatusPanel().addAccount(protocolProvider); //request the focus in the contact list panel, which @@ -440,7 +440,7 @@ public class MainFrame this.tabbedPane.getContactListPanel().getContactList() .requestFocus(); } - + if(!callManager.containsCallAccount(protocolProvider) && getTelephonyOpSet(protocolProvider) != null) { callManager.addCallAccount(protocolProvider); @@ -503,10 +503,10 @@ public class MainFrame { OperationSet opSet = protocolProvider.getOperationSet(OperationSetPresence.class); - + if(opSet != null && opSet instanceof OperationSetPresence) return (OperationSetPresence) opSet; - + return null; } @@ -521,13 +521,13 @@ public class MainFrame */ public OperationSetWebContactInfo getWebContactInfoOpSet( ProtocolProviderService protocolProvider) - { + { OperationSet opSet = protocolProvider.getOperationSet(OperationSetWebContactInfo.class); - + if(opSet != null && opSet instanceof OperationSetWebContactInfo) return (OperationSetWebContactInfo) opSet; - + return null; } @@ -544,13 +544,13 @@ public class MainFrame { OperationSet opSet = protocolProvider.getOperationSet(OperationSetBasicTelephony.class); - + if(opSet != null && opSet instanceof OperationSetBasicTelephony) return (OperationSetBasicTelephony) opSet; - + return null; } - + /** * Returns the multi user chat operation set for the given protocol provider. * @@ -564,10 +564,10 @@ public class MainFrame { OperationSet opSet = protocolProvider.getOperationSet(OperationSetMultiUserChat.class); - + if(opSet != null && opSet instanceof OperationSetMultiUserChat) return (OperationSetMultiUserChat) opSet; - + return null; } @@ -598,7 +598,7 @@ public class MainFrame { /** * Indicates that a contact has changed its status. - * + * * @param evt the presence event containing information about the * contact status change */ @@ -716,7 +716,7 @@ public class MainFrame ConfigurationManager.setApplicationVisible(false); } } - + public void windowClosed(WindowEvent e) { if(GuiActivator.getUIService().getExitOnMainWindowClose()) @@ -738,7 +738,7 @@ public class MainFrame //System.exit(0); } } - } + } /** * Returns the class that manages user login. @@ -771,7 +771,7 @@ public class MainFrame { return this.tabbedPane.getContactListPanel(); } - + /** * Returns the panel containing the chat rooms list. * @return the panel containing the chat rooms list @@ -802,7 +802,7 @@ public class MainFrame // Should remove all participant panels explicetly, thus removing also // all related dialogs (like dialpad for example). callPanel.removeDialogs(); - + tabbedPane.remove(callPanel); Component c = getSelectedTab(); @@ -1180,7 +1180,7 @@ public class MainFrame }; /** - * + * * @param protocolProvider * @param contactHandler */ @@ -1194,7 +1194,7 @@ public class MainFrame /** * Returns the <tt>ContactEventHandler</tt> registered for this protocol * provider. - * + * * @param protocolProvider the <tt>ProtocolProviderService</tt> for which * we are searching a <tt>ContactEventHandler</tt>. * @return the <tt>ContactEventHandler</tt> registered for this protocol @@ -1207,7 +1207,7 @@ public class MainFrame } /** - * + * * @param protocolProvider * @return */ @@ -1434,7 +1434,7 @@ public class MainFrame /** * Paints the logo bar. - * + * * @param g the <tt>Graphics</tt> object used to paint the background * image of this logo bar. */ @@ -1538,7 +1538,7 @@ public class MainFrame { this.setExtendedState(JFrame.ICONIFIED); } - + /** * Implements <code>isVisible</code> in the UIService interface. Checks if * the main application window is visible. @@ -1559,7 +1559,7 @@ public class MainFrame else return false; } - + /** * Implements <code>setVisible</code> in the UIService interface. Shows or * hides the main application window depending on the parameter @@ -1576,15 +1576,15 @@ public class MainFrame public void run() { if(isVisible) - { - MainFrame.this.addNativePlugins(); - MainFrame.super.setVisible(isVisible); - MainFrame.super.toFront(); - } - else - { - MainFrame.super.setVisible(isVisible); - } + { + MainFrame.this.addNativePlugins(); + MainFrame.super.setVisible(isVisible); + MainFrame.super.toFront(); + } + else + { + MainFrame.super.setVisible(isVisible); + } } }); } @@ -1592,7 +1592,7 @@ public class MainFrame /** * Adds the given component with to the container corresponding to the * given constraints. - * + * * @param c the component to add * @param constraints the constraints determining the container */ @@ -1632,7 +1632,7 @@ public class MainFrame /** * Removes the given component from the container corresponding to the given * constraints. - * + * * @param c the component to remove * @param constraints the constraints determining the container */ diff --git a/src/net/java/sip/communicator/impl/gui/main/login/DefaultSecurityAuthority.java b/src/net/java/sip/communicator/impl/gui/main/login/DefaultSecurityAuthority.java new file mode 100644 index 0000000..03fe01e --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/login/DefaultSecurityAuthority.java @@ -0,0 +1,106 @@ +/* + * 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.impl.gui.main.login; + +import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.protocol.*; + +/** + * Utility class that can be used in cases where components other than the main + * user interface may need to launch provider registration. At the time I am + * writing this, the <tt>DefaultSecurityAuthority</tt> is being used by the + * systray and + * + * @author Emil Ivov + */ +public class DefaultSecurityAuthority + implements SecurityAuthority +{ + private boolean isUserNameEditable = false; + + /** + * The provider that this authority would be responsible for. + */ + private ProtocolProviderService provider = null; + + /** + * Creates this authority for a particular provider. + */ + public DefaultSecurityAuthority(ProtocolProviderService provider) + { + this.provider = provider; + } + + /** + * Used to login to the protocol providers + * + * @param realm the realm that the credentials are needed for + * @param userCredentials the values to propose the user by default + * @return The Credentials associated with the speciefied realm + */ + public UserCredentials obtainCredentials( + String realm, + UserCredentials userCredentials) + { + return obtainCredentials( realm, + userCredentials, + SecurityAuthority.AUTHENTICATION_REQUIRED); + } + + + /** + * Used to login to the protocol providers + * + * @param realm the realm that the credentials are needed for + * @param userCredentials the values to propose the user by default + * @param reasonCode the reason for which we're asking for credentials + * @return The Credentials associated with the speciefied realm + */ + public UserCredentials obtainCredentials( + String realm, + UserCredentials userCredentials, + int reasonCode) + { + ExportedWindow loginWindow + = GuiActivator.getUIService() + .getAuthenticationWindow(provider, + realm, + userCredentials, + isUserNameEditable); + + loginWindow.setVisible(true); + + return userCredentials; + } + + + /** + * Sets the userNameEditable property, which should indicate to the + * implementations of this interface if the user name could be changed + * by user or not. + * + * @param isUserNameEditable indicates if the user name could be changed + * by user in the implementation of this interface. + */ + public void setUserNameEditable(boolean isUserNameEditable) + { + this.isUserNameEditable = isUserNameEditable; + } + + /** + * Indicates if the user name is currently editable, i.e. could be + * changed by user or not. + * + * @return <tt>true</tt> if the user name could be changed and + * <tt>false</tt> otherwise. + */ + public boolean isUserNameEditable() + { + return isUserNameEditable; + } +} diff --git a/src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java b/src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java index 9eafcd4..c67c0c6 100644 --- a/src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java +++ b/src/net/java/sip/communicator/impl/msghistory/MessageHistoryServiceImpl.java @@ -71,7 +71,7 @@ public class MessageHistoryServiceImpl private ConfigurationService configService; private MessageHistoryPropertyChangeListener msgHistoryPropListener; - + private static ResourceManagementService resourcesService; public HistoryService getHistoryService() @@ -96,7 +96,7 @@ public class MessageHistoryServiceImpl Hashtable readers = getHistoryReaders(contact); int recordsCount = countRecords(readers); - + Iterator iter = readers.keySet().iterator(); while (iter.hasNext()) { @@ -141,7 +141,7 @@ public class MessageHistoryServiceImpl Hashtable readers = getHistoryReaders(contact); int recordsCount = countRecords(readers); - + Iterator iter = readers.keySet().iterator(); while (iter.hasNext()) { @@ -186,7 +186,7 @@ public class MessageHistoryServiceImpl Hashtable readers = getHistoryReaders(contact); int recordsCount = countRecords(readers); - + Iterator iter = readers.keySet().iterator(); while (iter.hasNext()) { @@ -353,11 +353,11 @@ public class MessageHistoryServiceImpl } LinkedList resultAsList = new LinkedList(result); - + int toIndex = count; if(toIndex > resultAsList.size()) toIndex = resultAsList.size(); - + return resultAsList.subList(0, toIndex); } @@ -443,10 +443,10 @@ public class MessageHistoryServiceImpl return retVal; } - + /** * Returns the history by specified local contact - * (if is null the default is used) + * (if is null the default is used) * and by the chat room * * @param room The chat room @@ -456,20 +456,20 @@ public class MessageHistoryServiceImpl private History getHistoryForMultiChat( Contact localContact, ChatRoom room) - throws IOException + throws IOException { AccountID account = room.getParentProvider().getAccountID(); return this.getHistoryForMultiChat( - null, + null, account.getAccountUniqueID(), account.getService(), room.getName()); } - + /** * Returns the history by specified local contact - * (if is null the default is used) + * (if is null the default is used) * and by accountUniqueID, channel and server * used by the multichat account. * @@ -485,7 +485,7 @@ public class MessageHistoryServiceImpl String account, String server, String channel) - throws IOException + throws IOException { History retVal = null; @@ -494,8 +494,8 @@ public class MessageHistoryServiceImpl HistoryID historyId = HistoryID.createFromRawID( new String[] { "messages", - localId, - account, + localId, + account, channel + "@" + server }); if (this.historyService.isHistoryExisting(historyId)) @@ -550,9 +550,9 @@ public class MessageHistoryServiceImpl contact, timestamp); } - + /** - * Used to convert HistoryRecord in ChatRoomMessageDeliveredEvent or + * Used to convert HistoryRecord in ChatRoomMessageDeliveredEvent or * ChatRoomMessageReceivedEvent * which are returned by the finder methods * @@ -580,10 +580,10 @@ public class MessageHistoryServiceImpl } else timestamp = hr.getTimestamp(); - + // 5 is the index of the subject in the structure String fromStr = hr.getPropertyValues()[5]; - + ChatRoomMember from = new ChatRoomMemberImpl(fromStr, room, null); if(msg.isOutgoing) @@ -669,15 +669,15 @@ public class MessageHistoryServiceImpl configService = (ConfigurationService) bundleContext.getService(refConfig); - // Check if the message history is enabled in the configuration + // Check if the message history is enabled in the configuration // service, and if not do not register the service. String isMessageHistoryEnabledPropertyString = "net.java.sip.communicator.impl.msghistory.isMessageHistoryEnabled"; String isMessageHistoryEnabledString = configService.getString( isMessageHistoryEnabledPropertyString); - + if(isMessageHistoryEnabledString == null) - isMessageHistoryEnabledString = + isMessageHistoryEnabledString = getResources(). getSettingsString(isMessageHistoryEnabledPropertyString); @@ -737,19 +737,19 @@ public class MessageHistoryServiceImpl public void messageDeliveryFailed(MessageDeliveryFailedEvent evt) { } - + // ////////////////////////////////////////////////////////////////////////// // ChatRoomMessageListener implementation methods - + public void messageReceived(ChatRoomMessageReceivedEvent evt) { - try + try { History history = this.getHistoryForMultiChat( - null, + null, evt.getSourceChatRoom()); - - writeMessage(history, "in", evt.getSourceChatRoomMember(), + + writeMessage(history, "in", evt.getSourceChatRoomMember(), evt.getMessage(), evt.getTimestamp()); } catch (IOException e) { @@ -759,13 +759,13 @@ public class MessageHistoryServiceImpl public void messageDelivered(ChatRoomMessageDeliveredEvent evt) { - try + try { History history = this. getHistoryForMultiChat( - null, + null, evt.getSourceChatRoom()); - + writeMessage(history, "out", evt.getMessage(), evt.getTimestamp()); } catch (IOException e) { @@ -776,11 +776,11 @@ public class MessageHistoryServiceImpl public void messageDeliveryFailed(ChatRoomMessageDeliveryFailedEvent evt) { } - + /** * Writes message to the history * @param direction String direction of the message - * @param source The source Contact + * @param source The source Contact * @param destination The destiantion Contact * @param message Message message to be written * @param messageTimestamp Date this is the timestamp when was message received @@ -791,14 +791,14 @@ public class MessageHistoryServiceImpl { try { History history = this.getHistory(source, destination); - + writeMessage(history, direction, message, messageTimestamp); } catch (IOException e) { logger.error("Could not add message to history", e); } } - + /** * Writes message to the history * @param history The history to which will write the message @@ -821,7 +821,7 @@ public class MessageHistoryServiceImpl logger.error("Could not add message to history", e); } } - + /** * Writes message to the history * @param history The history to which will write the message @@ -838,7 +838,7 @@ public class MessageHistoryServiceImpl historyWriter.addRecord(new String[] { direction, message.getContent(), message.getContentType(), message.getEncoding(), message.getMessageUID(), - from.getContactAddress(), + from.getContactAddress(), String.valueOf(messageTimestamp.getTime()) }, new Date()); // this date is when the history record is written } catch (IOException e) @@ -846,7 +846,7 @@ public class MessageHistoryServiceImpl logger.error("Could not add message to history", e); } } - + // ////////////////////////////////////////////////////////////////////////// /** @@ -942,7 +942,7 @@ public class MessageHistoryServiceImpl { logger.trace("Service did not have a im op. set."); } - + OperationSetMultiUserChat opSetMultiUChat = (OperationSetMultiUserChat) provider .getSupportedOperationSets().get( @@ -950,7 +950,7 @@ public class MessageHistoryServiceImpl if (opSetMultiUChat != null) { - Iterator iter = + Iterator iter = opSetMultiUChat.getCurrentlyJoinedChatRooms().iterator(); while(iter.hasNext()) @@ -958,7 +958,7 @@ public class MessageHistoryServiceImpl ChatRoom room = (ChatRoom)iter.next(); room.addMessageListener(this); } - + opSetMultiUChat.addPresenceListener(this); } else @@ -984,7 +984,7 @@ public class MessageHistoryServiceImpl { opSetIm.removeMessageListener(this); } - + OperationSetMultiUserChat opSetMultiUChat = (OperationSetMultiUserChat) provider .getSupportedOperationSets().get( @@ -992,9 +992,9 @@ public class MessageHistoryServiceImpl if (opSetMultiUChat != null) { - Iterator iter = + Iterator iter = opSetMultiUChat.getCurrentlyJoinedChatRooms().iterator(); - + while(iter.hasNext()) { ChatRoom room = (ChatRoom)iter.next(); @@ -1002,7 +1002,7 @@ public class MessageHistoryServiceImpl } } } - + /** * Called to notify interested parties that a change in our presence in * a chat room has occured. Changes may include us being kicked, join, @@ -1012,7 +1012,7 @@ public class MessageHistoryServiceImpl */ public void localUserPresenceChanged(LocalUserChatRoomPresenceChangeEvent evt) { - if(evt.getEventType() == + if(evt.getEventType() == LocalUserChatRoomPresenceChangeEvent.LOCAL_USER_JOINED) { if (!evt.getChatRoom().isSystem()) @@ -1119,7 +1119,7 @@ public class MessageHistoryServiceImpl Hashtable readers = getHistoryReaders(contact); int recordsCount = countRecords(readers); - + Iterator iter = readers.keySet().iterator(); while (iter.hasNext()) { @@ -1167,7 +1167,7 @@ public class MessageHistoryServiceImpl Hashtable readers = getHistoryReaders(contact); int recordsCount = countRecords(readers); - + Iterator iter = readers.keySet().iterator(); while (iter.hasNext()) { @@ -1215,7 +1215,7 @@ public class MessageHistoryServiceImpl Hashtable readers = getHistoryReaders(contact); int recordsCount = countRecords(readers); - + Iterator iter = readers.keySet().iterator(); while (iter.hasNext()) { @@ -1269,22 +1269,22 @@ public class MessageHistoryServiceImpl } return readers; } - + /** * Total count of records for supplied history readers will read through - * + * * @param readers hashtable with pairs contact <-> history reader * @return the number of searched messages - * @throws UnsupportedOperationException + * @throws UnsupportedOperationException * Thrown if an exception occurs during the execution of the * query, such as internal IO error. */ public int countRecords(Hashtable readers) { int result = 0; - + Enumeration readersEnum = readers.elements(); - + while (readersEnum.hasMoreElements()) { HistoryReader r = (HistoryReader)readersEnum.nextElement(); @@ -1293,9 +1293,9 @@ public class MessageHistoryServiceImpl return result; } - + /** - * Returns all the messages exchanged in the supplied + * Returns all the messages exchanged in the supplied * chat room after the given date * * @param room The chat room @@ -1310,7 +1310,7 @@ public class MessageHistoryServiceImpl try { // get the readers for this room - HistoryReader reader = + HistoryReader reader = this.getHistoryForMultiChat(null, room).getReader(); // add the progress listeners @@ -1328,12 +1328,12 @@ public class MessageHistoryServiceImpl { logger.error("Could not read history", e); } - + return result; } /** - * Returns all the messages exchanged + * Returns all the messages exchanged * in the supplied chat room before the given date * * @param room The chat room @@ -1348,7 +1348,7 @@ public class MessageHistoryServiceImpl try { // get the readers for this room - HistoryReader reader = + HistoryReader reader = this.getHistoryForMultiChat(null, room).getReader(); // add the progress listeners @@ -1371,7 +1371,7 @@ public class MessageHistoryServiceImpl } /** - * Returns all the messages exchanged + * Returns all the messages exchanged * in the supplied chat room between the given dates * * @param room The chat room @@ -1387,7 +1387,7 @@ public class MessageHistoryServiceImpl try { // get the readers for this room - HistoryReader reader = + HistoryReader reader = this.getHistoryForMultiChat(null, room).getReader(); // add the progress listeners @@ -1405,12 +1405,12 @@ public class MessageHistoryServiceImpl { logger.error("Could not read history", e); } - + return result; } /** - * Returns all the messages exchanged + * Returns all the messages exchanged * in the supplied chat room between the given dates and having the given * keywords * @@ -1421,7 +1421,7 @@ public class MessageHistoryServiceImpl * @return Collection of MessageReceivedEvents or MessageDeliveredEvents * @throws RuntimeException */ - public Collection findByPeriod(ChatRoom room, + public Collection findByPeriod(ChatRoom room, Date startDate, Date endDate, String[] keywords) throws RuntimeException { @@ -1429,7 +1429,7 @@ public class MessageHistoryServiceImpl } /** - * Returns all the messages exchanged + * Returns all the messages exchanged * in the supplied chat room between the given dates and having the given * keywords * @@ -1449,7 +1449,7 @@ public class MessageHistoryServiceImpl try { // get the readers for this room - HistoryReader reader = + HistoryReader reader = this.getHistoryForMultiChat(null, room).getReader(); // add the progress listeners @@ -1468,12 +1468,12 @@ public class MessageHistoryServiceImpl { logger.error("Could not read history", e); } - + return result; } /** - * Returns all the messages exchanged + * Returns all the messages exchanged * in the supplied room having the given keyword * * @param room The Chat room @@ -1488,7 +1488,7 @@ public class MessageHistoryServiceImpl } /** - * Returns all the messages exchanged + * Returns all the messages exchanged * in the supplied chat room having the given keyword * * @param room The chat room @@ -1497,7 +1497,7 @@ public class MessageHistoryServiceImpl * @return Collection of MessageReceivedEvents or MessageDeliveredEvents * @throws RuntimeException */ - public Collection findByKeyword(ChatRoom room, String keyword, + public Collection findByKeyword(ChatRoom room, String keyword, boolean caseSensitive) throws RuntimeException { @@ -1505,7 +1505,7 @@ public class MessageHistoryServiceImpl try { // get the readers for this room - HistoryReader reader = + HistoryReader reader = this.getHistoryForMultiChat(null, room).getReader(); // add the progress listeners @@ -1529,7 +1529,7 @@ public class MessageHistoryServiceImpl } /** - * Returns all the messages exchanged + * Returns all the messages exchanged * in the supplied chat room having the given keywords * * @param room The chat room @@ -1544,7 +1544,7 @@ public class MessageHistoryServiceImpl } /** - * Returns all the messages exchanged + * Returns all the messages exchanged * in the supplied chat room having the given keywords * * @param room The chat room @@ -1553,7 +1553,7 @@ public class MessageHistoryServiceImpl * @return Collection of MessageReceivedEvents or MessageDeliveredEvents * @throws RuntimeException */ - public Collection findByKeywords(ChatRoom room, String[] keywords, + public Collection findByKeywords(ChatRoom room, String[] keywords, boolean caseSensitive) throws RuntimeException { @@ -1561,7 +1561,7 @@ public class MessageHistoryServiceImpl try { // get the readers for this room - HistoryReader reader = + HistoryReader reader = this.getHistoryForMultiChat(null, room).getReader(); // add the progress listeners @@ -1580,12 +1580,12 @@ public class MessageHistoryServiceImpl { logger.error("Could not read history", e); } - + return result; } /** - * Returns the supplied number of recent messages exchanged + * Returns the supplied number of recent messages exchanged * in the supplied chat room * * @param room The chat room @@ -1597,12 +1597,12 @@ public class MessageHistoryServiceImpl throws RuntimeException { TreeSet result = new TreeSet(new ChatRoomMessageEventComparator()); - + try { // get the readers for this room - HistoryReader reader = - this.getHistoryForMultiChat(null, room).getReader(); + HistoryReader reader = + this.getHistoryForMultiChat(null, room).getReader(); Iterator recs = reader.findLast(count); while (recs.hasNext()) { @@ -1616,7 +1616,7 @@ public class MessageHistoryServiceImpl { logger.error("Could not read history", e); } - + LinkedList resultAsList = new LinkedList(result); int startIndex = resultAsList.size() - count; @@ -1643,7 +1643,7 @@ public class MessageHistoryServiceImpl try { - HistoryReader reader = + HistoryReader reader = this.getHistoryForMultiChat(null, room).getReader(); Iterator recs = reader.findFirstRecordsAfter(date, count); while (recs.hasNext()) @@ -1660,11 +1660,11 @@ public class MessageHistoryServiceImpl } LinkedList resultAsList = new LinkedList(result); - + int toIndex = count; if(toIndex > resultAsList.size()) toIndex = resultAsList.size(); - + return resultAsList.subList(0, toIndex); } @@ -1685,7 +1685,7 @@ public class MessageHistoryServiceImpl try { - HistoryReader reader = + HistoryReader reader = this.getHistoryForMultiChat(null, room).getReader(); Iterator recs = reader.findLastRecordsBefore(date, count); while (recs.hasNext()) @@ -1709,7 +1709,7 @@ public class MessageHistoryServiceImpl return resultAsList.subList(startIndex, resultAsList.size()); } - + public static ResourceManagementService getResources() { if (resourcesService == null) @@ -1719,8 +1719,8 @@ public class MessageHistoryServiceImpl if(serviceReference == null) return null; - - resourcesService = (ResourceManagementService) + + resourcesService = (ResourceManagementService) MessageHistoryActivator.bundleContext .getService(serviceReference); } @@ -1742,7 +1742,7 @@ public class MessageHistoryServiceImpl double accumulatedRatio = 0; double currentProgress = 0; double lastHistoryProgress = 0; - + // used for more precise calculations with double values int raiser = 1000; @@ -1750,12 +1750,12 @@ public class MessageHistoryServiceImpl { this.listener = listener; } - + private void setCurrentValues(HistoryReader currentReader, int allRecords) { this.allRecords = allRecords; this.reader = currentReader; - currentReaderProgressRatio = + currentReaderProgressRatio = (double)currentReader.countRecords()/allRecords * raiser; accumulatedRatio += currentReaderProgressRatio; } @@ -1779,15 +1779,15 @@ public class MessageHistoryServiceImpl */ private int getProgressMapping(ProgressEvent evt) { - double tmpHistoryProgress = + double tmpHistoryProgress = currentReaderProgressRatio * evt.getProgress(); - + currentProgress += tmpHistoryProgress - lastHistoryProgress; - + if(evt.getProgress() == HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE) { lastHistoryProgress = 0; - + // this is the last one and the last event fire the max // there will be looses in currentProgress due to the devision if((int)accumulatedRatio == raiser) @@ -1796,7 +1796,7 @@ public class MessageHistoryServiceImpl } else lastHistoryProgress = tmpHistoryProgress; - + return (int)currentProgress; } @@ -1804,7 +1804,7 @@ public class MessageHistoryServiceImpl * clear the values */ void clear() - { + { allRecords = 0; currentProgress = 0; lastHistoryProgress = 0; @@ -1866,9 +1866,9 @@ public class MessageHistoryServiceImpl return date1.compareTo(date2); } } - + /** - * Used to compare ChatRoomMessageDeliveredEvent + * Used to compare ChatRoomMessageDeliveredEvent * or ChatRoomMessageReceivedEvent * and to be ordered in TreeSet according their timestamp */ @@ -1879,7 +1879,7 @@ public class MessageHistoryServiceImpl { Date date1 = null; Date date2 = null; - + if(o1 instanceof ChatRoomMessageDeliveredEvent) date1 = ((ChatRoomMessageDeliveredEvent)o1).getTimestamp(); else if(o1 instanceof ChatRoomMessageReceivedEvent) @@ -1897,7 +1897,7 @@ public class MessageHistoryServiceImpl return date1.compareTo(date2); } } - + /** * Simple ChatRoomMember implementation. */ @@ -1908,7 +1908,7 @@ public class MessageHistoryServiceImpl private String name; private ChatRoomMemberRole role; - public ChatRoomMemberImpl(String name, ChatRoom chatRoom, + public ChatRoomMemberImpl(String name, ChatRoom chatRoom, ChatRoomMemberRole role) { this.chatRoom = chatRoom; @@ -1941,7 +1941,7 @@ public class MessageHistoryServiceImpl return role; } } - + /** * Handles <tt>PropertyChangeEvent</tt> triggered from the modification of * the isMessageHistoryEnabled property. diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java index a04f23b..ec23e10 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -155,6 +155,14 @@ public class OperationSetBasicTelephonySipImpl private synchronized CallSipImpl createOutgoingCall(Address calleeAddress) throws OperationFailedException { + if(!protocolProvider.isRegistered()) + { + throw new OperationFailedException( + "The protocol provider should be registered " + +"before placing an outgoing call.", + OperationFailedException.PROVIDER_NOT_REGISTERED); + } + // create the invite request Request invite = createInviteRequest(calleeAddress); diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java index 586eb34..8764a6e 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderFactorySipImpl.java @@ -27,7 +27,8 @@ public class ProtocolProviderFactorySipImpl /** * The table that we store our accounts in. */ - private Hashtable registeredAccounts = new Hashtable(); + private Hashtable<AccountID, ServiceRegistration> registeredAccounts + = new Hashtable<AccountID, ServiceRegistration>(); /** * Constructs a new instance of the ProtocolProviderFactorySipImpl. @@ -63,9 +64,9 @@ public class ProtocolProviderFactorySipImpl * @return a copy of the llist containing all accounts currently installed * in the protocol provider. */ - public ArrayList getRegisteredAccounts() + public ArrayList<AccountID> getRegisteredAccounts() { - return new ArrayList(registeredAccounts.keySet()); + return new ArrayList<AccountID>(registeredAccounts.keySet()); } /** @@ -153,7 +154,7 @@ public class ProtocolProviderFactorySipImpl * the modified account. * @param accountProperties a set of protocol (or implementation) specific * properties defining the new account. - * + * * @throws java.lang.NullPointerException if any of the arguments is null. */ public void modifyAccount( ProtocolProviderService protocolProvider, @@ -235,7 +236,7 @@ public class ProtocolProviderFactorySipImpl + ex.getMessage()); } } - + /** * Initializes and creates an account corresponding to the specified * accountProperties and registers the resulting ProtocolProvider in the diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java index 9f5a59b..9562436 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -312,6 +312,9 @@ public class ProtocolProviderServiceSipImpl */ private ProtocolIconSipImpl protocolIcon; + /** + * The presence status set supported by this provider + */ private SipStatusEnum sipStatusEnum; /** @@ -2107,7 +2110,7 @@ public class ProtocolProviderServiceSipImpl * so that it would receives all messages in a transaction initiated by a * <tt>method</tt> request. If any previous processors exist for the same * method, they will be replaced by this one. - * + * * @param method a String representing the SIP method that we're registering * the processor for (e.g. INVITE, REGISTER, or SUBSCRIBE). * @param methodProcessor a <tt>MethodProcessor</tt> implementation that @@ -2143,7 +2146,7 @@ public class ProtocolProviderServiceSipImpl * Unregisters <tt>methodProcessor</tt> from the <tt>methorProcessors</tt> * table so that it won't receive further messages in a transaction * initiated by a <tt>method</tt> request. - * + * * @param method the name of the method whose processor we'd like to * unregister. * @param methodProcessor diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java b/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java index 192536a..832060d 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipActivator.java @@ -10,6 +10,7 @@ import java.util.*; import org.osgi.framework.*; import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.service.gui.*; import net.java.sip.communicator.service.netaddr.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.util.*; @@ -26,15 +27,18 @@ public class SipActivator private Logger logger = Logger.getLogger(SipActivator.class.getName()); private ServiceRegistration sipPpFactoryServReg = null; - static BundleContext bundleContext = null; + static BundleContext bundleContext = null; private static ConfigurationService configurationService = null; private static NetworkAddressManagerService networkAddressManagerService - = null; + = null; private static MediaService mediaService = null; private static VersionService versionService = null; + private static UIService uiService = null; private static ProtocolProviderFactorySipImpl sipProviderFactory = null; + private static UriHandlerSipImpl uriHandler = null; + /** * Called when this bundle is started so the Framework can perform the * bundle-specific activities necessary to start this bundle. @@ -63,6 +67,8 @@ public class SipActivator sipProviderFactory, hashtable); + uriHandler = new UriHandlerSipImpl(sipProviderFactory); + logger.debug("SIP Protocol Provider Factory ... [REGISTERED]"); } @@ -148,7 +154,7 @@ public class SipActivator } return mediaService; } - + /** * Returns a reference to a VersionService implementation currently registered * in the bundle context or null if no such implementation was found. @@ -170,6 +176,26 @@ public class SipActivator } /** + * Returns a reference to the UIService implementation currently registered + * in the bundle context or null if no such implementation was found. + * + * @return a reference to a UIService implementation currently registered + * in the bundle context or null if no such implementation was found. + */ + public static UIService getUIService() + { + if(uiService == null) + { + ServiceReference uiServiceReference + = bundleContext.getServiceReference( + UIService.class.getName()); + uiService = (UIService)bundleContext + .getService(uiServiceReference); + } + return uiService; + } + + /** * Called when this bundle is stopped so the Framework can perform the * bundle-specific activities necessary to stop the bundle. * diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarlessConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarlessConnection.java new file mode 100644 index 0000000..1f7a28b --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarlessConnection.java @@ -0,0 +1,185 @@ +/* + * 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.impl.protocol.sip; + +import java.net.*; +import java.text.*; +import java.util.*; +import java.util.logging.Level; +import javax.sip.*; +import javax.sip.address.*; +import javax.sip.header.*; +import javax.sip.message.*; + +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +/** + * Allows SIP communicator to create SIP accounts without a registrar. We use + * this class as a replacement of the SipRegistrarConnection for accounts that + * do not have a configured registrar. + * + * @author Emil Ivov + */ +public class SipRegistrarlessConnection + extends SipRegistrarConnection +{ + private static final Logger logger = + Logger.getLogger(SipRegistrarlessConnection.class); + + /** + * A reference to the sip provider that created us. + */ + private ProtocolProviderServiceSipImpl sipProvider = null; + + /** + * Keeps our current registration state. + */ + private RegistrationState currentRegistrationState + = RegistrationState.UNREGISTERED; + + /** + * Creates a new instance of this class. + * + * @param sipProviderCallback a reference to the + * ProtocolProviderServiceSipImpl instance that created us. + * + */ + public SipRegistrarlessConnection( + ProtocolProviderServiceSipImpl sipProviderCallback) + { + this.sipProvider = sipProviderCallback; + } + + /** + * Simply sets the state of the connection to REGISTERED without doing + * anything else. + * + * @throws OperationFailedException never thrown + */ + @Override + void register() + throws OperationFailedException + { + setRegistrationState(RegistrationState.REGISTERED, + RegistrationStateChangeEvent.REASON_USER_REQUEST, + null); + } + + /** + * Simply sets the state of the connection to UNREGISTERED without doing + * anything else. + * + * @throws OperationFailedException never thrown. + */ + @Override + public void unregister() throws OperationFailedException + { + setRegistrationState(RegistrationState.UNREGISTERED, + RegistrationStateChangeEvent.REASON_USER_REQUEST, + null); + } + + /** + * Returns the state of this connection. + * + * @return a RegistrationState instance indicating the state of our + * registration with the corresponding registrar. + */ + @Override + public RegistrationState getRegistrationState() + { + return currentRegistrationState; + } + + /** + * Sets our registration state to <tt>newState</tt> and dispatches an event + * through the protocol provider service impl. + * <p> + * @param newState a reference to the RegistrationState that we're currently + * detaining. + * @param reasonCode one of the REASON_XXX error codes specified in + * {@link RegistrationStateChangeEvent}. + * @param reason a reason String further explaining the reasonCode. + */ + @Override + public void setRegistrationState(RegistrationState newState, + int reasonCode, + String reason) + { + if( currentRegistrationState.equals(newState) ) + { + return; + } + + RegistrationState oldState = currentRegistrationState; + this.currentRegistrationState = newState; + + sipProvider.fireRegistrationStateChanged( + oldState, newState, reasonCode, reason); + } + + /** + * Returns the address of this connection's registrar. + * + * @return the InetAddress of our registrar server. + */ + @Override + public InetAddress getRegistrarAddress() + { + try + { + return InetAddress.getByAddress("2001:1890:1112:1::20", + new byte[]{(byte) 20, (byte) 01, (byte) 18, (byte) 90, + (byte) 11, (byte) 11, (byte) 12, (byte) 00, + (byte) 01, (byte) 00, (byte) 00, (byte) 00, + (byte) 00, (byte) 00, (byte) 00, (byte) 20}); + } catch (UnknownHostException ex) + { + logger.error("Failed to generate a dummy registrar addr", ex); + return null; + } + } + + /** + * Returns the listening point that should be used for communication with our + * current registrar. + * + * @return the listening point that should be used for communication with our + * current registrar. + */ + @Override + public ListeningPoint getRegistrarListeningPoint() + { + return sipProvider.getDefaultListeningPoint(); + } + + /** + * Returns a string representation of this connection instance + * instance including information that would permit to distinguish it among + * other sip listeners when reading a log file. + * <p> + * @return a string representation of this operation set. + */ + @Override + public String toString() + { + String className = getClass().getName(); + try + { + className = className.substring(className.lastIndexOf('.') + 1); + } + catch (Exception ex) + { + // we don't want to fail in this method because we've messed up + //something with indexes, so just ignore. + } + return className + "-[dn=" + sipProvider.getOurDisplayName() + +" addr="+sipProvider.getOurSipAddress() + "]"; + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java new file mode 100644 index 0000000..6957842 --- /dev/null +++ b/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java @@ -0,0 +1,524 @@ +/* + * 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.impl.protocol.sip; + +import java.text.*; +import java.util.*; + +import org.osgi.framework.*; + +import net.java.sip.communicator.impl.systray.*; +import net.java.sip.communicator.service.argdelegation.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; + +/** + * The sip implementation of the URI handler. This class handles sip URIs by + * trying to establish a call to them. + * + * @author Emil Ivov + */ +public class UriHandlerSipImpl + implements UriHandler, + ServiceListener + +{ + private static final Logger logger = + Logger.getLogger(UriHandlerSipImpl.class); + + /** + * The protocol provider factory that created us. + */ + private ProtocolProviderFactory protoFactory = null; + + /** + * A reference to the OSGi registration we create with this handler. + */ + private ServiceRegistration ourServiceRegistration = null; + + /** + * The object that we are using to synchronize our service registration. + */ + private Object registrationLock = new Object(); + + /** + * Creates an instance of this uri handler, so that it would start handling + * URIs by passing them to the providers registered by <tt>protoFactory</tt>. + * + * @param parentProvider the provider that created us. + * + * @throws NullPointerException if <tt>protoFactory</tt> is <tt>null</tt>. + */ + protected UriHandlerSipImpl(ProtocolProviderFactory protoFactory) + throws NullPointerException + { + if(protoFactory == null) + { + throw new NullPointerException( + "The ProtocolProviderFactory that a UriHandler is created with " + + " cannot be null."); + } + + this.protoFactory = protoFactory; + + //we listen for service events so that we can disable ourselves in + //case our protocol factory decides to leave. + SipActivator.bundleContext.addServiceListener(this); + + registerHandlerService(); + } + + /** + * Registers this UriHandler with the bundle context so that it could + * start handling URIs + */ + private void registerHandlerService() + { + synchronized(registrationLock) + { + if (ourServiceRegistration != null) + { + // ... we are already registered (this is probably + // happening during startup) + return; + } + + Hashtable<String, String> registrationProperties + = new Hashtable<String, String>(); + + registrationProperties.put(UriHandler.PROTOCOL_PROPERTY, + getProtocol()); + + ourServiceRegistration = SipActivator.bundleContext + .registerService(UriHandler.class.getName(), this, + registrationProperties); + } + + } + + /** + * Unregisters this UriHandler from the bundle context. + */ + private void unregisterHandlerService() + { + synchronized(registrationLock) + { + ourServiceRegistration.unregister(); + ourServiceRegistration = null; + } + } + + /** + * Returns the protocol that this handler is responsible for or "sip" in + * other words. + * + * @return the "sip" string to indicate that this handler is responsible + * for handling "sip" uris. + */ + public String getProtocol() + { + return "sip"; + } + + /** + * Parses the specified URI and creates a call with the currently active + * telephony operation set. + * + * @param uri the SIP URI that we have to call. + */ + public void handleUri(String uri) + { + ProtocolProviderService provider; + try + { + provider = selectHandlingProvider(uri); + } + catch (OperationFailedException exc) + { + // The operation has been canceled by the user. Bail out. + logger.trace("User canceled handling of uri " + uri); + return; + } + + //if provider is null then we need to tell the user to create an account + if(provider == null) + { + showErrorMessage( + "You need to configure at least one " + + "SIP" +" account \n" + +"to be able to call " + uri, + null); + return; + } + + OperationSetBasicTelephony telephonyOpSet + = (OperationSetBasicTelephony) provider + .getOperationSet(OperationSetBasicTelephony.class); + + try + { + telephonyOpSet.createCall(uri); + } + catch (OperationFailedException exc) + { + //make sure that we prompt for registration only if it is really + //required by the provider. + if(exc.getErrorCode() + == OperationFailedException.PROVIDER_NOT_REGISTERED) + { + promptForRegistration(uri, provider); + } + showErrorMessage("Failed to create a call to " + uri, exc); + } + catch (ParseException exc) + { + showErrorMessage( + uri + " does not appear to be a valid SIP address", + exc); + } + } + + /** + * Informs the user that they need to be registered before placing calls + * and asks them whether they would like us to do it for them. + * + * @param uri the uri that the user would like us to call after registering. + * @param provider the provider that we may have to reregister. + */ + private void promptForRegistration(String uri, + ProtocolProviderService provider) + { + int answer = SipActivator.getUIService() + .getPopupDialog().showConfirmPopupDialog( + "You need to be online in order to make a call and your " + + "account is currently offline. Do want to connect now?", + "Account is currently offline", + PopupDialog.YES_NO_OPTION); + + if(answer == PopupDialog.YES_OPTION) + { + new ProtocolRegistrationThread(uri, provider).start(); + } + } + + /** + * The point of implementing a service listener here is so that we would + * only register our own uri handling service and thus only handle URIs + * while the factory is available as an OSGi service. We remove ourselves + * when our factory unregisters its service reference. + * + * @param event the OSGi <tt>ServiceEvent</tt> + */ + public void serviceChanged(ServiceEvent event) + { + Object sourceService = SipActivator.bundleContext + .getService(event.getServiceReference()); + + //ignore anything but our protocol factory. + if( ! (sourceService instanceof ProtocolProviderFactorySipImpl) + || (sourceService != protoFactory)) + { + return; + } + + if(event.getType() == ServiceEvent.REGISTERED) + { + //our factory has just been registered as a service ... + registerHandlerService(); + } + else if(event.getType() == ServiceEvent.UNREGISTERING) + { + //our factory just died - seppuku. + unregisterHandlerService(); + } + else if(event.getType() == ServiceEvent.MODIFIED) + { + //we don't care. + } + } + + /** + * Uses the <tt>UIService</tt> to show an error <tt>message</tt> and log + * and <tt>exception</tt>. + * + * @param message the message that we'd like to show to the user. + * @param exc the exception that we'd like to log + */ + private void showErrorMessage(String message, Exception exc) + { + SipActivator.getUIService().getPopupDialog().showMessagePopupDialog( + message, + "Failed to create call!", + PopupDialog.ERROR_MESSAGE); + logger.error(message, exc); + } + + /** + * We use this class when launching a provider registration by ourselves in + * order to track for provider registration states and retry uri handling, + * once the provider is registered. + * + */ + private class ProtocolRegistrationThread + extends Thread + implements SecurityAuthority, + RegistrationStateChangeListener + { + + private boolean isUserNameEditable = false; + private ProtocolProviderService handlerProvider = null; + + /** + * The URI that we'd need to re-call. + */ + private String uri = null; + + /** + * Configures this thread register our parent provider and re-attempt + * connection to the specified <tt>uri</tt>. + * + * @param uri the uri that we need to handle. + * @param handlerProvider the provider that we are going to make + * register and that we are going to use to handle the <tt>uri</tt>. + */ + public ProtocolRegistrationThread( + String uri, + ProtocolProviderService handlerProvider) + { + super("UriHandlerProviderRegistrationThread:uri=" + uri); + this.uri = uri; + this.handlerProvider = handlerProvider; + } + + /** + * Used to login to the protocol providers + * + * @param realm the realm that the credentials are needed for + * @param userCredentials the values to propose the user by default + * @return The Credentials associated with the speciefied realm + */ + public UserCredentials obtainCredentials( + String realm, + UserCredentials userCredentials) + { + return obtainCredentials( realm, + userCredentials, + SecurityAuthority.AUTHENTICATION_REQUIRED); + } + + + /** + * Used to login to the protocol providers + * + * @param realm the realm that the credentials are needed for + * @param userCredentials the values to propose the user by default + * @param reasonCode the reason for which we're asking for credentials + * @return The Credentials associated with the speciefied realm + */ + public UserCredentials obtainCredentials( + String realm, + UserCredentials userCredentials, + int reasonCode) + { + ExportedWindow loginWindow + = SystrayActivator.getUIService() + .getAuthenticationWindow(handlerProvider, + realm, + userCredentials, + isUserNameEditable); + + loginWindow.setVisible(true); + + return userCredentials; + } + + + /** + * Sets the userNameEditable property, which should indicate to the + * implementations of this interface if the user name could be changed + * by user or not. + * + * @param isUserNameEditable indicates if the user name could be changed + * by user in the implementation of this interface. + */ + public void setUserNameEditable(boolean isUserNameEditable) + { + this.isUserNameEditable = isUserNameEditable; + } + + /** + * Indicates if the user name is currently editable, i.e. could be + * changed by user or not. + * + * @return <tt>true</tt> if the user name could be changed and + * <tt>false</tt> otherwise. + */ + public boolean isUserNameEditable() + { + return isUserNameEditable; + } + + + /** + * Starts the registration process, ads this class as a registration + * listener and then tries to rehandle the uri this thread was initiaded + * with. + */ + @Override + public void run() + { + handlerProvider.addRegistrationStateChangeListener(this); + + try + { + handlerProvider.register(this); + } + catch (OperationFailedException exc) + { + logger.error("Failed to manually register provider."); + logger.warn(exc.getMessage(), exc); + } + } + + + /** + * If the parent provider passes into the registration state, the method + * re-handles the URI that this thread was initiated with. The method + * would only rehandle the uri if the event shows successful + * registration. It would ignore intermediate states such as + * REGISTERING. Disconnection and failure events would simply cause this + * listener to remove itself from the list of registration listeners. + * + * @param evt the <tt>RegistrationStateChangeEvent</tt> that this + * thread was initiated with. + */ + public void registrationStateChanged(RegistrationStateChangeEvent evt) + { + if (evt.getNewState() == RegistrationState.REGISTERED) + { + Thread uriRehandleThread = new Thread() + { + public void run() + { + handleUri(uri); + } + }; + + uriRehandleThread.setName("UriRehandleThread:uri="+uri); + uriRehandleThread.start(); + } + + //we're only interested in a single event so we stop listening + //(unless this was a REGISTERING notification) + if(evt.getNewState() == RegistrationState.REGISTERING) + return; + + handlerProvider.removeRegistrationStateChangeListener(this); + } + } + + /** + * Returns the default provider that we are supposed to handle URIs through + * or null if there aren't any. Depending on the implementation this + * method may require user intervention so make sure you don't rely on + * a quick outcome when calling it. + * + * @param uri the uri that we'd like to handle with the provider that we are + * about to select. + * + * @return the provider that we should handle URIs through. + * + * @throws OperationFailedException with code <tt>OPERATION_CANCELED</tt> + * if the users. + */ + public ProtocolProviderService selectHandlingProvider(String uri) + throws OperationFailedException + { + ArrayList<AccountID> registeredAccounts + = protoFactory.getRegisteredAccounts(); + + //if we don't have any providers - return null. + if(registeredAccounts.size() == 0) + { + return null; + } + + + //if we only have one provider - select it + if(registeredAccounts.size() == 1) + { + ServiceReference providerReference + = protoFactory.getProviderForAccount(registeredAccounts.get(0)); + + ProtocolProviderService provider = (ProtocolProviderService) + SipActivator.getBundleContext().getService(providerReference); + + return provider; + } + + //otherwise - ask the user. + ArrayList<ProviderComboBoxEntry> providers + = new ArrayList<ProviderComboBoxEntry>(); + for (AccountID accountID : registeredAccounts) + { + ServiceReference providerReference + = protoFactory.getProviderForAccount(accountID); + + ProtocolProviderService provider = (ProtocolProviderService) + SipActivator.getBundleContext().getService(providerReference); + + providers.add( new ProviderComboBoxEntry( provider ) ); + } + + Object result = SipActivator.getUIService().getPopupDialog() + .showInputPopupDialog( + "Please select the account that you would like \n" + + "to use to call " + + uri, + "Account Selection", + PopupDialog.OK_CANCEL_OPTION, + providers.toArray(), + providers.get(0)); + + if( result == null) + { + throw new OperationFailedException( + "Operation cancelled", + OperationFailedException.OPERATION_CANCELED); + } + + return ((ProviderComboBoxEntry)result).provider; + } + + /** + * A class that we use to wrap providers before showing them to the user + * through a selection popup dialog from the UIService. + */ + private class ProviderComboBoxEntry + { + public ProtocolProviderService provider; + + public ProviderComboBoxEntry(ProtocolProviderService provider) + { + this.provider = provider; + } + + /** + * Returns a human readable <tt>String</tt> representing the + * provider encapsulated by this class. + * + * @return a human readable string representing the provider. + */ + @Override + public String toString() + { + return provider.getAccountID().getAccountAddress(); + } + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf index 8408124..c0cefad 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf +++ b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf @@ -8,6 +8,8 @@ Import-Package: org.osgi.framework, net.java.sip.communicator.service.configuration, net.java.sip.communicator.util, net.java.sip.communicator.util.xml, + net.java.sip.communicator.service.argdelegation, + net.java.sip.communicator.service.gui, net.java.sip.communicator.service.configuration.event, net.java.sip.communicator.service.resources, net.java.sip.communicator.service.protocol, diff --git a/src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java b/src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java index 436d57a..100629d 100644 --- a/src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java +++ b/src/net/java/sip/communicator/impl/systray/jdic/ProviderRegistration.java @@ -19,19 +19,17 @@ import net.java.sip.communicator.util.*; * continue its execution during this operation. * * @author Nicolas Chamouard + * @author Emil Ivov */ public class ProviderRegistration extends Thread - implements SecurityAuthority { /** * The protocol provider to whom we want to register */ private ProtocolProviderService protocolProvider; - private boolean isUserNameEditable = false; - /** * The logger for this class. */ @@ -53,8 +51,10 @@ public class ProviderRegistration */ public void run() { - try { - protocolProvider.register(this); + try + { + protocolProvider.register(SystrayActivator.getUIService() + .getDefaultSecurityAuthority(protocolProvider)); } catch (OperationFailedException ex) { @@ -86,69 +86,4 @@ public class ProviderRegistration } } } - - /** - * Used to login to the protocol providers - * - * @param realm the realm that the credentials are needed for - * @param userCredentials the values to propose the user by default - * @param reasonCode the reason for which we're asking for credentials - * @return The Credentials associated with the speciefied realm - */ - public UserCredentials obtainCredentials( - String realm, - UserCredentials userCredentials, - int reasonCode) - { - ExportedWindow loginWindow - = SystrayActivator.getUIService() - .getAuthenticationWindow(protocolProvider, - realm, - userCredentials, - isUserNameEditable); - - loginWindow.setVisible(true); - - return userCredentials; - } - - /** - * Used to login to the protocol providers - * - * @param realm the realm that the credentials are needed for - * @param userCredentials the values to propose the user by default - * @return The Credentials associated with the speciefied realm - */ - public UserCredentials obtainCredentials( - String realm, - UserCredentials userCredentials) - { - return obtainCredentials( realm, - userCredentials, - SecurityAuthority.AUTHENTICATION_REQUIRED); - } - /** - * Sets the userNameEditable property, which should indicate to the - * implementations of this interface if the user name could be changed by - * user or not. - * - * @param isUserNameEditable indicates if the user name could be changed by - * user in the implementation of this interface. - */ - public void setUserNameEditable(boolean isUserNameEditable) - { - this.isUserNameEditable = isUserNameEditable; - } - - /** - * Indicates if the user name is currently editable, i.e. could be changed - * by user or not. - * - * @return <code>true</code> if the user name could be changed, - * <code>false</code> - otherwise. - */ - public boolean isUserNameEditable() - { - return isUserNameEditable; - } } diff --git a/src/net/java/sip/communicator/launcher/SIPCommunicator.java b/src/net/java/sip/communicator/launcher/SIPCommunicator.java index ca35e99..e3d65fa 100644 --- a/src/net/java/sip/communicator/launcher/SIPCommunicator.java +++ b/src/net/java/sip/communicator/launcher/SIPCommunicator.java @@ -9,25 +9,28 @@ package net.java.sip.communicator.launcher; import java.awt.*; import java.io.*; +import net.java.sip.communicator.util.launchutils.*; + import org.apache.felix.main.*; /** * Starts the SIP Communicator. - * + * * @author Yana Stamcheva * @author Lubomir Marinov + * @author Emil Ivov */ public class SIPCommunicator { - private static final String PNAME_SC_HOME_DIR_LOCATION = + public static final String PNAME_SC_HOME_DIR_LOCATION = "net.java.sip.communicator.SC_HOME_DIR_LOCATION"; - private static final String PNAME_SC_HOME_DIR_NAME = + public static final String PNAME_SC_HOME_DIR_NAME = "net.java.sip.communicator.SC_HOME_DIR_NAME"; /** * Starts the SIP Communicator. - * + * * @param args */ public static void main(String[] args) @@ -68,6 +71,51 @@ public class SIPCommunicator return; } + //first - pass the arguments to our arg handler + LaunchArgHandler argHandler = LaunchArgHandler.getInstance(); + int argHandlerRes = argHandler.handleArgs(args); + + if ( argHandlerRes == LaunchArgHandler.ACTION_EXIT + || argHandlerRes == LaunchArgHandler.ACTION_ERROR) + { + System.exit(argHandler.getErrorCode()); + } + + //lock our config di so that we would only have a single instance of + //sip communicator, no matter how many times we start it (use mainly + //for handling sip: uris after starting the application) + if ( argHandlerRes != LaunchArgHandler.ACTION_CONTINUE_MULTIINSTANCE ) + { + SipCommunicatorLock lock = new SipCommunicatorLock(); + + int lockResult = lock.tryLock(args); + + if( lockResult == SipCommunicatorLock.LOCK_ERROR ) + { + System.err.print("Failed to lock SIP Communicator's " + +"configuration directory.\n" + +"Try launching with the --multiple param."); + System.exit(SipCommunicatorLock.LOCK_ERROR); + + } + else if(lockResult == SipCommunicatorLock.ALREADY_STARTED) + { + System.err.print( + "SIP Communicator is already running and will " + +"handle your parameters (if any).\n" + +"Launch with the --multiple param to override this " + +"behaviour."); + + //we exit with succes because for the user that's what it is. + System.exit(SipCommunicatorLock.SUCCESS); + } + else if(lockResult == SipCommunicatorLock.SUCCESS) + { + //Successfully locked, continue as normal. + } + } + + //there was no error, continue; Main.main(args); } @@ -75,13 +123,12 @@ public class SIPCommunicator * Sets the system properties net.java.sip.communicator.SC_HOME_DIR_LOCATION * and net.java.sip.communicator.SC_HOME_DIR_NAME (if they aren't already * set) in accord with the OS conventions specified by the name of the OS. - * + * * @param osName the name of the OS according to which the SC_HOME_DIR_* * properties are to be set */ private static void setScHomeDir(String osName) { - /* * Though we'll be setting the SC_HOME_DIR_* property values depending * on the OS running the application, we have to make sure we are diff --git a/src/net/java/sip/communicator/service/argdelegation/UriHandler.java b/src/net/java/sip/communicator/service/argdelegation/UriHandler.java new file mode 100644 index 0000000..d533dc1 --- /dev/null +++ b/src/net/java/sip/communicator/service/argdelegation/UriHandler.java @@ -0,0 +1,35 @@ +/* + * 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.argdelegation; + +/** + * This interface is meant to be implemented by all bundles that wish to handle + * URIs passed as invocation arguments. + * + * @author Emil Ivov <emcho at sip-communicator.org> + */ +public interface UriHandler +{ + /** + * The name of the property that we use in the service registration + * properties to store a protocol name when registering <tt>UriHandler</tt>s + */ + public static final String PROTOCOL_PROPERTY = "ProtocolName"; + + /** + * Returns the protocol that this handler is responsible for. + */ + public String getProtocol(); + + /** + * Handles/opens the URI. + * + * @param uri the URI that the handler has to open. + */ + public void handleUri(String uri); +} diff --git a/src/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf b/src/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf new file mode 100644 index 0000000..1e97f1a --- /dev/null +++ b/src/net/java/sip/communicator/service/argdelegation/argdelegation.manifest.mf @@ -0,0 +1,5 @@ +Bundle-Name: Argument Delegation Service +Bundle-Description: A service that allows bundles to register as handlers for specific command line arguments +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +Export-Package: net.java.sip.communicator.service.argdelegation diff --git a/src/net/java/sip/communicator/service/gui/UIService.java b/src/net/java/sip/communicator/service/gui/UIService.java index 40b74cc..b8ceb26 100644 --- a/src/net/java/sip/communicator/service/gui/UIService.java +++ b/src/net/java/sip/communicator/service/gui/UIService.java @@ -23,22 +23,22 @@ import net.java.sip.communicator.service.protocol.*; * The <tt>UIService</tt> provides also methods that would allow to other * modules to control the visibility, size and position of the main application * window. Some of these methods are: setVisible, minimize, maximize, resize, - * move, etc. + * move, etc. * <p> * A way to show different types of simple windows is provided to allow other * modules to show different simple messages, like warning or error messages. - * In order to show a simple warning message, a module should invoke the + * In order to show a simple warning message, a module should invoke the * getPopupDialog method and then one of the showXXX methods, which corresponds - * best to the required dialog. + * best to the required dialog. * <p> * Certain components within the GUI, like "AddContact" window for example, * could be also shown from outside the ui. To make one of * these component exportable, the <tt>UIService</tt> implementation should attach * to it an <tt>WindowID</tt>. A window then could be shown, by invoking - * <code>getExportedWindow(WindowID)</code> and then + * <code>getExportedWindow(WindowID)</code> and then * <code>show</code>. The <tt>WindowID</tt> above should be obtained from * <code>getSupportedExportedWindows</code>. - * + * * @author Yana Stamcheva */ public interface UIService @@ -47,52 +47,52 @@ public interface UIService * Returns TRUE if the application is visible and FALSE otherwise. * This method is meant to be used by the systray service in order to * detect the visibility of the application. - * + * * @return <code>true</code> if the application is visible and * <code>false</code> otherwise. - * + * * @see #setVisible(boolean) */ public boolean isVisible(); - + /** * Shows or hides the main application window depending on the value of * parameter <code>visible</code>. Meant to be used by the systray when it * needs to show or hide the application. - * + * * @param visible if <code>true</code>, shows the main application window; * otherwise, hides the main application window. - * + * * @see #isVisible() */ public void setVisible(boolean visible); - + /** * Minimizes the main application window. */ public void minimize(); - + /** * Mawimizes the main application window. */ public void maximize(); - + /** * Restores the main application window. */ public void restore(); - + /** * Resizes the main application window with the given width and height. - * + * * @param width The new width. * @param height The new height. */ public void resize(int width, int height); - + /** * Moves the main application window to the given coordinates. - * + * * @param x The x coordinate. * @param y The y coordinate. */ @@ -103,29 +103,29 @@ public interface UIService * application by simply closing the main application window (by clicking * the X button or pressing Alt-F4). When set to FALSE the main application * window will be only hidden. - * + * * @param exitOnClose When TRUE, the user could exit the * application by simply closing the main application window (by clicking * the X button or pressing Alt-F4). When set to FALSE the main application * window will be only hidden. */ public void setExitOnMainWindowClose(boolean exitOnClose); - + /** * Returns TRUE if the application could be exited by closing the main * application window, otherwise returns FALSE. - * + * * @return Returns TRUE if the application could be exited by closing the * main application window, otherwise returns FALSE */ public boolean getExitOnMainWindowClose(); - + /** * Returns an exported window given by the <tt>WindowID</tt>. * This could be for example the "Add contact" window or any other window * within the application. The <tt>windowID</tt> should be one of the * WINDOW_XXX obtained by the <tt>getSupportedExportedWindows</tt> method. - * + * * @param windowID One of the WINDOW_XXX WindowID-s. * @throws IllegalArgumentException if the specified <tt>windowID</tt> * is not recognized by the implementation (note that implementations @@ -135,20 +135,20 @@ public interface UIService */ public ExportedWindow getExportedWindow(WindowID windowID) throws IllegalArgumentException; - + /** * Returns a configurable popup dialog, that could be used to show either * a warning message, error message, information message, etc. or to prompt * user for simple one field input or to question the user. - * + * * @return a <code>PopupDialog</code>. * @see PopupDialog */ public PopupDialog getPopupDialog(); - + /** * Returns the <tt>Chat</tt> corresponding to the given <tt>Contact</tt>. - * + * * @param contact the <tt>Contact</tt> for which the searched chat is about. * @return the <tt>Chat</tt> corresponding to the given <tt>Contact</tt>. */ @@ -156,7 +156,7 @@ public interface UIService /** * Returns the <tt>Chat</tt> corresponding to the given <tt>ChatRoom</tt>. - * + * * @param chatRoom the <tt>ChatRoom</tt> for which the searched chat is * about. * @return the <tt>Chat</tt> corresponding to the given <tt>ChatRoom</tt>. @@ -165,18 +165,18 @@ public interface UIService /** * Returns the selected <tt>Chat</tt>. - * + * * @return the selected <tt>Chat</tt>. */ public Chat getCurrentChat(); - + /** * Returns an <tt>ExportableComponent</tt> that corresponds to an * authentication window for the given protocol provider and user * inromation. Initially this method is meant to be used by the * <tt>SystrayService</tt> in order to show a login window when user tries * to connect using the systray menu. - * + * * @param protocolProvider the <tt>ProtocolProviderService</tt> for which * the authentication window is about. * @param realm the realm @@ -194,6 +194,22 @@ public interface UIService boolean isUserNameEditable); /** + * Returns a default implementation of the <tt>SecurityAuthority</tt> + * interface that can be used by non-UI components that would like to launch + * the registration process for a protocol provider. Initially this method + * was meant for use by the systray bundle and the protocol URI handlers. + * + * @param protocolProvider the <tt>ProtocolProviderService</tt> for which + * the authentication window is about. + * + * @return a default implementation of the <tt>SecurityAuthority</tt> + * interface that can be used by non-UI components that would like to launch + * the registration process for a protocol provider. + */ + public SecurityAuthority getDefaultSecurityAuthority( + ProtocolProviderService protocolProvider); + + /** * Returns an iterator over a set of windowID-s. Each <tt>WindowID</tt> * points to a window in the current UI implementation. Each * <tt>WindowID</tt> in the set is one of the constants in the @@ -201,25 +217,25 @@ public interface UIService * bundles that would like to have access to some windows in the gui * - for example the "Add contact" window, the "Settings" window, the * "Chat window", etc. - * - * @return Iterator An iterator to a set containing WindowID-s + * + * @return Iterator An iterator to a set containing WindowID-s * representing all exported windows supported by the current UI * implementation. */ public Iterator getSupportedExportedWindows(); - + /** * Chechks if a window with the given <tt>WindowID</tt> is contained in the * current UI implementation. - * + * * @param windowID one of the <tt>WindowID</tt>-s, defined in the - * <tt>ExportedWindow</tt> interface. + * <tt>ExportedWindow</tt> interface. * @return <code>true</code> if the component with the given * <tt>WindowID</tt> is contained in the current UI implementation, * <code>false</code> otherwise. */ public boolean isExportedWindowSupported(WindowID windowID); - + /** * Returns the <tt>WizardContainer</tt> for the current * UIService implementation. The <tt>WizardContainer</tt> @@ -228,7 +244,7 @@ public interface UIService * s. Each of these wizards is made for a given protocol and should provide * a sequence of user interface forms through which the user could * registrate a new account. - * + * * @return Returns the <tt>AccountRegistrationWizardContainer</tt> for the * current UIService implementation. */ @@ -238,12 +254,12 @@ public interface UIService * Returns an iterator over a set containing containerID-s pointing to * containers supported by the current UI implementation. Each containerID * in the set is one of the CONTAINER_XXX constants. The method is meant to - * be used by plugins or bundles that would like to add components to the + * be used by plugins or bundles that would like to add components to the * user interface. Before adding any component they should use this method * to obtain all possible places, which could contain external components, * like different menus, toolbars, etc. - * - * @return Iterator An iterator to a set containing containerID-s + * + * @return Iterator An iterator to a set containing containerID-s * representing all containers supported by the current UI implementation. */ public Iterator getSupportedContainers(); @@ -251,9 +267,9 @@ public interface UIService /** * Chechks if the container with the given <tt>Container</tt> is supported * from the current UI implementation. - * - * @param containderID One of the CONTAINER_XXX Container-s. - * @return <code>true</code> if the contaner with the given + * + * @param containderID One of the CONTAINER_XXX Container-s. + * @return <code>true</code> if the contaner with the given * <tt>Container</tt> is supported from the current UI implementation, * <code>false</code> otherwise. */ diff --git a/src/net/java/sip/communicator/service/protocol/OperationFailedException.java b/src/net/java/sip/communicator/service/protocol/OperationFailedException.java index 19b1322..d877497 100644 --- a/src/net/java/sip/communicator/service/protocol/OperationFailedException.java +++ b/src/net/java/sip/communicator/service/protocol/OperationFailedException.java @@ -122,10 +122,15 @@ public class OperationFailedException public static final int AUTHENTICATION_CANCELED = 15; /** + * Indicates that the operation has been canceled by the user. + */ + public static final int OPERATION_CANCELED = 16; + + /** * The error code of the exception */ private int errorCode = GENERAL_ERROR; - + /** * Creates an exception with the specified error message and error code. * @param message A message containing details on the error that caused the diff --git a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java index 97410fb..3501cd5 100644 --- a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java +++ b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java @@ -133,12 +133,12 @@ public abstract class ProtocolProviderFactory * resource property. */ public static final String RESOURCE = "RESOURCE"; - + /** * The name of the property under which we store resource priority. */ public static final String RESOURCE_PRIORITY = "RESOURCE_PRIORITY"; - + /** * The name of the property under which we store the boolean value * indicating if the user name should be automatically changed if the @@ -146,44 +146,44 @@ public abstract class ProtocolProviderFactory * implementations. */ public static final String AUTO_CHANGE_USER_NAME = "AUTO_CHANGE_USER_NAME"; - + /** * The name of the property under which we store the boolean value * indicating if a password is required. Initially this property is meant to * be used by IRC implementations. */ public static final String NO_PASSWORD_REQUIRED = "NO_PASSWORD_REQUIRED"; - + /** * The name of the property under which we store if the presence is enabled. */ public static final String IS_PRESENCE_ENABLED = "IS_PRESENCE_ENABLED"; - + /** * The name of the property under which we store if the p2p mode for SIMPLE * should be forced. */ public static final String FORCE_P2P_MODE = "FORCE_P2P_MODE"; - + /** * The name of the property under which we store the offline contact polling * period for SIMPLE. */ - public static final String POLLING_PERIOD = "POLLING_PERIOD"; - + public static final String POLLING_PERIOD = "POLLING_PERIOD"; + /** * The name of the property under which we store the chosen default * subscription expiration value for SIMPLE. */ public static final String SUBSCRIPTION_EXPIRATION = "SUBSCRIPTION_EXPIRATION"; - + /** * Indicates if the server address has been validated. */ public static final String SERVER_ADDRESS_VALIDATED = "SERVER_ADDRESS_VALIDATED"; - + /** * Indicates if the proxy address has been validated. */ @@ -240,7 +240,7 @@ public abstract class ProtocolProviderFactory * the modified account. * @param accountProperties a set of protocol (or implementation) specific * properties defining the new account. - * + * * @throws java.lang.NullPointerException if any of the arguments is null. */ public abstract void modifyAccount( @@ -254,7 +254,7 @@ public abstract class ProtocolProviderFactory * @return a copy of the list containing the <tt>AccountID</tt>s of all * accounts currently registered in this protocol provider. */ - public abstract ArrayList getRegisteredAccounts(); + public abstract ArrayList<AccountID> getRegisteredAccounts(); /** * Returns the ServiceReference for the protocol provider corresponding to @@ -531,7 +531,7 @@ public abstract class ProtocolProviderFactory Base64.decode(storedPropertyValue)); } } - + if(storedPropertyValue != null) accountProperties.put(propertyName, storedPropertyValue); } @@ -558,7 +558,7 @@ public abstract class ProtocolProviderFactory * * @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); diff --git a/src/net/java/sip/communicator/util/launchutils/LaunchArgHandler.java b/src/net/java/sip/communicator/util/launchutils/LaunchArgHandler.java new file mode 100644 index 0000000..fe08c2c --- /dev/null +++ b/src/net/java/sip/communicator/util/launchutils/LaunchArgHandler.java @@ -0,0 +1,404 @@ +/* + * 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.util.launchutils; + +import java.util.*; +import net.java.sip.communicator.util.*; +import java.io.*; + +/** + * The <tt>LauncherArgHandler</tt> class handles invocation arguments that have + * been passed to us when running SIP Communicator. The class supports a fixed + * set of options and also allows for registration of delegates. + * + * @author Emil Ivov <emcho at sip-communicator.org> + */ +public class LaunchArgHandler +{ + private static final Logger logger = + Logger.getLogger(LaunchArgHandler.class); + + /** + * The name of the property that contains the location of the SC + * configuration directory. + */ + private static final String PNAME_SC_HOME_DIR_LOCATION = + "net.java.sip.communicator.SC_HOME_DIR_LOCATION"; + + /** + * The name of the property that contains the name of the SC configuration + * directory. + */ + private static final String PNAME_SC_HOME_DIR_NAME = + "net.java.sip.communicator.SC_HOME_DIR_NAME"; + + /** + * Returned by the <tt>handleArgs</tt> methods when the arguments that have + * been parsed do not require for SIP Communicator to be started and the + * Launcher is supposed to exit. That could happen when "SIP Communicator" + * is launched with a --version argument for example or when trying to + * run the application after an instance was already launched. + */ + public static final int ACTION_EXIT = 0; + + /** + * Returned by the <tt>handleArgs</tt> methods when all arguments have been + * parsed and the SIP Communicator launch can continue. + */ + public static final int ACTION_CONTINUE = 1; + + /** + * Returned by the <tt>handleArgs</tt> method when parsing the arguments + * has failed or if no arguments were passed and an instance of SC was + * already launched. If this is the code returned by handleArgs, then the + * <tt>getErrorCode</tt> method would return an error code indicating what + * the error was. + */ + public static final int ACTION_ERROR = 2; + + /** + * Returned by the <tt>handleArgs</tt> methods when all arguments have been + * successfully parsed and one of them indicates that the user has requested + * a multi instance launch. + */ + public static final int ACTION_CONTINUE_MULTIINSTANCE = 3; + + /** + * The error code returned when we couldn't parse one of the options. + */ + public static final int ERROR_CODE_UNKNOWN_ARG = 1; + + /** + * The error code returned when we try to launch SIP Communicator while + * there is already a running instance and there were no arguments that we + * forward to that instance. + */ + public static final int ERROR_CODE_ALREADY_STARTED = 2; + + /** + * The error code that we return when we fail to create a directory that has + * been specified with the -c|--config option. + */ + public static final int ERROR_CODE_CREATE_DIR_FAILED = 3; + + /** + * The property name containing the name of the application + * (e.g. SIP Communicator) + */ + private static final String PNAME_APPLICATION_NAME = "APPLICATION_NAME"; + + /** + * The property name containing the current version. + */ + private static final String PNAME_VERSION = "APPLICATION_VERSION"; + + /** + * The name of the file containing version properties for use with the + * argument handler. + */ + private static final String VERSION_PROPERTIES = "version.properties"; + + /** + * The errorCode identifying the error that occurred last time + * <tt>handleArgs</tt> was called. + */ + private int errorCode = 0; + + /** + * A reference to the instance of the + */ + private UriArgManager uriArgManager = new UriArgManager(); + + /** + * The singleton instance of this handler. + */ + private static LaunchArgHandler argHandler = null; + + private Properties versionProperties = new Properties(); + + /** + * Creates the sole instance of this class; + */ + private LaunchArgHandler() + { + try + { + versionProperties.load( + getClass().getResourceAsStream(VERSION_PROPERTIES)); + } + catch(IOException exc) + { + //no need to worry the user, so only print if we're in FINEST + logger.trace("Couldn't open version.properties"); + } + } + + /** + * Creates a singleton instance of the LauncherArgHandler if necessary and + * returns a reference to it. + * + * @return the singleton instance of the LauncherArgHandler. + */ + public static LaunchArgHandler getInstance() + { + if(argHandler == null) + { + argHandler = new LaunchArgHandler(); + } + + return argHandler; + } + + /** + * Does the actual argument handling. + * + * @param args the arguments the way we have received them from the main() + * method. + * + * @return one of the ACTION_XXX fields defined here, intended to indicate + * to the caller they action that they are supposed as a result of the arg + * handling. + */ + public int handleArgs(String[] args) + { + int returnAction = ACTION_CONTINUE; + + for(int i = 0; i < args.length; i++) + { + logger.trace("handling arg " + i); + + if (args[i].equals("--version") || args[i].equals("-v")) + { + handleVersionArg(); + //we're supposed to exit after printing version info + returnAction = ACTION_EXIT; + break; + } + else if (args[i].equals("--help") || args[i].equals("-h")) + { + handleHelpArg(); + //we're supposed to exit after printing the help message + returnAction = ACTION_EXIT; + break; + } + else if (args[i].equals("--debug") || args[i].equals("-d")) + { + handleDebugArg(args[i]); + continue; + } + else if (args[i].startsWith("--config=")) + { + returnAction = handleConfigArg(args[i]); + + if(returnAction == ACTION_ERROR) + break; + else + continue; + } + else if (args[i].equals("-c")) + { + //make sure we have at least one more argument left. + if( i == args.length - 1) + { + System.out.println("The \"-c\" option expects a directory parameter."); + returnAction = ACTION_ERROR; + break; + } + handleConfigArg(args[++i]); + continue; + } + else if (args[i].equals("--multiple") || args[i].equals("-m")) + { + handleMultipleArg(args[i]); + continue; + } + //if this is the last arg and it's not an option then it's probably + //an URI + else if ( i == args.length - 1 + && !args[i].startsWith("-")) + { + handleUri(args[i]); + } + else + { + handleUnknownArg(args[i]); + + errorCode = ERROR_CODE_UNKNOWN_ARG; + returnAction = ACTION_ERROR; + break; + } + } + + return returnAction; + } + + /** + * Passes <tt>uriArg</tt> to our uri manager for handling. + * + * @param arg the uri that we'd like to pass to + */ + private void handleUri(String uri) + { + logger.trace("Handling uri "+ uri); + uriArgManager.handleUri(uri); + } + + /** + * Instructs SIP Communicator to print logging messages to the console. + */ + private void handleDebugArg(String arg) + { + System.out.println("Option " + arg + " is not yet implemented!"); + } + + /** + * Instructs SIP Communicator to allow for more than a single running + * instance. + */ + private void handleMultipleArg(String arg) + { + System.out.println("Option " + arg + " is not yet implemented!"); + } + + /** + * Instructs SIP Communicator to allow for more than a single running + * instance. + * + * @return either ACTION_ERROR or ACTION_CONTINUE depending on whether or + * not parsing the option went fine. + */ + private int handleConfigArg(String configArg) + { + if (configArg.startsWith("--config=")) + { + configArg = configArg.substring("--config=".length()); + + } + + File configDir = new File(configArg); + + configDir.mkdirs(); + + if(!configDir.isDirectory()) + { + System.out.println("Failed to create directory " + configArg); + errorCode = ERROR_CODE_CREATE_DIR_FAILED; + return ACTION_ERROR; + } + + System.setProperty(PNAME_SC_HOME_DIR_LOCATION, configDir.getParent()); + System.setProperty(PNAME_SC_HOME_DIR_NAME, configDir.getName()); + + return ACTION_CONTINUE; + } + + /** + * Prints the name and the version of this application. This method uses the + * version.properties file which is created by ant during the build process. + * If this file does not exist the method would print a default name and + * version string. + */ + private void handleVersionArg() + { + String name = getApplicationName(); + String version = getVersion(); + + if (name == null || name.trim().length() == 0) + { + name = "SIP Communicator"; + } + + if (version == null || version.trim().length() == 0) + { + version = "build.by.SVN"; + } + System.out.println(name + " " + version); + + } + + /** + * Returns the version of the SIP Communicator instance that we are + * currently running. + * + * @return a String containing the version of the SC instance we are + * currently running. + */ + private String getVersion() + { + String version = versionProperties.getProperty(PNAME_VERSION); + + return version == null + ? "build.by.SVN" + : version; + } + + /** + * Returns the name of the application. That should be SIP Communicator + * most of the time but who knows .. + * + * @return the name of the application (i.e. SIP Communicator until we + * change our name some day.) + */ + private String getApplicationName() + { + String name = versionProperties.getProperty(PNAME_APPLICATION_NAME); + + return name == null + ? "SIP Communicator" + : name; + } + /** + * Prints an error message and then prints the help message. + */ + public void handleUnknownArg(String arg) + { + System.out.println("Unknown argument: " + arg); + handleHelpArg(); + } + + /** + * Prints a help message containing usage instructions and descriptions of + * all options currently supported by SIP Communicator. + */ + public void handleHelpArg() + { + handleVersionArg(); + + System.out.println("Usage: sip-communicator [OPTIONS] [uri-to-call]"); + System.out.println(""); + System.out.println(" -c, --config=DIR use DIR for config files"); + System.out.println(" -d, --debug print debugging messages to stdout"); + System.out.println(" -h, --help display this help message and exit"); + System.out.println(" -m, --multiple do not ensure single instance"); + System.out.println(" -v, --version display the current version and exit"); + } + + /** + * Returns an error code that could help identify an error when + * <tt>handleArgs</tt> returns ACTION_ERROR or 0 if everything went fine. + * + * @return an error code that could help identify an error when + * <tt>handleArgs</tt> returns ACTION_ERROR or 0 if everything went fine. + */ + public int getErrorCode() + { + return errorCode; + } + + /** + * Sets the <tt>delegationPeer</tt> that would be handling all URIs passed + * as command line arguments to SIP Communicator. + * + * @param delegationPeer the <tt>delegationPeer</tt> that should handle URIs + * or <tt>null</tt> if we'd like to unset a previously set peer. + */ + public void setDelegationPeer(UriDelegationPeer delegationPeer) + { + this.uriArgManager.setDelegationPeer(delegationPeer); + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/util/launchutils/SipCommunicatorLock.java b/src/net/java/sip/communicator/util/launchutils/SipCommunicatorLock.java new file mode 100644 index 0000000..51198e0 --- /dev/null +++ b/src/net/java/sip/communicator/util/launchutils/SipCommunicatorLock.java @@ -0,0 +1,784 @@ +/* + * 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.util.launchutils; + +import java.io.*; +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.launcher.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.launchutils.*; + +/** + * This class is used to prevent from running multiple instances of SIP + * Communicator. The class binds a socket somewhere on the localhost domain and + * records its socket address in the SIP Communicator configuration directory. + * + * All following instances of SIP Communicator (and hence this class) will look + * for this record in the configuration directory and try to connect to the + * original instance through the socket address in there. + * + * @author Emil Ivov + */ +public class SipCommunicatorLock extends Thread +{ + private static final Logger logger = Logger + .getLogger(SipCommunicatorLock.class); + + /** + * Indicates that something went wrong. More information will probably be + * available in the console ... if anyone cares at all. + */ + public static final int LOCK_ERROR = 300; + + /** + * Returned by the soft start method to indicate that we have successfully + * started and locked the configuration directory. + */ + public static final int SUCCESS = 0; + + /** + * Returned by the soft start method to indicate that an instance of SIP + * Communicator has been already started and we should exit. This return + * code also indicates that all arguments were passed to that new instance. + */ + public static final int ALREADY_STARTED = 301; + + /** + * The name of the file that we use to store the address and port that this + * lock is bound on. + */ + private static final String LOCK_FILE_NAME = ".lock"; + + /** + * The name of the property that we use to store the address that we bind on + * in this class. + */ + private static final String PNAME_LOCK_ADDRESS = "lockAddress"; + + /** + * The name of the property that we use to store the address that we bind on + * in this class. + */ + private static final String PNAME_LOCK_PORT = "lockPort"; + + /** + * The header preceding each of the arguments that we toss around between + * instances of SIP Communicator + */ + private static final String ARGUMENT = "Argument"; + + /** + * The name of the header that contains the number of arguments that we send + * from one instance to another. + */ + private static final String ARG_COUNT = "Arg-Count"; + + /** + * The name of the header that contains any error messages resulting from + * remote argument handling. + */ + private static final String ERROR_ARG = "ERROR"; + + /** + * The carriage return, line feed sequence (\r\n). + */ + private static final String CRLF = "\r\n"; + + /** + * The number of milliseconds that we should wait for a remote SC instance + * to come back to us. + */ + private long LOCK_COMMUNICATION_DELAY = 50; + + /** + * The socket that we use for cross instance lock and communication. + */ + private ServerSocket instanceServerSocket = null; + + /** + * Tries to lock the configuration directory. If lock-ing is not possible + * because a previous instance is already running, then it transmits the + * list of args to that running instance. + * <p> + * There are three possible outcomes of this method. 1. We lock + * successfully; 2. We fail to lock because another instance of SIP + * Communicator is already running; 3. We fail to lock for some unknown + * error. Each of these cases is represented by an error code returned as a + * result. + * + * @param args + * the array of arguments that we are to submit in case an + * instance of SIP Communicator has already been started. + * + * @return an error or success code indicating the outcome of the lock + * operation. + */ + public int tryLock(String[] args) + { + // first check whether we have a file. + File lockFile = getLockFile(); + + if (lockFile.exists()) + { + InetSocketAddress lockAddress = readLockFile(lockFile); + + if (lockAddress != null) + { + // we have a valid lockAddress and hence possibly an already + // running instance of SC. Try to communicate with it. + if (interInstanceConnect(lockAddress, args) == SUCCESS) + { + return ALREADY_STARTED; + } + } + + // our lockFile is probably stale and left from a previous instance. + // or an instance that is still running but is not responding. + lockFile.delete(); + } + + // if we get here then this means that we should go for a real lock + // initialization + // create a new socket, + // right the bind address in the file + try + { + lockFile.createNewFile(); + } + catch (IOException e) + { + logger.error("Failed to create lock file", e); + } + + lockFile.deleteOnExit(); + + return lock(lockFile); + } + + /** + * Locks the configuration directory by binding our lock socket and + * recording the lock file into the configuration directory. Returns SUCCESS + * if everything goes well and ERROR if something fails. This method does + * not return the ALREADY_RUNNING code as it is assumed that this has + * already been checked before calling this method. + * + * @param lockFile + * the file that we should use to lock the configuration + * directory. + * + * @return the SUCCESS or ERROR codes defined by this class. + */ + private int lock(File lockFile) + { + InetAddress lockAddress = getRandomBindAddress(); + + if (lockAddress == null) + { + return LOCK_ERROR; + } + + int port = getRandomPortNumber(); + + InetSocketAddress serverSocketAddress = new InetSocketAddress( + lockAddress, port); + + writeLockFile(lockFile, serverSocketAddress); + + startLockServer(serverSocketAddress); + + return SUCCESS; + } + + /** + * Creates and binds a socket on <tt>lockAddress</tt> and then starts a + * <tt>LockServer</tt> instance so that we would start interacting with + * other instances of SIP Communicator that are trying to start. + * + * @return the <tt>ERROR</tt> code if something goes wrong and + * <tt>SUCCESS</tt> otherwise. + */ + private int startLockServer(InetSocketAddress localAddress) + { + try + { + // check config directory + instanceServerSocket = new ServerSocket(); + } + catch (IOException exc) + { + // Just checked the impl and this doesn't seem to ever be thrown + // .... ignore ... + logger.error("Couldn't create server socket", exc); + return LOCK_ERROR; + } + + try + { + instanceServerSocket.bind(localAddress, 16);// Why 16? 'cos I say + // so. + } + catch (IOException exc) + { + logger.error("Couldn't create server socket", exc); + return LOCK_ERROR; + } + + LockServer lockServ = new LockServer(instanceServerSocket); + + lockServ.start(); + + return SUCCESS; + } + + /** + * Returns a randomly chosen socket address using a loopback interface (or + * another one in case the loopback is not available) that we should bind + * on. + * + * @return an InetAddress (most probably a loopback) that we can use to bind + * our semaphore socket on. + */ + private InetAddress getRandomBindAddress() + { + NetworkInterface loopback; + try + { + // find a loopback interface + Enumeration<NetworkInterface> interfaces; + try + { + interfaces = NetworkInterface.getNetworkInterfaces(); + } + catch (SocketException exc) + { + // I don't quite understand why this would happen ... + logger.error( + "Failed to obtain a list of the local interfaces.", + exc); + return null; + } + + loopback = null; + while (interfaces.hasMoreElements()) + { + NetworkInterface iface = interfaces.nextElement(); + + if (iface.isLoopback()) + { + loopback = iface; + break; + } + } + + // if we didn't find a loopback (unlikely but possible) + // return the first available interface on this machine + if (loopback == null) + { + loopback = NetworkInterface.getNetworkInterfaces() + .nextElement(); + } + } + catch (SocketException exc) + { + // I don't quite understand what could possibly cause this ... + logger.error("Could not find the loopback interface", exc); + return null; + } + + // get the first address on the loopback. + InetAddress addr = loopback.getInetAddresses().nextElement(); + + return addr; + } + + /** + * Returns a random port number that we can use to bind a socket on. + * + * @return a random port number that we can use to bind a socket on. + */ + private int getRandomPortNumber() + { + return (int) (Math.random() * 64509) + 1025; + } + + /** + * Parses the <tt>lockFile</tt> into a standard Properties Object and + * verifies it for completeness. The method also tries to validate the + * contents of <tt>lockFile</tt> and asserts presence of all properties + * mandated by this version. + * + * @param lockFile + * the file that we are to parse. + * + * @return the <tt>SocketAddress</tt> that we should use to communicate with + * a possibly already running version of SIP Communicator. + */ + private InetSocketAddress readLockFile(File lockFile) + { + Properties lockProperties = new Properties(); + + try + { + lockProperties.load(new FileInputStream(lockFile)); + } + catch (Exception exc) + { + logger.error("Failed to read lock properties.", exc); + return null; + } + + String lockAddressStr = lockProperties.getProperty(PNAME_LOCK_ADDRESS); + if (lockAddressStr == null) + { + logger.error("Lock file contains no lock address."); + return null; + } + + String lockPort = lockProperties.getProperty(PNAME_LOCK_PORT); + if (lockPort == null) + { + logger.error("Lock file contains no lock port."); + return null; + } + + InetAddress lockAddress = findLocalAddress(lockAddressStr); + + if (lockAddress == null) + { + logger.error(lockAddressStr + " is not a valid local address."); + return null; + } + + int port; + try + { + port = Integer.parseInt(lockPort); + } + catch (NumberFormatException exc) + { + logger.error(lockPort + " is not a valid port number.", exc); + return null; + } + + InetSocketAddress lockSocketAddress = new InetSocketAddress( + lockAddress, port); + + return lockSocketAddress; + } + + /** + * Records our <tt>lockAddress</tt> into <tt>lockFile</tt> using the + * standard properties format. + * + * @param lockFile + * the file that we should store the address in. + * @param lockAddress + * the address that we have to record. + * + * @return <tt>SUCCESS</tt> upon success and <tt>ERROR</tt> if we fail to + * store the file. + */ + private int writeLockFile(File lockFile, InetSocketAddress lockAddress) + { + Properties lockProperties = new Properties(); + + lockProperties.setProperty(PNAME_LOCK_ADDRESS, lockAddress.getAddress() + .getHostAddress()); + + lockProperties.setProperty(PNAME_LOCK_PORT, Integer + .toString(lockAddress.getPort())); + + try + { + lockProperties.store(new FileOutputStream(lockFile), + "SIP Communicator lock file. This file will be automatically" + + "removed when execution of SIP Communicator terminates."); + } + catch (FileNotFoundException e) + { + e.printStackTrace(); + } + catch (IOException e) + { + logger.error("Failed to create lock file.", e); + return LOCK_ERROR; + } + + return SUCCESS; + + } + + /** + * Returns a reference to the file that we should be using to lock SIP + * Communicator's home directory, whether it exists or not. + * + * @return a reference to the file that we should be using to lock SIP + * Communicator's home directory. + */ + private File getLockFile() + { + String homeDirLocation = System + .getProperty(SIPCommunicator.PNAME_SC_HOME_DIR_LOCATION); + String homeDirName = System + .getProperty(SIPCommunicator.PNAME_SC_HOME_DIR_NAME); + + String fileSeparator = System.getProperty("file.separator"); + + String fullLockFileName = homeDirLocation + fileSeparator + homeDirName + + fileSeparator + LOCK_FILE_NAME; + + return new File(fullLockFileName); + } + + /** + * Returns an <tt>InetAddress</tt> instance corresponding to + * <tt>addressStr</tt> or <tt>null</tt> if no such address exists on the + * local interfaces. + * + * @param addressStr + * the address string that we are trying to resolve into an + * <tt>InetAddress</tt> + * + * @return an <tt>InetAddress</tt> instance corresponding to + * <tt>addressStr</tt> or <tt>null</tt> if none of the local + * interfaces has such an address. + */ + private InetAddress findLocalAddress(String addressStr) + { + Enumeration<NetworkInterface> ifaces; + + try + { + ifaces = NetworkInterface.getNetworkInterfaces(); + } + catch (SocketException exc) + { + logger.error( + "Could not extract the list of local intefcaces.", + exc); + return null; + } + + // loop through local interfaces + while (ifaces.hasMoreElements()) + { + NetworkInterface iface = ifaces.nextElement(); + + Enumeration<InetAddress> addreses = iface.getInetAddresses(); + + // loop iface addresses + while (addreses.hasMoreElements()) + { + InetAddress addr = addreses.nextElement(); + + if (addr.getHostAddress().equals(addressStr)) + return addr; + } + } + return null; + } + + /** + * Initializes a client TCP socket, connects if to <tt>sockAddr</tt> and + * sends all <tt>args</tt> to it. + * + * @param sockAddr the address that we are to connect to. + * @param args the args that we need to send to <tt>sockAddr</tt>. + * + * @return <tt>SUCCESS</tt> upond success and <tt>ERROR</tt> if anything + * goes wrong. + */ + private int interInstanceConnect(InetSocketAddress sockAddr, String[] args) + { + try + { + Socket interInstanceSocket = new Socket(sockAddr.getAddress(), + sockAddr.getPort()); + + LockClient lockClient = new LockClient(interInstanceSocket); + lockClient.start(); + + PrintStream printStream = new PrintStream(interInstanceSocket + .getOutputStream()); + + printStream.print(ARG_COUNT + "=" + args.length + CRLF); + + for (int i = 0; i < args.length; i++) + { + printStream.print(ARGUMENT + "=" + args[i] + CRLF); + } + + lockClient.waitForReply(LOCK_COMMUNICATION_DELAY); + + //NPEs are handled in catch so no need to check whether or not we + //actually have a reply. + String serverReadArgCountStr = lockClient.message + .substring((ARG_COUNT + "=").length()); + + int serverReadArgCount = Integer.parseInt(serverReadArgCountStr); + logger.debug("Server read " + serverReadArgCount + " args."); + + if(serverReadArgCount != args.length) + return LOCK_ERROR; + + printStream.flush(); + printStream.close(); + interInstanceSocket.close(); + } + //catch IOExceptions, NPEs and NumberFormatExceptions here. + catch (Exception e) + { + logger.debug("Failed to connect to a running sc instance."); + return LOCK_ERROR; + } + + return SUCCESS; + } + + /** + * We use this thread to communicate with an already running instance of SIP + * Communicator. This thread will listen for a reply to a message that we've + * sent to the other instance. We will wait for this message for a maximum + * of <tt>runDuration</tt> milliseconds and then consider the remote + * instance dead. + */ + private class LockClient extends Thread + { + /** + * The <tt>String</tt> that we've read from the socketInputStream + */ + public String message = null; + + /** + * The socket that this <tt>LockClient</tt> is created to read from. + */ + private Socket interInstanceSocket = null; + + /** + * Creates a <tt>LockClient</tt> that should read whatever data we + * receive on <tt>sockInputStream</tt>. + * + * @param commSocket + * the socket that this client should be reading from. + */ + public LockClient(Socket commSocket) + { + super(LockClient.class.getName()); + setDaemon(true); + this.interInstanceSocket = commSocket; + } + + + /** + * Blocks until a reply has been received or until run<tt>Duration</tt> + * milliseconds had passed. + * + * @param runDuration the number of seconds to wait for a reply from + * the remote instance + */ + public void waitForReply(long runDuration) + { + try + { + synchronized(this) + { + //return if we have already received a message. + if(message != null) + return; + + wait(runDuration); + } + + logger.debug("Done waiting. Will close socket"); + interInstanceSocket.close(); + } + catch (Exception exception) + { + logger.error("Failed to close our inter instance input stream", + exception); + } + } + + /** + * Simply collects everything that we read from the InputStream that + * this <tt>InterInstanceCommunicationClient</tt> was created with. + */ + public void run() + { + try + { + BufferedReader lineReader = new BufferedReader( + new InputStreamReader(interInstanceSocket + .getInputStream())); + + //we only need to read a single line and then bail out. + message = lineReader.readLine(); + logger.debug("Message is " + message); + synchronized(this) + { + notifyAll(); + } + } + catch (IOException exc) + { + // does not necessarily mean something is wrong. Could be + // that we got tired of waiting and want to quit. + logger.info("An IOException is thrown while reading sock", exc); + } + } + } + + /** + * We start this thread when running SIP Communicator as a means of + * notifying others that this is + */ + private class LockServer extends Thread + { + private boolean keepAccepting = true; + + /** + * The socket that we use for cross instance lock and communication. + */ + private ServerSocket lockSocket = null; + + /** + * Creates an instance of this <tt>LockServer</tt> wrapping the + * specified <tt>serverSocket</tt>. It is expected that the serverSocket + * will be already bound and ready to accept. + * + * @param serverSocket + * the serverSocket that we should use for inter instance + * communication. + */ + public LockServer(ServerSocket serverSocket) + { + super(LockServer.class.getName()); + setDaemon(true); + this.lockSocket = serverSocket; + } + + public void run() + { + try + { + while (keepAccepting) + { + Socket instanceSocket = lockSocket.accept(); + + new LockServerConnectionProcessor(instanceSocket).start(); + } + } + catch (Exception exc) + { + logger.warn("Someone tried ", exc); + } + } + } + + /** + * We use this thread to handle individual messages in server side inter + * instance communication. + */ + private class LockServerConnectionProcessor extends Thread + { + /** + * The socket that we will be using to communicate with the fellow SIP + * Communicator instance.. + */ + private Socket connectionSocket = null; + + /** + * Creates an instance of <tt>LockServerConnectionProcessor</tt> that + * would handle parameters received through the + * <tt>connectionSocket</tt>. + * + * @param connectedSocket + * the socket that we will be using to read arguments from + * the remote SIP Communicator instance. + */ + public LockServerConnectionProcessor(Socket connectionSocket) + { + this.connectionSocket = connectionSocket; + } + + /** + * Starts reading messages arriving through the connection socket. + */ + public void run() + { + InputStream is; + PrintWriter printer; + try + { + is = connectionSocket.getInputStream(); + printer = new PrintWriter(connectionSocket + .getOutputStream()); + } + catch (IOException exc) + { + logger.warn("Failed to read arguments from another SC instance", + exc); + return; + } + + ArrayList<String> argsList = new ArrayList<String>(); + + logger.debug("Handling incoming connection"); + + int argCount = 1024; + try + { + while (true) + { + BufferedReader lineReader = new BufferedReader( + new InputStreamReader(is)); + String line = lineReader.readLine(); + + logger.debug(line); + + if (line.startsWith(ARG_COUNT)) + { + argCount = Integer.parseInt(line + .substring((ARG_COUNT + "=").length())); + } + else if (line.startsWith(ARGUMENT)) + { + String arg = line.substring((ARGUMENT + "=").length()); + argsList.add(arg); + } + else + { + // ignore unknown headers. + } + + if (argCount <= argsList.size()) + break; + } + + // first tell the remote application that everything went OK + // and end the connection so that it could exit + printer.print(ARG_COUNT + "=" + argCount + CRLF); + printer.close(); + connectionSocket.close(); + + // now let's handle what we've got + String[] args = new String[argsList.size()]; + LaunchArgHandler.getInstance().handleArgs( + argsList.toArray(args)); + } + catch (IOException exc) + { + logger.info("An IOException is thrown while " + + "processing remote args", exc); + + printer.print(ERROR_ARG + "=" + exc.getMessage()); + } + } + } +} diff --git a/src/net/java/sip/communicator/util/launchutils/UriArgManager.java b/src/net/java/sip/communicator/util/launchutils/UriArgManager.java new file mode 100644 index 0000000..6c669fc --- /dev/null +++ b/src/net/java/sip/communicator/util/launchutils/UriArgManager.java @@ -0,0 +1,84 @@ +/* + * 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.util.launchutils; + +import java.util.*; + +import net.java.sip.communicator.util.*; + +/** + * The <tt>UriArgManager</tt> implements an utility for handling URIs that have + * been passed as command line arguments. The class maintains a list of + * registered delegates that do the actual URI handling. The UriArgDelegator + * is previewed for use with SIP Communicator argdelegation service. It would + * therefore record all URIs until the corresponding DelegationPeer has been + * registered with the UriArgManager. + * + * @author Emil Ivov + */ +class UriArgManager +{ + private static final Logger logger = Logger.getLogger(UriArgManager.class); + + /** + * The delegation peer that we pass arguments to. This peer is going to + * get set only after Felix starts and all its services have been properly + * loaded. + */ + private UriDelegationPeer uriDelegationPeer = null; + + /** + * We use this list to store arguments that we have been asked to handle + * before we had a registered delegation peer. + */ + private List<String> recordedArgs = new LinkedList<String>(); + + /** + * Passes the <tt>uriArg</tt> to the uri delegation peer or, in case + * no peer is currently registered, stores it and keeps it until one + * appears. + * + * @param uriArg the uri argument that we'd like to delegate to our peer. + */ + protected void handleUri(String uriArg) + { + synchronized(recordedArgs) + { + if(uriDelegationPeer == null) + { + recordedArgs.add(uriArg); + return; + } + } + + uriDelegationPeer.handleUri(uriArg); + } + + /** + * Sets a delegation peer that we can now use to pass arguments to and + * makes it handle all arguments that have been already registered. + * + * @param delegationPeer the delegation peer that we can use to deliver + * command line URIs to. + */ + public void setDelegationPeer(UriDelegationPeer delegationPeer) + { + synchronized(recordedArgs) + { + logger.trace("Someone set a delegationPeer. " + +"Will dispatch "+ recordedArgs.size() +" args"); + this.uriDelegationPeer = delegationPeer; + + for (String arg : recordedArgs) + { + logger.trace("Dispatching arg: " + arg); + uriDelegationPeer.handleUri(arg); + } + + recordedArgs.clear(); + } + } +} diff --git a/src/net/java/sip/communicator/util/launchutils/UriDelegationPeer.java b/src/net/java/sip/communicator/util/launchutils/UriDelegationPeer.java new file mode 100644 index 0000000..e19bc18 --- /dev/null +++ b/src/net/java/sip/communicator/util/launchutils/UriDelegationPeer.java @@ -0,0 +1,26 @@ +/* + * 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.util.launchutils; + +/** + * The <tt>UriDelegationPeer</tt> is used as a mechanism to pass arguments from + * the UriArgManager which resides in "launcher space" to our argument + * delegation service implementation that lives as an osgi bundle. An instance + * of this peer is created from within the argument delegation service impl + * and is registered with the UriArgManager. + * + * @author Emil Ivov + */ +public interface UriDelegationPeer +{ + /** + * Handles <tt>uriArg</tt> in whatever way it finds fit. + * + * @param uriArg the uri argument that this delegate has to handle. + */ + public void handleUri(String uriArg); +} |