diff options
author | Sebastien Vincent <seb@jitsi.org> | 2010-09-29 11:15:10 +0000 |
---|---|---|
committer | Sebastien Vincent <seb@jitsi.org> | 2010-09-29 11:15:10 +0000 |
commit | 3d77efb36f85e00b0a1df6ba5a612b9beffd326a (patch) | |
tree | 810927921b242dc7ae5351ae7cb236320e5cd5d6 | |
parent | 502471520721b867ff7fd37ab4497699b20764fa (diff) | |
download | jitsi-3d77efb36f85e00b0a1df6ba5a612b9beffd326a.zip jitsi-3d77efb36f85e00b0a1df6ba5a612b9beffd326a.tar.gz jitsi-3d77efb36f85e00b0a1df6ba5a612b9beffd326a.tar.bz2 |
Initial commit to support provisioning. It provides the DHCP provisioning discovery method, the provisioning URL can also be retrieved from configuration.
17 files changed, 1569 insertions, 4 deletions
@@ -931,7 +931,9 @@ bundle-replacement,bundle-youtube,bundle-dailymotion,bundle-smiley, bundle-vimeo,bundle-vbox7,bundle-metacafe,bundle-flickr,bundle-hulu, bundle-twitpic,bundle-directimage,bundle-bliptv,bundle-viddler, - bundle-plugin-chatconfig,bundle-certificate"/> + bundle-plugin-chatconfig,bundle-certificate, + bundle-provdisc,bundle-provdisc-dhcp, + bundle-provisioning"/> <!--BUNDLE-SC-LAUNCHER--> <target name="bundle-sc-launcher"> @@ -2526,6 +2528,35 @@ org.apache.http.util"/> </jar> </target> + <!-- BUNDLE-PROVDISC--> + <target name="bundle-provdisc"> + <!-- Creates a bundle containing the provisioning discovery service.--> + <jar compress="false" destfile="${bundles.dest}/provdisc.jar" + manifest="${src}/net/java/sip/communicator/service/provdisc/provdisc.manifest.mf"> + <zipfileset dir="${dest}/net/java/sip/communicator/service/provdisc" + prefix="net/java/sip/communicator/service/provdisc"/> + </jar> + </target> + + <!--BUNDLE-PROVDISC-DHCP --> + <target name="bundle-provdisc-dhcp"> + <jar compress="false" destfile="${bundles.dest}/provdisc-dhcp.jar" + manifest="${src}/net/java/sip/communicator/impl/provdisc/dhcp/dhcp.provdisc.manifest.mf"> + <zipfileset dir="${dest}/net/java/sip/communicator/impl/provdisc/dhcp" + prefix="net/java/sip/communicator/impl/provdisc/dhcp" /> + <zipfileset src="${lib.noinst}/dhcp4java-1.00.jar"/> + </jar> + </target> + + <!--BUNDLE-PROVISIONING --> + <target name="bundle-provisioning"> + <jar compress="false" destfile="${bundles.dest}/provisioning.jar" + manifest="${src}/net/java/sip/communicator/plugin/provisioning/provisioning.manifest.mf"> + <zipfileset dir="${dest}/net/java/sip/communicator/plugin/provisioning" + prefix="net/java/sip/communicator/plugin/provisioning" /> + </jar> + </target> + <target name="bundle-certificate"> <jar compress="false" destfile="${bundles.dest}/certificate.jar" manifest="${src}/net/java/sip/communicator/impl/certificate/certificate.manifest.mf"> diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties index f1907d9..6425738 100644 --- a/lib/felix.client.run.properties +++ b/lib/felix.client.run.properties @@ -38,8 +38,10 @@ felix.auto.start.30= \ felix.auto.start.40= \ reference:file:sc-bundles/configuration.jar \ + reference:file:sc-bundles/provdisc.jar \ reference:file:sc-bundles/resourcemanager.jar \ reference:file:sc-bundles/jfontchooserlib.jar \ + reference:file:sc-bundles/netaddr.jar \ reference:file:sc-bundles/browserlauncher.jar felix.auto.start.42= \ @@ -52,9 +54,11 @@ felix.auto.start.45= \ reference:file:sc-bundles/version.jar \ reference:file:sc-bundles/version-impl.jar \ reference:file:sc-bundles/branding.jar \ + reference:file:sc-bundles/provdisc-dhcp.jar \ reference:file:sc-bundles/certificate.jar felix.auto.start.49= \ + reference:file:sc-bundles/provisioning.jar \ reference:file:sc-bundles/bouncycastle.jar \ reference:file:sc-bundles/zrtp4j.jar \ reference:file:sc-bundles/protocol.jar \ @@ -81,7 +85,6 @@ felix.auto.start.52= \ reference:file:sc-bundles/protocol-yahoo.jar \ reference:file:sc-bundles/protocol-gibberish.jar \ reference:file:sc-bundles/protocol-ssh.jar \ - reference:file:sc-bundles/netaddr.jar \ reference:file:sc-bundles/reconnectplugin.jar \ reference:file:sc-bundles/protocol-zeroconf.jar \ reference:file:sc-bundles/protocol-irc.jar \ diff --git a/lib/installer-exclude/dhcp4java-1.00.jar b/lib/installer-exclude/dhcp4java-1.00.jar Binary files differnew file mode 100644 index 0000000..516897b --- /dev/null +++ b/lib/installer-exclude/dhcp4java-1.00.jar diff --git a/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java b/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java index 042a7d8..3515643 100644 --- a/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java +++ b/src/net/java/sip/communicator/impl/netaddr/NetworkAddressManagerServiceImpl.java @@ -163,7 +163,6 @@ public class NetworkAddressManagerServiceImpl } else { - //no point in making sure that the localHostFinderSocket is //initialized. //better let it through a NullPointerException. diff --git a/src/net/java/sip/communicator/impl/provdisc/dhcp/DHCPProvisioningDiscover.java b/src/net/java/sip/communicator/impl/provdisc/dhcp/DHCPProvisioningDiscover.java new file mode 100644 index 0000000..6c593f8 --- /dev/null +++ b/src/net/java/sip/communicator/impl/provdisc/dhcp/DHCPProvisioningDiscover.java @@ -0,0 +1,252 @@ +/* + * 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.provdisc.dhcp; + +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.service.provdisc.event.*; +import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.util.*; + +import org.dhcp4java.*; + +/** + * Class that will perform DHCP provisioning discovery. + * + * @author Sebastien Vincent + */ +public class DHCPProvisioningDiscover + implements Runnable +{ + /** + * Logger. + */ + private final Logger logger + = Logger.getLogger(DHCPProvisioningDiscover.class); + + /** + * UDP socket. + */ + private DatagramSocket socket = null; + + /** + * Listening port of the client. Note that the socket will send packet to + * DHCP server on port - 1. + */ + private int port = 6768; + + /** + * Option code of the specific provisioning option. + */ + private byte option = (byte)120; + + /** + * DHCP timeout (in milliseconds). + */ + private static final int DHCP_TIMEOUT = 5000; + + /** + * List of <tt>ProvisioningListener</tt> that will be notified when + * a provisioning URL is retrieved. + */ + private List<DiscoveryListener> listeners = + new ArrayList<DiscoveryListener>(); + + /** + * Constructor. + * + * @param port port on which we will bound and listen for DHCP response + * @param option code of the specific provisioning option + * @throws Exception if anything goes wrong during initialization + */ + public DHCPProvisioningDiscover(int port, byte option) throws Exception + { + this.port = port; + this.option = option; + socket = new DatagramSocket(port); + + /* set timeout so that we will not blocked forever if we + * have no response from DHCP server + */ + socket.setSoTimeout(DHCP_TIMEOUT); + } + + /** + * It sends a DHCPINFORM message from all interfaces and wait for a + * response. Thread stops after first successful answer that contains + * specific option and thus the provisioning URL. + * + * @return provisioning URL + */ + public String discoverProvisioningURL() + { + DHCPPacket inform = new DHCPPacket(); + byte macAddress[] = null; + byte zeroIPAddress[] = {0x00, 0x00, 0x00, 0x00}; + byte broadcastIPAddr[] = {(byte)255, (byte)255, (byte)255, (byte)255}; + DHCPOption dhcpOpts[] = new DHCPOption[1]; + int xid = new Random().nextInt(); + + try + { + + inform.setOp(DHCPConstants.BOOTREQUEST); + inform.setHtype(DHCPConstants.HTYPE_ETHER); + inform.setHlen((byte) 6); + inform.setHops((byte) 0); + inform.setXid(xid); + inform.setSecs((short) 0); + inform.setFlags((short) 0); + inform.setYiaddr(InetAddress.getByAddress(zeroIPAddress)); + inform.setSiaddr(InetAddress.getByAddress(zeroIPAddress)); + inform.setGiaddr(InetAddress.getByAddress(zeroIPAddress)); + inform.setChaddr(macAddress); + inform.setDhcp(true); + inform.setDHCPMessageType(DHCPConstants.DHCPINFORM); + + dhcpOpts[0] = new DHCPOption( + DHCPConstants.DHO_DHCP_PARAMETER_REQUEST_LIST, + new byte[] {option}); + + inform.setOptions(dhcpOpts); + + Enumeration<NetworkInterface> en = + NetworkInterface.getNetworkInterfaces(); + + while(en.hasMoreElements()) + { + NetworkInterface iface = en.nextElement(); + + Enumeration<InetAddress> enAddr = iface.getInetAddresses(); + while(enAddr.hasMoreElements()) + { + InetAddress addr = enAddr.nextElement(); + + /* just take IPv4 address */ + if(addr instanceof Inet4Address) + { + NetworkAddressManagerService netaddr = + ProvisioningDiscoveryDHCPActivator. + getNetworkAddressManagerService(); + + if(!addr.isLoopbackAddress()) + { + macAddress = netaddr.getHardwareAddress(iface); + DHCPPacket p = inform.clone(); + + p.setCiaddr(addr); + p.setChaddr(macAddress); + + byte msg[] = inform.serialize(); + DatagramPacket pkt = new DatagramPacket(msg, + msg.length, + InetAddress.getByAddress(broadcastIPAddr), + port - 1); + + socket.send(pkt); + msg = null; + pkt = null; + p = null; + } + } + } + } + + /* now see if we receive DHCP ACK response and if it contains + * our custom option + */ + boolean found = false; + + try + { + DatagramPacket pkt2 = new DatagramPacket(new byte[1500], 1500); + + while(!found) + { + /* we timeout after 5 seconds if no DHCP response are + * received + */ + socket.receive(pkt2); + DHCPPacket dhcp = DHCPPacket.getPacket(pkt2); + + if(dhcp.getXid() != xid) + { + continue; + } + + DHCPOption optProvisioning = dhcp.getOption(option); + + /* notify */ + if(optProvisioning != null) + { + found = true; + return new String(optProvisioning.getValue()); + } + } + } + catch(SocketTimeoutException est) + { + logger.warn("Timeout, no DHCP answer received", est); + } + } + catch(Exception e) + { + logger.warn("Exception occurred during DHCP discover", e); + } + + return null; + } + + /** + * Thread entry point. It runs <tt>discoverProvisioningURL</tt> in a + * separate thread. + */ + public void run() + { + String url = discoverProvisioningURL(); + + if(url != null) + { + /* as we run in an asynchronous manner, notify the listener */ + DiscoveryEvent evt = new DiscoveryEvent(this, url); + + for(DiscoveryListener listener : listeners) + { + listener.notifyProvisioningURL(evt); + } + } + } + + /** + * Add a listener that will be notified when the + * <tt>discoverProvisioningURL</tt> has finished. + * + * @param listener <tt>ProvisioningListener</tt> to add + */ + public void addDiscoveryListener(DiscoveryListener listener) + { + if(!listeners.contains(listener)) + { + listeners.add(listener); + } + } + + /** + * Add a listener that will be notified when the + * <tt>discoverProvisioningURL</tt> has finished. + * + * @param listener <tt>ProvisioningListener</tt> to add + */ + public void removeDiscoveryListener(DiscoveryListener listener) + { + if(listeners.contains(listener)) + { + listeners.remove(listener); + } + } +} diff --git a/src/net/java/sip/communicator/impl/provdisc/dhcp/ProvisioningDiscoveryDHCPActivator.java b/src/net/java/sip/communicator/impl/provdisc/dhcp/ProvisioningDiscoveryDHCPActivator.java new file mode 100644 index 0000000..01ba693 --- /dev/null +++ b/src/net/java/sip/communicator/impl/provdisc/dhcp/ProvisioningDiscoveryDHCPActivator.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.provdisc.dhcp; + +import org.osgi.framework.*; + +import net.java.sip.communicator.service.provdisc.*; +import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.util.*; + +/** + * Implements <tt>BundleActivator</tt> for the DHCP provisioning bundle. + * + * @author Sebastien Vincent + */ +public class ProvisioningDiscoveryDHCPActivator + implements BundleActivator +{ + /** + * <tt>Logger</tt> used by this <tt>DHCPProvisioningDiscoveryActivator</tt> + * instance for logging output. + */ + private final Logger logger + = Logger.getLogger(ProvisioningDiscoveryDHCPActivator.class); + + /** + * DHCP provisioning service. + */ + private static ProvisioningDiscoveryServiceDHCPImpl provisioningService = + new ProvisioningDiscoveryServiceDHCPImpl(); + + /** + * A reference to the currently valid {@link NetworkAddressManagerService}. + */ + private static NetworkAddressManagerService + networkAddressManagerService = null; + + /** + * Bundle context from OSGi. + */ + private static BundleContext bundleContext = null; + + /** + * Starts the DHCP provisioning service + * + * @param bundleContext the <tt>BundleContext</tt> as provided by the OSGi + * framework. + * @throws Exception if anything goes wrong + */ + public void start(BundleContext bundleContext) + throws Exception + { + if (logger.isDebugEnabled()) + logger.debug("DHCP provisioning discovery Service [STARTED]"); + + bundleContext.registerService( + ProvisioningDiscoveryService.class.getName(), + provisioningService, + null); + + ProvisioningDiscoveryDHCPActivator.bundleContext = bundleContext; + + if (logger.isDebugEnabled()) + logger.debug("DHCP provisioning discovery Service [REGISTERED]"); + } + + /** + * Stops the DHCP provisioning service. + * + * @param bundleContext the <tt>BundleContext</tt> as provided by the OSGi + * framework. + * @throws Exception if anything goes wrong + */ + public void stop(BundleContext bundleContext) throws Exception + { + ProvisioningDiscoveryDHCPActivator.bundleContext = null; + + if (logger.isInfoEnabled()) + logger.info("DHCP provisioning discovery Service ...[STOPPED]"); + } + + /** + * Returns a reference to a NetworkAddressManagerService implementation + * currently registered in the bundle context or null if no such + * implementation was found. + * + * @return a currently valid implementation of the + * NetworkAddressManagerService . + */ + public static NetworkAddressManagerService getNetworkAddressManagerService() + { + if(networkAddressManagerService == null) + { + ServiceReference confReference + = bundleContext.getServiceReference( + NetworkAddressManagerService.class.getName()); + networkAddressManagerService = (NetworkAddressManagerService) + bundleContext.getService(confReference); + } + return networkAddressManagerService; + } +} diff --git a/src/net/java/sip/communicator/impl/provdisc/dhcp/ProvisioningDiscoveryServiceDHCPImpl.java b/src/net/java/sip/communicator/impl/provdisc/dhcp/ProvisioningDiscoveryServiceDHCPImpl.java new file mode 100644 index 0000000..121a218 --- /dev/null +++ b/src/net/java/sip/communicator/impl/provdisc/dhcp/ProvisioningDiscoveryServiceDHCPImpl.java @@ -0,0 +1,109 @@ +/* + * 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.provdisc.dhcp; + +import net.java.sip.communicator.service.provdisc.*; +import net.java.sip.communicator.service.provdisc.event.*; +import net.java.sip.communicator.util.*; + +/** + * Class that uses DHCP to retrieve provisioning URL. Basically it sends a + * DHCPINFORM message with a custom option code in parameters list. + * + * Note that DHCP server have to understand this option and thus configured to + * answer with a HTTP/HTTPS URL. + * + * @author Sebastien Vincent + */ +public class ProvisioningDiscoveryServiceDHCPImpl + extends AbstractProvisioningDiscoveryService + implements DiscoveryListener +{ + /** + * Logger. + */ + private final Logger logger + = Logger.getLogger(ProvisioningDiscoveryServiceDHCPImpl.class); + + /** + * DHCP provisioning discover object. + */ + private DHCPProvisioningDiscover discover = null; + + /** + * Name of the method used to retrieve provisioning URL. + */ + private static final String METHOD_NAME = "DHCP"; + + /** + * Get the name of the method name used to retrieve provisioning URL. + * + * @return method name + */ + public String getMethodName() + { + return METHOD_NAME; + } + + /** + * Constructor. + */ + public ProvisioningDiscoveryServiceDHCPImpl() + { + try + { + discover = new DHCPProvisioningDiscover(6768, (byte)120); + discover.addDiscoveryListener(this); + } + catch(Exception e) + { + logger.warn("Cannot create DHCP client socket", e); + } + } + + /** + * Launch a discovery for a provisioning URL. This method is synchronous and + * may block for some time. Note that you don't have to call + * <tt>startDiscovery</tt> method prior to this one to retrieve URL. + * + * @return provisioning URL + */ + public String discoverURL() + { + if(discover != null) + { + return discover.discoverProvisioningURL(); + } + + return null; + } + + /** + * Launch a discovery for a provisioning URL by sending a DHCP Inform + * with parameter list option containing a custom option code. + * + * This method is asynchronous, the response will be notified to any + * <tt>ProvisioningListener</tt> registered. + */ + public void startDiscovery() + { + if(discover != null) + { + new Thread(discover).start(); + } + } + + /** + * Notify the provisioning URL. + * + * @param event provisioning event + */ + public void notifyProvisioningURL(DiscoveryEvent event) + { + fireDiscoveryEvent(event); + } +}
\ No newline at end of file diff --git a/src/net/java/sip/communicator/impl/provdisc/dhcp/dhcp.provdisc.manifest.mf b/src/net/java/sip/communicator/impl/provdisc/dhcp/dhcp.provdisc.manifest.mf new file mode 100644 index 0000000..6663e51 --- /dev/null +++ b/src/net/java/sip/communicator/impl/provdisc/dhcp/dhcp.provdisc.manifest.mf @@ -0,0 +1,11 @@ +Bundle-Activator: net.java.sip.communicator.impl.provdisc.dhcp.ProvisioningDiscoveryDHCPActivator +Bundle-Name: DHCP provisioning discovery +Bundle-Description: A bundle providing support for DHCP provisioning discovery +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +System-Bundle: yes +Import-Package: org.osgi.framework, + net.java.sip.communicator.service.provdisc, + net.java.sip.communicator.service.provdisc.event, + net.java.sip.communicator.service.netaddr, + net.java.sip.communicator.util diff --git a/src/net/java/sip/communicator/plugin/provisioning/ProvisioningPluginActivator.java b/src/net/java/sip/communicator/plugin/provisioning/ProvisioningPluginActivator.java new file mode 100644 index 0000000..23143be --- /dev/null +++ b/src/net/java/sip/communicator/plugin/provisioning/ProvisioningPluginActivator.java @@ -0,0 +1,800 @@ +/* + * 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.plugin.provisioning; + +import java.io.*; +import java.net.*; +import java.util.*; + +import javax.net.ssl.*; +import javax.swing.*; + +import net.java.sip.communicator.service.configuration.*; +import net.java.sip.communicator.service.credentialsstorage.*; +import net.java.sip.communicator.service.certificate.*; +import net.java.sip.communicator.service.netaddr.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.provdisc.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.swing.*; + +import org.osgi.framework.*; + +/** + * Activator the provisioning system. It will gather provisioning URL depending + * on the configuration (DHCP, manual, ...), retrieve configuration file and + * push properties to the <tt>ConfigurationService</tt>. + */ +public class ProvisioningPluginActivator + implements BundleActivator +{ + /** + * Logger of this class + */ + private static final Logger logger + = Logger.getLogger(ProvisioningPluginActivator.class); + + /** + * The current BundleContext. + */ + private static BundleContext bundleContext = null; + + /** + * Name of the provisioning URL in the configuration service. + */ + private static final String PROPERTY_PROVISIONING_URL + = "net.java.sip.communicator.plugin.provisioning.URL"; + + /** + * Name of the username for the HTTP authentication. + */ + private static final String PROPERTY_HTTP_USERNAME + = "net.java.sip.communicator.plugin.provisioning.auth.http.USERNAME"; + + /** + * Name of the password for the HTTP authentication. + */ + private static final String PROPERTY_HTTP_PASSWORD + = "net.java.sip.communicator.plugin.provisioning.auth.http"; + + /** + * Name of the provisioning username in the configuration service + * authentication). + */ + private static final String PROPERTY_PROVISIONING_USERNAME + = "net.java.sip.communicator.plugin.provisioning.auth.access.USERNAME"; + + /** + * Name of the provisioning password in the configuration service (HTTP + * authentication). + */ + private static final String PROPERTY_PROVISIONING_PASSWORD + = "net.java.sip.communicator.plugin.provisioning.auth.access"; + + /** + * Name of the property that contains the provisioning method (i.e. DHCP, + * DNS, manual, ...). + */ + private static final String PROVISIONING_METHOD_PROP + = "net.java.sip.communicator.plugin.provisioning.METHOD"; + + /** + * A reference to the ConfigurationService implementation instance that + * is currently registered with the bundle context. + */ + private static ConfigurationService configurationService = null; + + /** + * A reference to the CredentialsStorageService implementation instance + * that is registered with the bundle context. + */ + private static CredentialsStorageService credentialsService = null; + + /** + * The service we use to interact with user for SSL certificate stuff. + */ + private static CertificateVerificationService certVerification = null; + + /** + * A reference to the NetworkAddressManagerService implementation instance + * that is registered with the bundle context. + */ + private static NetworkAddressManagerService netaddrService = null; + + /** + * User credentials to access URL (protected by HTTP authentication) if any. + */ + private static UserCredentials userCredentials = null; + + /** + * User credentials that user have to provide to the provisioning server + * if any. + */ + private static UserCredentials provCredentials = null; + + /** + * HTTP method to request a page. + */ + private String method = "POST"; + + /** + * Starts this bundle + * + * @param bundleContext BundleContext + * @throws Exception if anything goes wrong during the start of the bundle + */ + public void start(BundleContext bundleContext) throws Exception + { + String url = null; + + if (logger.isDebugEnabled()) + logger.debug("Provisioning discovery [STARTED]"); + + ProvisioningPluginActivator.bundleContext = bundleContext; + + String method = getConfigurationService().getString( + PROVISIONING_METHOD_PROP); + + if(method == null || method.equals("NONE")) + { + return; + } + + ServiceReference serviceReferences[] = bundleContext. + getServiceReferences(ProvisioningDiscoveryService.class.getName(), + null); + + /* search the provisioning discovery implementation that correspond to + * the method name + */ + if(serviceReferences != null) + { + for(ServiceReference ref : serviceReferences) + { + ProvisioningDiscoveryService provdisc = + (ProvisioningDiscoveryService)bundleContext.getService(ref); + + if(provdisc.getMethodName().equals(method)) + { + /* may block for sometime depending on the method used */ + url = provdisc.discoverURL(); + break; + } + } + } + + if(url == null) + { + /* try to see if provisioning URL is stored in properties */ + url = getConfigurationService().getString( + PROPERTY_PROVISIONING_URL); + } + + if(url != null) + { + File file = retrieveConfigurationFile(url); + + if(file != null) + { + updateConfiguration(file); + + /* store the provisioning URL in local configuration in case + * the provisioning discovery failed (DHCP/DNS unavailable, ...) + */ + getConfigurationService().setProperty( + PROPERTY_PROVISIONING_URL, url); + } + } + + if (logger.isDebugEnabled()) + logger.debug("Provisioning discovery [REGISTERED]"); + } + + /** + * Stops this bundle + * + * @param bundleContext BundleContext + * @throws Exception if anything goes wrong during the stop of the bundle + */ + public void stop(BundleContext bundleContext) throws Exception + { + ProvisioningPluginActivator.bundleContext = null; + + if (logger.isDebugEnabled()) + logger.debug("Provisioning discovery [STOPPED]"); + } + + /** + * Configure HTTP connection to provide HTTP authentication and SSL + * truster. + * + * @param url provisioning URL + * @param connection the <tt>URLConnection</tt> + */ + private void configureHTTPConnection(URL url, HttpURLConnection connection) + { + try + { + connection.setRequestMethod(method); + + if(method.equals("POST")) + { + connection.setRequestProperty("Content-Type", + "application/x-www-form-urlencoded"); + } + + if(connection instanceof HttpsURLConnection) + { + CertificateVerificationService vs = + getCertificateVerificationService(); + + int port = url.getPort(); + + /* if we do not specify port in the URL (http://domain.org:port) + * we have to set up the default port of HTTP (80) or + * HTTPS (443). + */ + if(port == -1) + { + if(url.getProtocol().equals("http")) + { + port = 80; + } + else if(url.getProtocol().equals("https")) + { + port = 443; + } + } + + ((HttpsURLConnection)connection).setSSLSocketFactory( + vs.getSSLContext( + url.getHost(), port).getSocketFactory()); + } + } + catch (Exception e) + { + logger.warn("Failed to initialize secure connection", e); + } + + Authenticator.setDefault(new Authenticator() + { + protected PasswordAuthentication getPasswordAuthentication() + { + // if there is something save return it + ConfigurationService config = getConfigurationService(); + CredentialsStorageService credStorage = + getCredentialsStorageService(); + + String uName + = (String) config.getProperty( + PROPERTY_HTTP_USERNAME); + + if(uName != null) + { + String pass = credStorage.loadPassword( + PROPERTY_HTTP_PASSWORD); + + if(pass != null) + return new PasswordAuthentication(uName, + pass.toCharArray()); + } + + if(userCredentials != null) + { + return new PasswordAuthentication( + userCredentials.getUserName(), + userCredentials.getPassword()); + } + else + { + return null; + } + } + }); + } + + /** + * Handle authentication with the provisioning server. + */ + private void handleProvisioningAuth() + { + ConfigurationService configService = getConfigurationService(); + CredentialsStorageService credService = + getCredentialsStorageService(); + + String username = configService.getString( + PROPERTY_PROVISIONING_USERNAME); + String password = credService.loadPassword( + PROPERTY_PROVISIONING_PASSWORD); + + if(username != null && password != null) + { + /* we have already the credentials stored so return them */ + provCredentials = new UserCredentials(); + provCredentials.setUserName(username); + provCredentials.setPassword(password.toCharArray()); + provCredentials.setPasswordPersistent(true); + return; + } + + AuthenticationWindow authWindow = new AuthenticationWindow( + "provisioning", true, null); + + authWindow.setVisible(true); + + if(!authWindow.isCanceled()) + { + provCredentials = new UserCredentials(); + provCredentials.setUserName(authWindow.getUserName()); + provCredentials.setPassword(authWindow.getPassword()); + provCredentials.setPasswordPersistent(authWindow.isRememberPassword()); + + if(provCredentials.getUserName() == null) + { + provCredentials = null; + } + } + else + { + provCredentials = null; + } + } + + /** + * Retrieve configuration file from provisioning URL. + * This method is blocking until configuration file is retrieved from the + * network or if an exception happen + * + * @param url provisioning URL + * @return provisioning file downloaded + */ + private File retrieveConfigurationFile(String url) + { + File tmpFile = null; + + try + { + String arg = null; + String args[] = null; + final File temp = File.createTempFile("provisioning", + ".properties"); + + tmpFile = temp; + + if(url.contains("?")) + { + /* do not handle URL of type http://domain/index.php? (no + * parameters) + */ + if((url.indexOf('?') + 1) != url.length()) + { + arg = url.substring(url.indexOf('?') + 1); + args = arg.split("&"); + } + url = url.substring(0, url.indexOf('?')); + } + + URL u = new URL(url); + URLConnection uc = u.openConnection(); + OutputStreamWriter out = null; + + if(uc instanceof HttpURLConnection) + { + configureHTTPConnection(u, (HttpURLConnection)uc); + ((HttpURLConnection)uc).setInstanceFollowRedirects(false); + uc.setDoInput(true); + uc.setDoOutput(true); + out = new OutputStreamWriter(uc.getOutputStream()); + + /* send out (via GET or POST) */ + StringBuffer content = new StringBuffer(); + InetAddress ipaddr = getNetworkAddressManagerService(). + getLocalHost(InetAddress.getByName(u.getHost())); + + if(args != null && args.length > 0) + { + for(String s : args) + { + if(s.equals("username")) + { + if(provCredentials == null) + { + handleProvisioningAuth(); + } + + if(provCredentials == null) + { + return null; + } + + content.append("username=" + + URLEncoder.encode( + provCredentials.getUserName(), + "UTF-8")); + } + else if(s.equals("password")) + { + if(provCredentials == null) + { + handleProvisioningAuth(); + } + + if(provCredentials == null) + { + return null; + } + + content.append("&password=" + + URLEncoder.encode( + provCredentials. + getPasswordAsString(), + "UTF-8")); + } + else if(s.equals("osname")) + { + content.append("&osname=" + URLEncoder.encode( + System.getProperty("os.name"), "UTF-8")); + } + else if(s.equals("build")) + { + content.append("&build=" + URLEncoder.encode( + System.getProperty("sip-communicator.version"), + "UTF-8")); + } + else if(s.equals("ipaddr")) + { + content.append("&ipaddr=" + URLEncoder.encode( + ipaddr.getHostAddress(), "UTF-8")); + } + else if(s.equals("hwaddr")) + { + String hwaddr = null; + + if(ipaddr != null) + { + /* find the hardware address of the interface + * that has this IP address + */ + Enumeration<NetworkInterface> en = + NetworkInterface.getNetworkInterfaces(); + + while(en.hasMoreElements()) + { + NetworkInterface iface = en.nextElement(); + + Enumeration<InetAddress> enInet = + iface.getInetAddresses(); + + while(enInet.hasMoreElements()) + { + InetAddress inet = enInet.nextElement(); + + if(inet.equals(ipaddr)) + { + byte hw[] = + getNetworkAddressManagerService(). + getHardwareAddress(iface); + StringBuffer buf = + new StringBuffer(); + + for(byte h : hw) + { + int hi = h >= 0 ? h : h + 256; + String t = new String( + (hi <= 0xf) ? "0" : ""); + t += Integer.toHexString(hi); + buf.append(t); + buf.append("-"); + } + + buf.deleteCharAt(buf.length() - 1); + + hwaddr = buf.toString(); + content.append("&hwaddr=" + + URLEncoder.encode( + hwaddr, "UTF-8")); + break; + } + } + } + } + } + } + } + + out.write(content.toString()); + out.flush(); + + int responseCode = ((HttpURLConnection)uc).getResponseCode(); + + if(responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) + { + AuthenticationWindow authWindow = new AuthenticationWindow( + u.getHost(), true, null); + + authWindow.setVisible(true); + + userCredentials = new UserCredentials(); + userCredentials.setUserName(authWindow.getUserName()); + userCredentials.setPassword(authWindow.getPassword()); + userCredentials.setPasswordPersistent( + authWindow.isRememberPassword()); + + if(userCredentials.getUserName() == null) + { + userCredentials = null; + } + else + { + tmpFile.delete(); + return retrieveConfigurationFile(url); + } + } + else if(responseCode == HttpURLConnection.HTTP_MOVED_PERM || + responseCode == HttpURLConnection.HTTP_MOVED_TEMP) + { + String loc = + ((HttpURLConnection)uc).getHeaderField("Location"); + + if(loc != null && (loc.startsWith("http://") || + loc.startsWith("https://"))) + { + tmpFile.delete(); + /* TODO detect loops */ + return retrieveConfigurationFile(loc); + } + } + else if(responseCode == HttpURLConnection.HTTP_OK) + { + if(userCredentials != null && + userCredentials.getUserName() != null && + userCredentials.isPasswordPersistent()) + { + // if save password is checked save the pass + getConfigurationService().setProperty( + PROPERTY_HTTP_USERNAME, + userCredentials.getUserName()); + getCredentialsStorageService().storePassword( + PROPERTY_HTTP_PASSWORD, + userCredentials.getPasswordAsString()); + } + + if(provCredentials != null && + provCredentials.getUserName() != null && + provCredentials.isPasswordPersistent()) + { + getConfigurationService().setProperty( + PROPERTY_PROVISIONING_USERNAME, + provCredentials.getUserName()); + getCredentialsStorageService().storePassword( + PROPERTY_PROVISIONING_PASSWORD, + provCredentials.getPasswordAsString()); + } + } + } + else + { + return null; + } + + InputStream in = uc.getInputStream(); + + // Chain a ProgressMonitorInputStream to the + // URLConnection's InputStream + final ProgressMonitorInputStream pin + = new ProgressMonitorInputStream(null, u.toString(), in); + + // Set the maximum value of the ProgressMonitor + ProgressMonitor pm = pin.getProgressMonitor(); + pm.setMaximum(uc.getContentLength()); + + final BufferedOutputStream bout + = new BufferedOutputStream(new FileOutputStream(temp)); + + try + { + int read = -1; + byte[] buff = new byte[1024]; + + while((read = pin.read(buff)) != -1) + { + bout.write(buff, 0, read); + } + + pin.close(); + bout.flush(); + bout.close(); + out.close(); + + return temp; + } + catch (Exception e) + { + logger.error("Error saving", e); + + try + { + pin.close(); + bout.close(); + out.close(); + } + catch (Exception e1) + { + } + + return null; + } + } + catch (Exception e) + { + if (logger.isInfoEnabled()) + logger.info("Error retrieving provisioning file!", e); + tmpFile.delete(); + return null; + } + } + + /** + * Update configuration with properties retrieved from provisioning URL. + * + * @param file provisioning file + */ + private void updateConfiguration(final File file) + { + Properties fileProps = new Properties(); + InputStream in = null; + + try + { + in = new BufferedInputStream(new FileInputStream(file)); + fileProps.load(in); + + Iterator<Map.Entry<Object, Object> > it + = fileProps.entrySet().iterator(); + + while(it.hasNext()) + { + Map.Entry<Object, Object> entry = it.next(); + + String key = (String)entry.getKey(); + Object value = entry.getValue(); + + if(value instanceof String) + { + if(((String)value).equals("${null}")) + { + getConfigurationService().removeProperty(key); + continue; + } + } + + /* password => credentials storage service */ + if(key.endsWith(".PASSWORD")) + { + getCredentialsStorageService().storePassword( + key.substring(0, key.lastIndexOf(".")), + (String)value); + } + else + { + getConfigurationService().setProperty(key, value); + } + } + + /* save the "new" configuration */ + getConfigurationService().storeConfiguration(); + try + { + getConfigurationService().reloadConfiguration(); + } + catch(Exception e) + { + logger.error("Cannot reload configuration"); + } + } + catch(IOException e) + { + logger.warn("Error during load of provisioning file"); + } + finally + { + try + { + in.close(); + file.delete(); + } + catch(IOException e) + { + } + } + } + + /** + * Return the certificate verification service impl. + * @return the CertificateVerification service. + */ + private static CertificateVerificationService + getCertificateVerificationService() + { + if(certVerification == null) + { + ServiceReference certVerifyReference + = bundleContext.getServiceReference( + CertificateVerificationService.class.getName()); + if(certVerifyReference != null) + certVerification + = (CertificateVerificationService)bundleContext.getService( + certVerifyReference); + } + + return certVerification; + } + + /** + * Returns a reference to a ConfigurationService implementation currently + * registered in the bundle context or null if no such implementation was + * found. + * + * @return a currently valid implementation of the ConfigurationService. + */ + public static ConfigurationService getConfigurationService() + { + if (configurationService == null) + { + ServiceReference confReference + = bundleContext.getServiceReference( + ConfigurationService.class.getName()); + configurationService + = (ConfigurationService)bundleContext.getService(confReference); + } + return configurationService; + } + + /** + * Returns a reference to a CredentialsStorageService implementation + * currently registered in the bundle context or null if no such + * implementation was found. + * + * @return a currently valid implementation of the + * CredentialsStorageService. + */ + public static CredentialsStorageService getCredentialsStorageService() + { + if (credentialsService == null) + { + ServiceReference credentialsReference + = bundleContext.getServiceReference( + CredentialsStorageService.class.getName()); + credentialsService + = (CredentialsStorageService) bundleContext + .getService(credentialsReference); + } + return credentialsService; + } + + /** + * Returns a reference to a NetworkAddressManagerService implementation + * currently registered in the bundle context or null if no such + * implementation was found. + * + * @return a currently valid implementation of the + * NetworkAddressManagerService. + */ + public static NetworkAddressManagerService getNetworkAddressManagerService() + { + if (netaddrService == null) + { + ServiceReference netaddrReference + = bundleContext.getServiceReference( + NetworkAddressManagerService.class.getName()); + netaddrService + = (NetworkAddressManagerService) bundleContext + .getService(netaddrReference); + } + return netaddrService; + } +} diff --git a/src/net/java/sip/communicator/plugin/provisioning/provisioning.manifest.mf b/src/net/java/sip/communicator/plugin/provisioning/provisioning.manifest.mf new file mode 100644 index 0000000..826ce8c --- /dev/null +++ b/src/net/java/sip/communicator/plugin/provisioning/provisioning.manifest.mf @@ -0,0 +1,17 @@ +Bundle-Activator: net.java.sip.communicator.plugin.provisioning.ProvisioningPluginActivator +Bundle-Name: ProvisioningPlugin +Bundle-Description: A bundle that implements the Provisioning Plugin Package. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +System-Bundle: yes +Import-Package: org.osgi.framework, + net.java.sip.communicator.service.configuration, + net.java.sip.communicator.service.credentialsstorage, + net.java.sip.communicator.service.protocol, + net.java.sip.communicator.service.provdisc, + net.java.sip.communicator.service.certificate, + net.java.sip.communicator.service.netaddr, + net.java.sip.communicator.util, + net.java.sip.communicator.util.swing, + javax.net.ssl, + javax.swing, diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java index 62d862d..49999c6 100644 --- a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java +++ b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java @@ -147,6 +147,7 @@ public abstract class MediaAwareCall< * also we can obtain the reason for the <tt>CallPeerChangeEvent</tt> if * any. Use the event as cause for the call state change event.. */ + @SuppressWarnings("unchecked") private void removeCallPeer(CallPeerChangeEvent evt) { T callPeer = (T)evt.getSourceCallPeer(); diff --git a/src/net/java/sip/communicator/service/provdisc/AbstractProvisioningDiscoveryService.java b/src/net/java/sip/communicator/service/provdisc/AbstractProvisioningDiscoveryService.java new file mode 100644 index 0000000..b5712fb --- /dev/null +++ b/src/net/java/sip/communicator/service/provdisc/AbstractProvisioningDiscoveryService.java @@ -0,0 +1,92 @@ +/* + * 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.provdisc; + +import java.util.*; + +import net.java.sip.communicator.service.provdisc.event.*; + +/** + * Abstract base class of ProvisioningDiscoveryService that ease implementation + * + * @author seb + * + */ +public abstract class AbstractProvisioningDiscoveryService + implements ProvisioningDiscoveryService +{ + /** + * List of <tt>ProvisioningListener</tt> that will be notified when + * a provisioning URL is retrieved. + */ + private List<DiscoveryListener> listeners = + new ArrayList<DiscoveryListener>(); + + /** + * Get the name of the method name used to retrieve provisioning URL. + * + * @return method name + */ + public abstract String getMethodName(); + + /** + * Launch a discovery for a provisioning URL. + * + * This method is asynchronous, the response will be notified to any + * <tt>ProvisioningListener</tt> registered. + */ + public abstract void startDiscovery(); + + /** + * Launch a discovery for a provisioning URL. This method is synchronous and + * may block for some time. + * + * @return provisioning URL + */ + public abstract String discoverURL(); + + /** + * Add a listener that will be notified when the + * <tt>discoverProvisioningURL</tt> has finished. + * + * @param listener <tt>ProvisioningListener</tt> to add + */ + public void addDiscoveryListener(DiscoveryListener listener) + { + if(!listeners.contains(listener)) + { + listeners.add(listener); + } + } + + /** + * Add a listener that will be notified when the + * <tt>discoverProvisioningURL</tt> has finished. + * + * @param listener <tt>ProvisioningListener</tt> to add + */ + public void removeDiscoveryListener(DiscoveryListener listener) + { + if(listeners.contains(listener)) + { + listeners.remove(listener); + } + } + + /** + * Notify all listeners about a <tt>DiscoveryEvent</tt>. + * + * @param event <tt>DiscoveryEvent</tt> that contains provisioning URL + */ + public void fireDiscoveryEvent(DiscoveryEvent event) + { + for(DiscoveryListener listener : listeners) + { + listener.notifyProvisioningURL(event); + } + } +} diff --git a/src/net/java/sip/communicator/service/provdisc/ProvisioningDiscoveryService.java b/src/net/java/sip/communicator/service/provdisc/ProvisioningDiscoveryService.java new file mode 100644 index 0000000..6f35a06 --- /dev/null +++ b/src/net/java/sip/communicator/service/provdisc/ProvisioningDiscoveryService.java @@ -0,0 +1,58 @@ +/* + * 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.provdisc; + +import net.java.sip.communicator.service.provdisc.event.*; + +/** + * Service that allow to retrieve a provisioning URL to configure + * SIP Communicator. Implementations (not exhaustive) could use DHCP, + * DNS (A, AAAA, SRV, TXT) or mDNS (Bonjour). + * + * @author Sebastien Vincent + */ +public interface ProvisioningDiscoveryService +{ + /** + * Get the name of the method name used to retrieve provisioning URL. + * + * @return method name + */ + public String getMethodName(); + + /** + * Launch a discovery for a provisioning URL. + * + * This method is asynchronous, the response will be notified to any + * <tt>ProvisioningListener</tt> registered. + */ + public void startDiscovery(); + + /** + * Launch a discovery for a provisioning URL. This method is synchronous and + * may block for some time. + * + * @return provisioning URL + */ + public String discoverURL(); + + /** + * Add a listener that will be notified when the + * <tt>startDiscovery</tt> has finished. + * + * @param listener <tt>ProvisioningListener</tt> to add + */ + public void addDiscoveryListener(DiscoveryListener listener); + + /** + * Add a listener that will be notified when the + * <tt>discoverProvisioningURL</tt> has finished. + * + * @param listener <tt>ProvisioningListener</tt> to add + */ + public void removeDiscoveryListener(DiscoveryListener listener); +} diff --git a/src/net/java/sip/communicator/service/provdisc/event/DiscoveryEvent.java b/src/net/java/sip/communicator/service/provdisc/event/DiscoveryEvent.java new file mode 100644 index 0000000..0ab9001 --- /dev/null +++ b/src/net/java/sip/communicator/service/provdisc/event/DiscoveryEvent.java @@ -0,0 +1,49 @@ +/* + * 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.provdisc.event; + +import java.util.*; + +/** + * Event representing that a provisioning URL has been retrieved. + * + * @author Sebastien Vincent + */ +public class DiscoveryEvent extends EventObject +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * Provisioning URL. + */ + private String url = null; + + /** + * Constructor. + * + * @param source object that have created this event + * @param url provisioning URL + */ + public DiscoveryEvent(Object source, String url) + { + super(source); + this.url = url; + } + + /** + * Get the provisioning URL. + * + * @return provisioning URL + */ + public String getProvisioningURL() + { + return url; + } +} diff --git a/src/net/java/sip/communicator/service/provdisc/event/DiscoveryListener.java b/src/net/java/sip/communicator/service/provdisc/event/DiscoveryListener.java new file mode 100644 index 0000000..ef136b9 --- /dev/null +++ b/src/net/java/sip/communicator/service/provdisc/event/DiscoveryListener.java @@ -0,0 +1,25 @@ +/* + * 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.provdisc.event; + +import java.util.*; + +/** + * Listener that will be notified when a provisioning URL is retrieved by the + * <tt>ProvisioningDiscoveryService</tt>. + * + * @author Sebastien Vincent + */ +public interface DiscoveryListener extends EventListener +{ + /** + * Notify the provisioning URL. + * + * @param event provisioning event + */ + public void notifyProvisioningURL(DiscoveryEvent event); +} diff --git a/src/net/java/sip/communicator/service/provdisc/provdisc.manifest.mf b/src/net/java/sip/communicator/service/provdisc/provdisc.manifest.mf new file mode 100644 index 0000000..69e9d6c --- /dev/null +++ b/src/net/java/sip/communicator/service/provdisc/provdisc.manifest.mf @@ -0,0 +1,7 @@ +Bundle-Name: Provisioning Discovery Service +Bundle-Description: Provisioning Discovery Service. +Bundle-Vendor: sip-communicator.org +Bundle-Version: 0.0.1 +System-Bundle: yes +Export-Package: net.java.sip.communicator.service.provdisc, + net.java.sip.communicator.service.provdisc.event
\ No newline at end of file diff --git a/src/net/java/sip/communicator/util/swing/AuthenticationWindow.java b/src/net/java/sip/communicator/util/swing/AuthenticationWindow.java index 46e112e..b6533de 100644 --- a/src/net/java/sip/communicator/util/swing/AuthenticationWindow.java +++ b/src/net/java/sip/communicator/util/swing/AuthenticationWindow.java @@ -126,7 +126,12 @@ public class AuthenticationWindow { this.server = server; - Image logoImage = icon.getImage(); + Image logoImage = null; + + if(icon != null) + { + logoImage = icon.getImage(); + } if(!isUserNameEditable) this.uinValue = new JLabel(); |