diff options
author | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2017-03-11 22:15:03 +0100 |
---|---|---|
committer | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2017-03-11 22:15:03 +0100 |
commit | 85901329b0794b136b96bf745f4ab1572806fc89 (patch) | |
tree | f23da7e97cae727f39d825f0fef8348cffb238e4 /src/net/java/sip/communicator/impl/protocol/zeroconf | |
parent | 3db2e44f186c59429901b2c899e139ea60117a55 (diff) | |
parent | cf5da997da8820b4050f5b87ee9440a0ede36d1f (diff) | |
download | jitsi-master.zip jitsi-master.tar.gz jitsi-master.tar.bz2 |
Signed-off-by: Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
Diffstat (limited to 'src/net/java/sip/communicator/impl/protocol/zeroconf')
30 files changed, 0 insertions, 11448 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/BonjourService.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/BonjourService.java deleted file mode 100644 index 8258da7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/BonjourService.java +++ /dev/null @@ -1,706 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.io.*; -import java.net.*; -import java.util.*; - -import net.java.sip.communicator.impl.protocol.zeroconf.jmdns.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * Class dealing with JmDNS and treating all the - * incoming connections on the bonjour port - * @author Christian Vincenot - */ -public class BonjourService extends Thread - implements ServiceListener, - DNSListener -{ - private static final Logger logger = - Logger.getLogger(BonjourService.class); - - private int port = 5298; - private ServerSocket sock = null; - private String id = null; - private JmDNS jmdns=null; - private final Map<String, Object> props = new Hashtable<String, Object>(); - private ServiceInfo service = null; - private boolean dead = false; - - private final List<ContactZeroconfImpl> contacts - = new Vector<ContactZeroconfImpl>(); - - private ProtocolProviderServiceZeroconfImpl pps; - OperationSetPersistentPresenceZeroconfImpl opSetPersPresence; - - private ZeroconfAccountID acc; - - /* Should maybe better get the status directly from OperationSetPresence */ - private PresenceStatus status = ZeroconfStatusEnum.OFFLINE; - - /** - * Returns the corresponding ProtocolProviderService - * @return corresponding ProtocolProviderService - */ - public ProtocolProviderServiceZeroconfImpl getPPS() - { - return pps; - } - - /** - * Returns the id of this service. - * @return returns the id of this service. - */ - String getID() - { - return id; - } - - /** - * Creates a new instance of the Bonjour service thread - * @param port TCP Port number on which to try to start the Bonjour service - * @param pps ProtocolProviderService instance - * which is creating this BonjourService - */ - public BonjourService(int port, - ProtocolProviderServiceZeroconfImpl pps) - { - this.acc = (ZeroconfAccountID) pps.getAccountID(); - this.port = port; - this.id = acc.getUserID(); - this.pps = pps; - - opSetPersPresence = - (OperationSetPersistentPresenceZeroconfImpl) pps - .getOperationSet(OperationSetPersistentPresence.class); - - // Gaim - props.put("1st", (acc.getFirst() == null)? "":acc.getFirst()); - props.put("email", (acc.getMail() == null)? "":acc.getMail()); - props.put("jid", this.id); - props.put("last", (acc.getLast() == null)?"":acc.getLast()); - props.put("msg", opSetPersPresence.getCurrentStatusMessage()); - props.put("status", "avail"); - - //iChat - props.put("phsh","000"); - //props.put("status","avail"); - //props.put("port.p2pj", "5298"); - props.put("vc", "C!"); - //props.put("1st", "John"); - props.put("txtvers","1"); - - //XEP-0174 (Final paper) - props.put("ext",""); - props.put("nick", (acc.getFirst() == null)? this.id:acc.getFirst()); - props.put("ver", "1"); - props.put("node", "SIP Communicator"); - - //Ours - props.put("client", "SIP Communicator"); - - changeStatus(opSetPersPresence.getPresenceStatus()); - - sock = createSocket(port); - if (sock == null) - return; - - port = sock.getLocalPort(); - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: ServerSocket bound to port "+port); - - props.put("port.p2pj", Integer.toString(port)); - this.setDaemon(true); - this.start(); - } - - /* TODO: Better exception checking to avoid sudden exit and bonjour - * service shutdown */ - - /** - * Walk? - */ - @Override - public void run() - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Bonjour Service Thread up and running!"); - - /* Put jmDNS in DEBUD Mode : - * Following verbosity levels can be chosen : - * "INFO" , "WARNING", "SEVERE", "ALL", "FINE", "FINER", "FINEST", etc - */ - //System.setProperty("jmdns.debug", "0"); - - while (dead == false) - { - if (sock == null || sock.isClosed()) - { - sock = createSocket(port); - /* What should we do now? TEMPORARY: shutdown()*/ - if (sock == null) shutdown(); - port = sock.getLocalPort(); - props.put("port.p2pj", Integer.toString(port)); - //TODO: update JmDNS in case the port had to be changed! - } - try - { - Socket connection = sock.accept(); - ContactZeroconfImpl contact = getContact(null, - connection.getInetAddress()); - /*if (status.equals(ZeroconfStatusEnum.OFFLINE) - || status.equals(ZeroconfStatusEnum.INVISIBLE) */ - if (dead == true) break; - - if ((contact == null) - || (contact.getClientThread() != null)) - { - if (contact == null) - logger.error("ZEROCONF: Connexion from " - + "unknown contact [" - + connection.getInetAddress() - +"]. REJECTING!"); - else if (contact.getClientThread() == null) - logger.error("ZEROCONF: Redundant chat " - + "channel [" - + contact - +"]. REJECTING!"); - connection.close(); - } - else new ClientThread(connection, this); - } - catch(Exception e) - { - logger.error(e); - } - } - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Going Offline - " - +"BonjourService Thread exiting!"); - } - - /** - * Might be used for shutdown... - */ - public void shutdown() - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Shutdown!"); - - dead = true; - try - { sock.close(); } - catch (Exception ex) - { logger.error(ex); } - - changeStatus(ZeroconfStatusEnum.OFFLINE); - if(jmdns != null) - jmdns.close(); - } - - private ServerSocket createSocket(int port) - { - ServerSocket sock=null; - try - { - sock = new ServerSocket(port); - } - catch(Exception e) - { - logger.error("ZEROCONF: Couldn't bind socket to port " - +port+"! Switching to an other port..."); - try - { - sock = new ServerSocket(0); - } - catch (IOException ex) - { - logger.error("ZEROCONF: FATAL ERROR => " - +"Couldn't bind to a port!!", ex); - } - } - - return sock; - } - - /** - * Changes the status of the local user. - * @param stat New presence status - */ - public void changeStatus(PresenceStatus stat) - { - /* [old_status == new_status ?] => NOP */ - if (stat.equals(status)) - return; - - /* [new_status == OFFLINE ?] => clean up everything */ - if (stat.equals(ZeroconfStatusEnum.OFFLINE)) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Going OFFLINE"); - //jmdns.unregisterAllServices(); - jmdns.removeServiceListener("_presence._tcp.local.", this); - jmdns.close(); - jmdns=null; - //dead = true; - - // Erase all contacts by putting them OFFLINE - opSetPersPresence.changePresenceStatusForAllContacts( - opSetPersPresence.getServerStoredContactListRoot(), stat); - - try - { - sleep(1000); - } catch (InterruptedException ex) - { - logger.error(ex); - } - } - - /* [old_status == OFFLINE ?] => register service */ - else if (status.equals(ZeroconfStatusEnum.OFFLINE)) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Getting out of OFFLINE state"); - props.put("status", stat.getStatusName()); - service = new ServiceInfo("_presence._tcp.local.", id, - port, 0, 0, props); - - try - { - jmdns = new JmDNS(); - jmdns.registerServiceType("_presence._tcp.local."); - jmdns.addServiceListener("_presence._tcp.local.", this); - jmdns.registerService(service); - - /* In case the ID had to be changed */ - id = service.getName(); - } - catch (Exception ex) - { logger.error(ex); } - - //dead = false; - - /* Normal status change */ - } - else - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF : Changing status"); - - props.put("status", stat.getStatusName()); - - /* FIXME: Not totally race condition free since the 3 calls aren't - * atomic, but that's not really critical since there's little - * change chance of concurrent local contact change, and this - * wouldn't have big consequences. - */ - ServiceInfo info = - jmdns.getLocalService(id.toLowerCase()+"._presence._tcp.local."); - if (info == null) - logger.error("ZEROCONF/JMDNS: PROBLEM GETTING " - +"LOCAL SERVICEINFO !!"); - - byte[] old = info.getTextBytes(); - info.setProps(props); - jmdns.updateInfos(info, old); - } - - status = stat; - } - - private class AddThread extends Thread - { - private String type, name; - public AddThread(String type, String name) - { - this.setDaemon(true); - this.type = type; - this.name = name; - this.start(); - } - - @Override - public void run() - { - ServiceInfo service = null; - while ((service == null) && (dead == false) - && !status.equals(ZeroconfStatusEnum.OFFLINE)) - { - service = jmdns.getServiceInfo(type, name, 10000); - if (service == null) - logger.error("BONJOUR: ERROR - Service Info of " - + name +" not found in cache!!"); - try - { - sleep(2); - } - catch (InterruptedException ex) - { - logger.error(ex); - } - } - if ((dead == false) && !status.equals(ZeroconfStatusEnum.OFFLINE)) - jmdns.requestServiceInfo(type, name); - //} else handleResolvedService(name, type, service); - } - } - - /* Service Listener Implementation */ - - /** - * A service has been added. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - public void serviceAdded(ServiceEvent event) - { - /* WARNING: DONT PUT ANY BLOCKING CALLS OR FLAWED LOOPS IN THIS METHOD. - * JmDNS calls this method without creating a new thread, so if this - * method doesn't return, jmDNS will hang !! - */ - - String name = event.getName(); - String type = event.getType(); - - if (name.equals(id)) - return; - - if (logger.isDebugEnabled()) - logger.debug("BONJOUR: "+name - +"["+type+"] detected! Trying to get information..."); - try - { - sleep(2); - } - catch (InterruptedException ex) - { - logger.error(ex); - } - - jmdns.printServices(); - - new AddThread(type, name); - } - - - - /** - * A service has been removed. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - public void serviceRemoved(ServiceEvent event) - { - String name = event.getName(); - if (name.equals(id)) - return; - - ContactZeroconfImpl contact = getContact(name, null); - - if(contact == null) - return; - - opSetPersPresence.changePresenceStatusForContact(contact, - ZeroconfStatusEnum.OFFLINE); - if (logger.isDebugEnabled()) - logger.debug("BONJOUR: Received announcement that " - +name+" went offline!"); - - } - - /** - * A service has been resolved. Its details are now available in the - * ServiceInfo record. - * - * @param event The ServiceEvent providing the name, the fully qualified - * type of the service, and the service info record, - * or null if the service could not be resolved. - */ - public void serviceResolved(ServiceEvent event) - { - String contactID = event.getName(); - String type = event.getType(); - ServiceInfo info = event.getInfo(); - - if (logger.isDebugEnabled()) - logger.debug("BONJOUR: Information about " - +contactID+" discovered"); - - handleResolvedService(contactID, type, info); - } - - private void handleResolvedService(String contactID, - String type, - ServiceInfo info) - { - if (contactID.equals(id)) - return; - - if (info.getAddress().toString().length() > 15) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Temporarily ignoring IPv6 addresses!"); - return; - } - - ContactZeroconfImpl newFriend; - - synchronized(this) - { - if (getContact(contactID, info.getAddress()) != null) - { - if (logger.isDebugEnabled()) - logger.debug("Contact " - +contactID+" already in contact list! Skipping."); - return; - }; - if (logger.isDebugEnabled()) - logger.debug("ZEROCNF: ContactID " + contactID + - " Address " + info.getAddress()); - - if (logger.isDebugEnabled()) - logger.debug(" Address=>"+info.getAddress() - +":"+info.getPort()); - - for (Iterator<String> names = info.getPropertyNames(); - names.hasNext();) - { - String prop = names.next(); - if (logger.isDebugEnabled()) - logger.debug(" "+prop+"=>" - +info.getPropertyString(prop)); - } - - /* Creating the contact */ - String name = info.getPropertyString("1st"); - if (info.getPropertyString("last") != null) - name += " "+ info.getPropertyString("last"); - - int port = Integer.valueOf( - info.getPropertyString("port.p2pj")).intValue(); - - if (port < 1) - { - logger.error("ZEROCONF: Flawed contact announced himself" - +"without necessary parameters : "+contactID); - return; - } - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Detected client "+name); - - newFriend = - opSetPersPresence.createVolatileContact( - contactID, this, name, - info.getAddress(), port); - } - /* Try to detect which client type it is */ - int clientType = ContactZeroconfImpl.XMPP; - if (info.getPropertyString("client") != null - && info.getPropertyString("client"). - compareToIgnoreCase("SIP Communicator") == 0) - clientType = ContactZeroconfImpl.SIPCOM; - - else if ((info.getPropertyString("jid") != null) - && (info.getPropertyString("node") == null)) - clientType = ContactZeroconfImpl.GAIM; - - else if (info.getPropertyString("jid") == null) - clientType = ContactZeroconfImpl.ICHAT; - - newFriend.setClientType(clientType); - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: CLIENT TYPE "+clientType); - - ZeroconfStatusEnum status = - ZeroconfStatusEnum.statusOf(info.getPropertyString("status")); - opSetPersPresence. - changePresenceStatusForContact(newFriend, - status == null?ZeroconfStatusEnum.ONLINE:status); - - // Listening for changes - jmdns.addListener(this, new DNSQuestion(info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_UNIQUE)); - } - - /** - * Callback called by JmDNS to inform the - * BonjourService of a potential status change of some contacts. - * @param jmdns JmDNS instance responsible for this - * @param now Timestamp - * @param record DNSRecord which changed - */ - public synchronized void updateRecord( JmDNS jmdns, - long now, - DNSRecord record) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF/JMDNS: Received record update for "+record); - - int clazz = record.getClazz(); - int type = record.getType(); - - /* Check the info returned by JmDNS since we can't really trust its - * filtering. */ - if (!(((type & DNSConstants.TYPE_TXT) != 0) && - ((clazz & DNSConstants.CLASS_IN) != 0) && - record.isUnique() && - record.getName().endsWith("_presence._tcp.local."))) - return; - - String name = record.getName().replaceAll("._presence._tcp.local.",""); - ContactZeroconfImpl contact; - - synchronized(this) - { - contact = getContact(name, null); - - if (contact == null) { //return; - logger.error("ZEROCONF: BUG in jmDNS => Received update without " - +"previous contact annoucement. Trying to add contact"); - new AddThread("_presence._tcp.local.", name); - return; - } - } - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: "+ name - + " changed status. Requesting fresh data!"); - - /* Since a record was updated, we can be sure that we can do a blocking - * getServiceInfo without risk. (Still, we use the method with timeout - * to avoid bad surprises). If some problems of status change refresh - * appear, we'll have to fall back on the method with callback as we've - * done for "ServiceAdded". - */ - - ServiceInfo info = jmdns.getServiceInfo("_presence._tcp.local.", name, - 1000); - if (info == null) - { - logger.error("ZEROCONF/JMDNS: Problem!! The service " - +"information was not in cache. See comment in " - +"BonjourService.java:updateRecord !!"); - return; - } - - /* Let's change what we can : status, message, etc */ - ZeroconfStatusEnum status = - ZeroconfStatusEnum.statusOf(info.getPropertyString("status")); - - opSetPersPresence. - changePresenceStatusForContact(contact, - status == null ? ZeroconfStatusEnum.ONLINE:status); - - } - - /** - * Returns an Iterator over all contacts. - * - * @return a java.util.Iterator over all contacts - */ - public Iterator<ContactZeroconfImpl> contacts() - { - return contacts.iterator(); - } - - /** - * Adds a contact to the locally stored list of contacts - * @param contact Zeroconf Contact to add to the local list - */ - public void addContact(ContactZeroconfImpl contact) - { - if (contact == null) - throw new IllegalArgumentException("contact"); - - synchronized(contacts) - { - contacts.add(contact); - } - } - /** - * Returns the <tt>Contact</tt> with the specified identifier or IP address. - * - * @param id the identifier of the <tt>Contact</tt> we are - * looking for. - * @param ip the IP address of the <tt>Contact</tt> we are looking for. - * @return the <tt>Contact</tt> with the specified id or address. - */ - public ContactZeroconfImpl getContact(String id, InetAddress ip) - { - if (id == null && ip == null) return null; - - synchronized(contacts) - { - Iterator<ContactZeroconfImpl> contactsIter = contacts(); - - while (contactsIter.hasNext()) - { - ContactZeroconfImpl contact = contactsIter.next(); - //System.out.println("ZEROCNF: Comparing "+id+ " "+ip+ - //" with "+ contact.getAddress()+ " " + contact.getIpAddress()); - if (((contact.getAddress().equals(id)) || (id == null)) - && ((contact.getIpAddress().equals(ip)) || (ip == null))) - return contact; - - } - } - //System.out.println("ZEROCNF: ERROR - " + - //"Couldn't find contact to get ["+id+" / "+ip+"]"); - return null; - } - - /** - * Removes the <tt>Contact</tt> with the specified identifier or IP address. - * - * - * @param id the identifier of the <tt>Contact</tt> we are - * looking for. - * @param ip the IP address of the <tt>Contact</tt> we are looking for. - */ - public void removeContact(String id, InetAddress ip) - { - synchronized(contacts) - { - Iterator<ContactZeroconfImpl> contactsIter = contacts(); - while (contactsIter.hasNext()) - { - ContactZeroconfImpl contact = contactsIter.next(); - if (((contact.getAddress().equals(id)) || (id == null)) - &&((contact.getIpAddress().equals(ip)) || (ip == null))) - { - if (contact.getClientThread() != null) - contact.getClientThread().cleanThread(); - contacts.remove(contact); - return; - } - }; - } - logger.error( - "ZEROCONF: ERROR - Couldn't find contact to delete ["+id+" / "+ip+"]"); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ClientThread.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ClientThread.java deleted file mode 100644 index 4367eed..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ClientThread.java +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.io.*; -import java.net.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * Class creating a thread responsible for handling the chat - * with the remote user on the other end of the socket - * - * @author Christian Vincenot - */ -public class ClientThread - extends Thread -{ - private static final Logger logger = Logger.getLogger(ClientThread.class); - - private OperationSetBasicInstantMessagingZeroconfImpl opSetBasicIM; - private OperationSetTypingNotificationsZeroconfImpl opSetTyping; - private Socket sock; - private InetAddress remoteIPAddress; - private OutputStream out; - private DataInputStream in; - private BonjourService bonjourService; - private ContactZeroconfImpl contact=null; - private boolean streamState = false; - - private String messagesQueue=null; - - /** - * Sets the contact with which we're chatting in this ClientThread - * @param contact Zeroconf contact with which we're chatting - */ - protected void setContact(ContactZeroconfImpl contact) - { - this.contact = contact; - } - - /** - * Set the stream as opened. This means that the - * conversation with the client is really opened - * from now on (the XML greetings are over) - */ - protected void setStreamOpen() - { - synchronized(this) - { - this.streamState = true; - } - } - - /** - * Says if the stream between the local user and the remote user - * is in an opened state (greetings are over and we can chat) - * @return Returns true if the stream is "opened" (ie, ready for chat) - */ - protected boolean isStreamOpened() - { - synchronized(this) - { - return this.streamState; - } - } - - /** - * Creates a new instance of ClientThread reponsible - * for handling the conversation with the remote user. - * @param sock Socket created for chatting - * @param bonjourService BonjourService which spawned this ClientThread - */ - public ClientThread(Socket sock, BonjourService bonjourService) - { - this.sock = sock; - this.remoteIPAddress = sock.getInetAddress(); - this.bonjourService = bonjourService; - this.opSetBasicIM = - (OperationSetBasicInstantMessagingZeroconfImpl) bonjourService - .getPPS().getOperationSet( - OperationSetBasicInstantMessaging.class); - - this.opSetTyping = - (OperationSetTypingNotificationsZeroconfImpl) bonjourService - .getPPS() - .getOperationSet(OperationSetTypingNotifications.class); - this.setDaemon(true); - - try - { - out = sock.getOutputStream(); - in = new DataInputStream(sock.getInputStream()); - } - catch (IOException e) - { - logger.error("Creating ClientThread: Couldn't get I/O for " - +"the connection", e); - //System.exit(1); - return; - } - - this.start(); - } - - /* - * Read a message from the socket. - * TODO: clean the code a bit and optimize it. - */ - private String readMessage() - { - String line; - byte[] bytes = new byte[10]; - - try - { - int i=0; - - while (i < 9) - { - i += in.read(bytes,0,9-i); - } - - line = new String(bytes); - bytes = new byte[1]; - if ((line.getBytes())[0] == '\n') - line = line.substring(1); - - if (line.startsWith("<message")) - { - while (true) - { - bytes[0] = in.readByte(); - line += new String(bytes); - - if ((line.endsWith("</message>")) - || (line.endsWith("stream>"))) - return line; - } - } - else - { - while (true) - { - bytes[0] = in.readByte(); - line += new String(bytes); - if ( ">".compareTo(new String(bytes)) == 0 ) - return line; - } - } - } - catch (IOException e) - { - logger.error("Couldn't get I/O for the connection", e); - //System.exit(1); - } - - return null; - } - - /* - * Parse the payload and extract the information. - * TODO: If needed, fill in the remaining fields of MessageZeroconfImpl - * like the baloon icon color, color/size/font of the text. - */ - private MessageZeroconfImpl parseMessage(String str) - { - if (str.startsWith("<?xml") || str.startsWith("<stream")) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.STREAM_OPEN); - - if (str.endsWith("stream>")) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.STREAM_CLOSE); - - if ((str.indexOf("<delivered/>") > 0) && (str.indexOf("<body>") < 0)) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.DELIVERED); - - if (!str.startsWith("<message")) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.UNDEF); - - /* TODO: Parse Enconding (& contact id to be able to double-check - * the source of a message) - * - * TODO: Check that the fields are outside of <body>..</body> - */ - - if ((str.indexOf("<body>") < 0) || (str.indexOf("</body>") < 0)) - return new MessageZeroconfImpl - (null, null, MessageZeroconfImpl.UNDEF); - - String temp = - str.substring(str.indexOf("<body>")+6, str.indexOf("</body>")); - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: received message ["+temp+"]"); - - int messageType = MessageZeroconfImpl.MESSAGE; - - if ((str.indexOf("<id>") >= 0) && (str.indexOf("</id>") >= 0)) - messageType = MessageZeroconfImpl.TYPING; - - MessageZeroconfImpl msg = - new MessageZeroconfImpl( - temp, - null, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE, - messageType); - - return msg; - } - - private int handleMessage(MessageZeroconfImpl msg) - { - - switch(msg.getType()) - { - /* STREAM INIT */ - case MessageZeroconfImpl.STREAM_OPEN: - if (contact == null) - contact = bonjourService.getContact(null, remoteIPAddress); - if (!isStreamOpened()) - { - sendHello(); - setStreamOpen(); - } - if (messagesQueue != null) - { - write(messagesQueue); - messagesQueue = null; - } - break; - - /* ACK */ - case MessageZeroconfImpl.DELIVERED : break; - - /* NORMAL MESSAGE */ - case MessageZeroconfImpl.MESSAGE: - if (!isStreamOpened()) - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: client on the other side " - +"isn't polite (sending messages without " - +"saying hello :P"); - if (contact == null) - //TODO: Parse contact id to double-check - contact = bonjourService.getContact(null, remoteIPAddress); - - /* TODO: If we want to implement invisible status, we'll have to - * make this test less restrictive to handle messages from - * unannounced clients. - */ - if (contact == null) - { - logger.error("ZEROCONF: ERROR - Couldn't identify " - +"contact. Closing socket."); - return -1; - } - else if (contact.getClientThread() == null) - contact.setClientThread(this); - - opSetBasicIM.fireMessageReceived(msg, contact); - - opSetTyping.fireTypingNotificationsEvent(contact, - OperationSetTypingNotificationsZeroconfImpl.STATE_STOPPED); - break; - - case MessageZeroconfImpl.TYPING: - if (!isStreamOpened()) - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: client on the other side " - +"isn't polite (sending messages without " - +"saying hello :P"); - if (contact == null) - //TODO: Parse contact id to double-check - contact = bonjourService.getContact(null, remoteIPAddress); - opSetTyping.fireTypingNotificationsEvent(contact, - OperationSetTypingNotificationsZeroconfImpl.STATE_TYPING); - - /* TODO: code a private runnable class to be used as timeout - * to set the typing state to STATE_PAUSED when a few seconds - * without news have passed. - */ - - break; - - case MessageZeroconfImpl.STREAM_CLOSE: - sendBye(); - contact.setClientThread(null); - return 1; - - case MessageZeroconfImpl.UNDEF: - logger.error("ZEROCONF: received strange message. SKIPPING!"); - break; - } - - //System.out.println("RECEIVED MESSAGE "+ msg.getContent()+ - //" from "+contact.getAddress() + "!!!!!!!!!!!!!!"); - return 0; - } - - - private void write(String string) - { - //System.out.println("Writing " + string + "!!!!!!!!!"); - byte[] bytes = string.getBytes(); - try - { - out.write(bytes); - out.flush(); - } - catch (IOException e) - { - logger.error("Couldn't get I/O for the connection"); - if (contact != null) - { - contact.setClientThread(null); - } - - try - { - sock.close(); - } - catch (IOException ex) - { - logger.error(ex); - } - - } - } - - /** - * Say hello :) - */ - protected void sendHello() - { - switch(contact.getClientType()) - { - case ContactZeroconfImpl.GAIM: - case ContactZeroconfImpl.ICHAT: - case ContactZeroconfImpl.SIPCOM: - write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"); - write("<stream:stream xmlns=\"jabber:client\" " - +"xmlns:stream=\"http://etherx.jabber.org/streams\">"); - break; - case ContactZeroconfImpl.XMPP: - write("<stream:stream" - +"xmlns='jabber:client'" - +"xmlns:stream='http://etherx.jabber.org/streams'" - +"from='"+bonjourService.getID()+"'" - +"to='"+contact.getAddress()+"'" - +"version='1.0'>\n"); - break; - } - - /* Legacy: OLD XMPP (XEP-0174 Draft) */ - //write("<stream:stream to='"+sock.getInetAddress().getHostAddress() - //+"' xmlns='jabber:client' stream='http://etherx.jabber.org/streams'>"); - } - - private void sendBye() - { - write("</stream:stream>\n"); - } - - private String toXHTML(MessageZeroconfImpl msg) - { - switch(contact.getClientType()) - { - case ContactZeroconfImpl.XMPP: - return new String("<message to='" - +contact.getAddress()+"' from='" - +bonjourService.getID()+"'>" - + "<body>"+msg.getContent()+"</body>" - + "</message>\n"); - - case ContactZeroconfImpl.SIPCOM: - - case ContactZeroconfImpl.ICHAT: - return new String( - "<message to='"+sock.getInetAddress().getHostAddress() - +"' type='chat' id='"+bonjourService.getID()+"'>" - + "<body>"+msg.getContent()+"</body>" - + "<html xmlns='http://www.w3.org/1999/xhtml'>" - + "<body ichatballooncolor='#7BB5EE' " - + "ichattextcolor='#000000'>" - + "<font face='Helvetica' ABSZ='12' color='#000000'>" - + msg.getContent() - + "</font>" - + "</body>" - + "</html>" - + "<x xmlns='jabber:x:event'>" - + "<offline/>" - + "<delivered/>" - + "<composing/>" - + (msg.getType()==MessageZeroconfImpl.TYPING?"<id></id>":"") - + "</x>" - + "</message>"); - - case ContactZeroconfImpl.GAIM: - default: - return new String( - "<message to='"+contact.getAddress() - +"' from='"+bonjourService.getID() - + "' type='chat'><body>"+msg.getContent()+"</body>" - + "<html xmlns='http://www.w3.org/1999/xhtml'><body><font>" - + msg.getContent() - + "</font></body></html><x xmlns='jabber:x:event'><composing/>" - + (msg.getType()==MessageZeroconfImpl.TYPING?"<id></id>":"") - + "</x></message>\n"); - } - } - - - /** - * Send a message to the remote user - * @param msg Message to send - */ - public void sendMessage(MessageZeroconfImpl msg) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Sending messag [" - +msg.getContent()+"] to " - + contact.getDisplayName()); - if (!isStreamOpened()) - { - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Stream not opened... " - +"will send the message later"); - messagesQueue += toXHTML(msg); - } - else write(toXHTML(msg)); - } - - /** - * Walk? - */ - @Override - public void run() - { - if (logger.isDebugEnabled()) - logger.debug("Bonjour: NEW CONNEXION from " - + sock.getInetAddress().getCanonicalHostName() - +" / "+sock.getInetAddress().getHostAddress()); - String input; - MessageZeroconfImpl msg=null; - - - input = readMessage(); - msg = parseMessage(input); - - while (handleMessage(msg) == 0 && !sock.isClosed()) - { - input = readMessage(); - msg = parseMessage(input); - } - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF : OUT OF LOOP !! Closed chat."); - cleanThread(); - } - - /** - * Clean-up the thread to exit - */ - public void cleanThread() - { - /* I wonder if that's ok... */ - if (sock != null && sock.isClosed() == false) - { - sendBye(); - try - { - sock.close(); - } - catch (IOException ex) - { - logger.error(ex); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactGroupZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactGroupZeroconfImpl.java deleted file mode 100644 index 568e087..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactGroupZeroconfImpl.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * A simple, straightforward implementation of a zeroconf ContactGroup. Since - * the Zeroconf protocol, we simply store all group details - * in class fields. You should know that when implementing a real protocol, - * the contact group implementation would rather encapsulate group objects from - * the protocol stack and group property values should be returned by consulting - * the encapsulated object. - * - * @author Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - */ -public class ContactGroupZeroconfImpl - implements ContactGroup -{ - - /** - * The name of this Zeroconf contact group. - */ - private String groupName = null; - - /** - * The list of this group's members. - */ - private Vector<Contact> contacts = new Vector<Contact>(); - - /** - * The list of sub groups belonging to this group. - */ - private Vector<ContactGroup> subGroups = new Vector<ContactGroup>(); - - /** - * The group that this group belongs to (or null if this is the root group). - */ - private ContactGroupZeroconfImpl parentGroup = null; - - /** - * Determines whether this group is really in the contact list or whether - * it is here only temporarily and will be gone next time we restart. - */ - private boolean isPersistent = false; - - /** - * The protocol provider that created us. - */ - private ProtocolProviderServiceZeroconfImpl parentProvider = null; - - /** - * Determines whether this group has been resolved on the server. - * Unresolved groups are groups that were available on previous runs and - * that the meta contact list has stored. During all next runs, when - * bootstrapping, the meta contact list would create these groups as - * unresolved. Once a protocol provider implementation confirms that the - * groups are still on the server, it would issue an event indicating that - * the groups are now resolved. - */ - private boolean isResolved = true; - - /** - * An id uniquely identifying the group. For many protocols this could be - * the group name itself. - */ - private String uid = null; - private static final String UID_SUFFIX = ".uid"; - - /** - * Creates a ContactGroupZeroconfImpl with the specified name. - * - * @param groupName the name of the group. - * @param parentProvider the protocol provider that created this group. - */ - public ContactGroupZeroconfImpl( - String groupName, - ProtocolProviderServiceZeroconfImpl parentProvider) - { - this.groupName = groupName; - this.uid = groupName + UID_SUFFIX; - this.parentProvider = parentProvider; - } - - /** - * Determines whether the group may contain subgroups or not. - * - * @return always true in this implementation. - */ - public boolean canContainSubgroups() - { - return true; - } - - /** - * Returns the protocol provider that this group belongs to. - * @return a regerence to the ProtocolProviderService instance that this - * ContactGroup belongs to. - */ - public ProtocolProviderService getProtocolProvider() - { - return parentProvider; - } - - /** - * Returns an Iterator over all contacts, member of this - * <tt>ContactGroup</tt>. - * - * @return a java.util.Iterator over all contacts inside this - * <tt>ContactGroup</tt> - */ - public Iterator<Contact> contacts() - { - return contacts.iterator(); - } - - /** - * Adds the specified contact to this group. - * @param contactToAdd the ContactZeroconfImpl to add to this group. - */ - public void addContact(ContactZeroconfImpl contactToAdd) - { - this.contacts.add(contactToAdd); - contactToAdd.setParentGroup(this); - } - - /** - * Returns the number of <tt>Contact</tt> members of this - * <tt>ContactGroup</tt> - * - * @return an int indicating the number of <tt>Contact</tt>s, members of - * this <tt>ContactGroup</tt>. - */ - public int countContacts() - { - return contacts.size(); - } - - /** - * Returns the number of subgroups contained by this - * <tt>ContactGroup</tt>. - * - * @return the number of subGroups currently added to this group. - */ - public int countSubgroups() - { - return subGroups.size(); - } - - /** - * Adds the specified contact group to the contained by this group. - * @param subgroup the ContactGroupZeroconfImpl to add as a - * subgroup to this group. - */ - public void addSubgroup(ContactGroupZeroconfImpl subgroup) - { - this.subGroups.add(subgroup); - subgroup.setParentGroup(this); - } - - /** - * Sets the group that is the new parent of this group - * @param parent ContactGroupZeroconfImpl - */ - void setParentGroup(ContactGroupZeroconfImpl parent) - { - this.parentGroup = parent; - } - - /** - * Returns the contact group that currently contains this group or null if - * this is the root contact group. - * @return the contact group that currently contains this group or null if - * this is the root contact group. - */ - public ContactGroup getParentContactGroup() - { - return this.parentGroup; - } - - /** - * Removes the specified contact group from the this group's subgroups. - * @param subgroup the ContactGroupZeroconfImpl subgroup to remove. - */ - public void removeSubGroup(ContactGroupZeroconfImpl subgroup) - { - this.subGroups.remove(subgroup); - subgroup.setParentGroup(null); - } - - /** - * Returns the group that is parent of the specified zeroconfGroup or null - * if no parent was found. - * @param zeroconfGroup the group whose parent we're looking for. - * @return the ContactGroupZeroconfImpl instance that zeroconfGroup - * belongs to or null if no parent was found. - */ - public ContactGroupZeroconfImpl findGroupParent( - ContactGroupZeroconfImpl zeroconfGroup) - { - if ( subGroups.contains(zeroconfGroup) ) - return this; - - Iterator<ContactGroup> subGroupsIter = subgroups(); - while (subGroupsIter.hasNext()) - { - ContactGroupZeroconfImpl subgroup - = (ContactGroupZeroconfImpl) subGroupsIter.next(); - - ContactGroupZeroconfImpl parent - = subgroup.findGroupParent(zeroconfGroup); - - if(parent != null) - return parent; - } - return null; - } - - /** - * Returns the group that is parent of the specified zeroconfContact or - * null if no parent was found. - * - * @param zeroconfContact the contact whose parent we're looking for. - * @return the ContactGroupZeroconfImpl instance that zeroconfContact - * belongs to or <tt>null</tt> if no parent was found. - */ - public ContactGroupZeroconfImpl findContactParent( - ContactZeroconfImpl zeroconfContact) - { - if ( contacts.contains(zeroconfContact) ) - return this; - - Iterator<ContactGroup> subGroupsIter = subgroups(); - while (subGroupsIter.hasNext()) - { - ContactGroupZeroconfImpl subgroup - = (ContactGroupZeroconfImpl) subGroupsIter.next(); - - ContactGroupZeroconfImpl parent - = subgroup.findContactParent(zeroconfContact); - - if(parent != null) - return parent; - } - return null; - } - - - - /** - * Returns the <tt>Contact</tt> with the specified address or identifier. - * - * @param id the addres or identifier of the <tt>Contact</tt> we are - * looking for. - * @return the <tt>Contact</tt> with the specified id or address. - */ - public Contact getContact(String id) - { - Iterator<Contact> contactsIter = contacts(); - while (contactsIter.hasNext()) - { - ContactZeroconfImpl contact = - (ContactZeroconfImpl)contactsIter.next(); - - if (contact.getAddress().equals(id)) - return contact; - - } - return null; - } - - /** - * Returns the subgroup with the specified index. - * - * @param index the index of the <tt>ContactGroup</tt> to retrieve. - * @return the <tt>ContactGroup</tt> with the specified index. - */ - public ContactGroup getGroup(int index) - { - return subGroups.get(index); - } - - /** - * Returns the subgroup with the specified name. - * - * @param groupName the name of the <tt>ContactGroup</tt> to retrieve. - * @return the <tt>ContactGroup</tt> with the specified index. - */ - public ContactGroup getGroup(String groupName) - { - Iterator<ContactGroup> groupsIter = subgroups(); - while (groupsIter.hasNext()) - { - ContactGroupZeroconfImpl contactGroup - = (ContactGroupZeroconfImpl) groupsIter.next(); - if (contactGroup.getGroupName().equals(groupName)) - return contactGroup; - - } - return null; - - } - - /** - * Returns the name of this group. - * - * @return a String containing the name of this group. - */ - public String getGroupName() - { - return this.groupName; - } - - /** - * Sets this group a new name. - * @param newGrpName a String containing the new name of this group. - */ - public void setGroupName(String newGrpName) - { - this.groupName = newGrpName; - } - - /** - * Returns an iterator over the sub groups that this - * <tt>ContactGroup</tt> contains. - * - * @return a java.util.Iterator over the <tt>ContactGroup</tt> children - * of this group (i.e. subgroups). - */ - public Iterator<ContactGroup> subgroups() - { - return subGroups.iterator(); - } - - /** - * Removes the specified contact from this group. - * @param contact the ContactZeroconfImpl to remove from this group - */ - public void removeContact(ContactZeroconfImpl contact) - { - this.contacts.remove(contact); - } - - /** - * Returns the contact with the specified id or null if no such contact - * exists. - * @param id the id of the contact we're looking for. - * @return ContactZeroconfImpl - */ - public ContactZeroconfImpl findContactByID(String id) - { - //first go through the contacts that are direct children. - Iterator<Contact> contactsIter = contacts(); - - while(contactsIter.hasNext()) - { - ContactZeroconfImpl mContact = - (ContactZeroconfImpl)contactsIter.next(); - - if( mContact.getAddress().equals(id) ) - return mContact; - } - - //if we didn't find it here, let's try in the subougroups - Iterator<ContactGroup> groupsIter = subgroups(); - - while( groupsIter.hasNext() ) - { - ContactGroupZeroconfImpl mGroup = - (ContactGroupZeroconfImpl)groupsIter.next(); - - ContactZeroconfImpl mContact = mGroup.findContactByID(id); - - if (mContact != null) - return mContact; - } - - return null; - } - - - /** - * Returns a String representation of this group and the contacts it - * contains (may turn out to be a relatively long string). - * @return a String representing this group and its child contacts. - */ - @Override - public String toString() - { - - StringBuffer buff = new StringBuffer(getGroupName()); - buff.append(".subGroups=" + countSubgroups() + ":\n"); - - Iterator<ContactGroup> subGroups = subgroups(); - while (subGroups.hasNext()) - { - ContactGroupZeroconfImpl group = - (ContactGroupZeroconfImpl)subGroups.next(); - buff.append(group.toString()); - if (subGroups.hasNext()) - buff.append("\n"); - } - - buff.append("\nChildContacts="+countContacts()+":["); - - Iterator<Contact> contacts = contacts(); - while (contacts.hasNext()) - { - ContactZeroconfImpl contact = (ContactZeroconfImpl) contacts.next(); - buff.append(contact.toString()); - if(contacts.hasNext()) - buff.append(", "); - } - return buff.append("]").toString(); - } - - /** - * Specifies whether or not this contact group is being stored by the server. - * Non persistent contact groups are common in the case of simple, - * non-persistent presence operation sets. They could however also be seen - * in persistent presence operation sets when for example we have received - * an event from someone not on our contact list and the contact that we - * associated with that user is placed in a non persistent group. Non - * persistent contact groups are volatile even when coming from a persistent - * presence op. set. They would only exist until the application is closed - * and will not be there next time it is loaded. - * - * @param isPersistent true if the contact group is to be persistent and - * false otherwise. - */ - public void setPersistent(boolean isPersistent) - { - this.isPersistent = isPersistent; - } - - /** - * Determines whether or not this contact group is being stored by the - * server. Non persistent contact groups exist for the sole purpose of - * containing non persistent contacts. - * @return true if the contact group is persistent and false otherwise. - */ - public boolean isPersistent() - { - return isPersistent; - } - - /** - * Returns null as no persistent data is required and the contact address is - * sufficient for restoring the contact. - * <p> - * @return null as no such data is needed. - */ - public String getPersistentData() - { - return null; - } - - /** - * Determines whether or not this contact has been resolved against the - * server. Unresolved contacts are used when initially loading a contact - * list that has been stored in a local file until the presence operation - * set has managed to retrieve all the contact list from the server and has - * properly mapped contacts to their on-line buddies. - * @return true if the contact has been resolved (mapped against a buddy) - * and false otherwise. - */ - public boolean isResolved() - { - return isResolved; - } - - /** - * Makes the group resolved or unresolved. - * - * @param resolved true to make the group resolved; false to - * make it unresolved - */ - public void setResolved(boolean resolved) - { - this.isResolved = resolved; - } - - /** - * Returns a <tt>String</tt> that uniquely represnets the group inside - * the current protocol. The string MUST be persistent (it must not change - * across connections or runs of the application). In many cases (Jabber, - * ICQ) the string may match the name of the group as these protocols - * only allow a single level of contact groups and there is no danger of - * having the same name twice in the same contact list. Other protocols - * (no examples come to mind but that doesn't bother me ;) ) may be - * supporting mutilple levels of grooups so it might be possible for group - * A and group B to both contain groups named C. In such cases the - * implementation must find a way to return a unique identifier in this - * method and this UID should never change for a given group. - * - * @return a String representing this group in a unique and persistent - * way. - */ - public String getUID() - { - return uid; - } - - /** - * Ugly but tricky conversion method. - * @param uid the uid we'd like to get a name from - * @return the name of the group with the specified <tt>uid</tt>. - */ - static String createNameFromUID(String uid) - { - return uid.substring(0, uid.length() - (UID_SUFFIX.length())); - } - - /** - * Indicates whether some other object is "equal to" this one which in terms - * of contact groups translates to having the equal names and matching - * subgroups and child contacts. The resolved status of contactgroups and - * contacts is deliberately ignored so that groups and/or contacts would - * be assumed equal even if it differs. - * <p> - * @param obj the reference object with which to compare. - * @return <code>true</code> if this contact group has the equal child - * contacts and subgroups to those of the <code>obj</code> argument. - */ - @Override - public boolean equals(Object obj) - { - if(obj == null - || !(obj instanceof ContactGroupZeroconfImpl)) - return false; - - ContactGroupZeroconfImpl zeroconfGroup - = (ContactGroupZeroconfImpl)obj; - - if(!zeroconfGroup.getGroupName().equals(getGroupName()) || - !zeroconfGroup.getUID().equals(getUID()) || - zeroconfGroup.countContacts() != countContacts() || - zeroconfGroup.countSubgroups() != countSubgroups()) - return false; - - //traverse child contacts - Iterator<Contact> theirContacts = zeroconfGroup.contacts(); - - while(theirContacts.hasNext()) - { - ContactZeroconfImpl theirContact - = (ContactZeroconfImpl)theirContacts.next(); - - ContactZeroconfImpl ourContact - = (ContactZeroconfImpl)getContact(theirContact.getAddress()); - - if(ourContact == null - || !ourContact.equals(theirContact)) - return false; - } - - //traverse subgroups - Iterator<ContactGroup> theirSubgroups = zeroconfGroup.subgroups(); - - while(theirSubgroups.hasNext()) - { - ContactGroupZeroconfImpl theirSubgroup - = (ContactGroupZeroconfImpl)theirSubgroups.next(); - - ContactGroupZeroconfImpl ourSubgroup - = (ContactGroupZeroconfImpl)getGroup( - theirSubgroup.getGroupName()); - - if(ourSubgroup == null - || !ourSubgroup.equals(theirSubgroup)) - return false; - } - - return true; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactZeroconfImpl.java deleted file mode 100644 index c22cf29..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ContactZeroconfImpl.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.net.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * A simple, straightforward implementation of a zeroconf Contact. Since - * the Zeroconf protocol is not a real one, we simply store all contact details - * in class fields. You should know that when implementing a real protocol, - * the contact implementation would rather encapsulate contact objects from - * the protocol stack and group property values should be returned after - * consulting the encapsulated object. - * - * @author Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - */ -public class ContactZeroconfImpl - extends AbstractContact -{ - private static final Logger logger - = Logger.getLogger(ContactZeroconfImpl.class); - - - /** - * The id of the contact. - */ - private String contactID = null; - - /** - * The ClientThread attached to this contact if we're already chatting - * with him. - */ - private ClientThread thread = null; - - /* - * Type of Client. - */ - /** - * Gaim/Pidgin client type - */ - public static final int GAIM = 1; - /** - * iChat client type - */ - public static final int ICHAT = 2; - /** - * XMPP - XEP-0174 client type - */ - public static final int XMPP = 3; - /** - * Another SIP Communicator client - */ - public static final int SIPCOM = 4; - private int clientType = XMPP; - - - /** - * The provider that created us. - */ - private ProtocolProviderServiceZeroconfImpl parentProvider = null; - - - /* - * The Bonjour Service who discovered this contact. - * TODO: This could probably be avoided using only the - * Protocol Provider. - */ - private BonjourService bonjourService; - - /** - * The group that belong to. - */ - private ContactGroupZeroconfImpl parentGroup = null; - - /** - * The presence status of the contact. - */ - private PresenceStatus presenceStatus = ZeroconfStatusEnum.OFFLINE; - - /** - * Determines whether this contact is persistent, - * i.e. member of the contact list or whether it is here only temporarily. - * Chris: should be set to false here - */ - private boolean isPersistent = false; - - /** - * Determines whether the contact has been resolved (i.e. we have a - * confirmation that it is still on the server contact list). - */ - private boolean isResolved = true; - - /** - * IP Address - */ - private InetAddress ipAddress; - - /** - * Port on which Bonjour is listening. - */ - private int port; - - /** - * Name announced by Bonjour. - */ - private String name; - - /** - * Contact personal message - */ - private String message; - - - /** - * Creates an instance of a meta contact with the specified string used - * as a name and identifier. - * @param bonjourId ID of the contact - * @param bonjourService BonjourService responsible for handling chat with - * this contact - * @param name Display name of this contact - * @param ipAddress IP address of this contact - * @param port Port declared by this contact for direct point-to-point chat - * @param parentProvider the provider that created us. - */ - public ContactZeroconfImpl( - String bonjourId, - ProtocolProviderServiceZeroconfImpl parentProvider, - BonjourService bonjourService, - String name, - InetAddress ipAddress, - int port) - { - this.contactID = bonjourId; - this.parentProvider = parentProvider; - this.bonjourService = bonjourService; - this.name = name; - this.ipAddress = ipAddress; - this.port = port; - bonjourService.addContact(this); - } - - /** - * This method is only called when the contact is added to a new - * <tt>ContactGroupZeroconfImpl</tt> by the - * <tt>ContactGroupZeroconfImpl</tt> itself. - * - * @param newParentGroup the <tt>ContactGroupZeroconfImpl</tt> that is now - * parent of this <tt>ContactZeroconfImpl</tt> - */ - void setParentGroup(ContactGroupZeroconfImpl newParentGroup) - { - this.parentGroup = newParentGroup; - } - - /** - * Return the BonjourService - * @return BonjourService - */ - public BonjourService getBonjourService() - { - return bonjourService; - } - - /** - * Return the ClientThread responsible for handling with this contact - * @return ClientThread corresponding to the chat with this contact or null - * if no chat was started - */ - protected ClientThread getClientThread() - { - return thread; - } - - /** - * Set the ClientThread responsible for handling with this contact - * @param thread ClientThread corresponding to the chat with this contact - * or null if the chat is over - */ - protected void setClientThread(ClientThread thread) - { - this.thread = thread; - } - - /** - * Return the type of client - * @return Type of client used by this contact - */ - public int getClientType() - { - return clientType; - } - - /** - * Sets the type of client - * @param clientType Type of client used by this contact - */ - public void setClientType(int clientType) - { - this.clientType = clientType; - } - - /** - * Returns a String that can be used for identifying the contact. - * - * @return a String id representing and uniquely identifying the contact. - */ - public String getAddress() - { - return contactID; - } - - /** - * Returns a String that could be used by any user interacting modules - * for referring to this contact. - * - * @return a String that can be used for referring to this contact when - * interacting with the user. - */ - public String getDisplayName() - { - return name; - } - - /** - * Returns the IP address declared by this Contact - * @return IP address declared by this Contact - */ - public InetAddress getIpAddress() - { - return ipAddress; - } - - /** - * Returns the TCP port declared by this Contact for direct chat - * @return the TCP port declared by this Contact for direct chat - */ - public int getPort() - { - return port; - } - - - /** - * Returns the status/private message displayed by this contact - * @return the status/private message displayed by this contact - */ - public String getMessage() - { - return message; - } - - /** - * Sets the status/private message displayed by this contact - * @param message the status/private message displayed by this contact - */ - public void setMessage(String message) - { - this.message = message; - } - - - /** - * Returns a byte array containing an image (most often a photo or an - * avatar) that the contact uses as a representation. - * - * @return byte[] an image representing the contact. - */ - public byte[] getImage() - { - return null; - } - - /** - * Returns the status of the contact. - * - * @return always ZeroconfStatusEnum. - */ - public PresenceStatus getPresenceStatus() - { - return this.presenceStatus; - } - - /** - * Sets <tt>zeroconfPresenceStatus</tt> as the PresenceStatus that this - * contact is currently in. - * @param zeroconfPresenceStatus the <tt>ZeroconfPresenceStatus</tt> - * currently valid for this contact. - */ - public void setPresenceStatus(PresenceStatus zeroconfPresenceStatus) - { - this.presenceStatus = zeroconfPresenceStatus; - - if (zeroconfPresenceStatus == ZeroconfStatusEnum.OFFLINE) { - try - { - bonjourService.opSetPersPresence.unsubscribe(this); - } - catch (Exception ex) - { - logger.error(ex); - } - } - } - - /** - * Returns a reference to the protocol provider that created the contact. - * - * @return a refererence to an instance of the ProtocolProviderService - */ - public ProtocolProviderService getProtocolProvider() - { - return parentProvider; - } - - /** - * Determines whether or not this contact represents our own identity. - * - * @return true in case this is a contact that represents ourselves and - * false otherwise. - */ - public boolean isLocal() - { - return false; - } - - /** - * Returns the group that contains this contact. - * @return a reference to the <tt>ContactGroupZeroconfImpl</tt> that - * contains this contact. - */ - public ContactGroup getParentContactGroup() - { - return this.parentGroup; - } - - /** - * Returns a string representation of this contact, containing most of its - * representative details. - * - * @return a string representation of this contact. - */ - @Override - public String toString() - { - StringBuffer buff - = new StringBuffer("ContactZeroconfImpl[ DisplayName=") - .append(getDisplayName()).append("]"); - - return buff.toString(); - } - - /** - * Determines whether or not this contact is being stored by the server. - * Non persistent contacts are common in the case of simple, non-persistent - * presence operation sets. They could however also be seen in persistent - * presence operation sets when for example we have received an event - * from someone not on our contact list. Non persistent contacts are - * volatile even when coming from a persistent presence op. set. They would - * only exist until the application is closed and will not be there next - * time it is loaded. - * - * @return true if the contact is persistent and false otherwise. - */ - public boolean isPersistent() - { - return isPersistent; - } - - /** - * Specifies whether or not this contact is being stored by the server. - * Non persistent contacts are common in the case of simple, non-persistent - * presence operation sets. They could however also be seen in persistent - * presence operation sets when for example we have received an event - * from someone not on our contact list. Non persistent contacts are - * volatile even when coming from a persistent presence op. set. They would - * only exist until the application is closed and will not be there next - * time it is loaded. - * - * @param isPersistent true if the contact is persistent and false - * otherwise. - */ - public void setPersistent(boolean isPersistent) - { - this.isPersistent = isPersistent; - } - - - /** - * Returns null as no persistent data is required and the contact address is - * sufficient for restoring the contact. - * <p> - * @return null as no such data is needed. - */ - public String getPersistentData() - { - return null; - } - - /** - * Determines whether or not this contact has been resolved against the - * server. Unresolved contacts are used when initially loading a contact - * list that has been stored in a local file until the presence operation - * set has managed to retrieve all the contact list from the server and has - * properly mapped contacts to their on-line buddies. - * - * @return true if the contact has been resolved (mapped against a buddy) - * and false otherwise. - */ - public boolean isResolved() - { - return isResolved; - } - - /** - * Makes the contact resolved or unresolved. - * - * @param resolved true to make the contact resolved; false to - * make it unresolved - */ - public void setResolved(boolean resolved) - { - this.isResolved = resolved; - } - - /** - * Returns the persistent presence operation set that this contact belongs - * to. - * - * @return the <tt>OperationSetPersistentPresenceZeroconfImpl</tt> that - * this contact belongs to. - */ - public OperationSetPersistentPresenceZeroconfImpl - getParentPresenceOperationSet() - { - return (OperationSetPersistentPresenceZeroconfImpl)parentProvider - .getOperationSet(OperationSetPersistentPresence.class); - } - - /** - * Return the current status message of this contact. - * - * @return null as the protocol has currently no support of status messages - */ - public String getStatusMessage() - { - return null; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/MessageZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/MessageZeroconfImpl.java deleted file mode 100644 index 3621edc..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/MessageZeroconfImpl.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import net.java.sip.communicator.service.protocol.*; - -/** - * Very simple message implementation for the Zeroconf protocol. - * - * @author Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - * @author Lubomir Marinov - */ -public class MessageZeroconfImpl - extends AbstractMessage -{ - - /** - * Message Type. - */ - private int type; - - /** - * Message type indicating that a stream is being created - */ - public static final int STREAM_OPEN = 0x1; - - /** - * Normal chat message - */ - public static final int MESSAGE = 0x2; - - /** - * Typing notification - */ - public static final int TYPING = 0x3; - - /** - * Message indicating that the stream is being closed - */ - public static final int STREAM_CLOSE = 0x4; - - /** - * Message indicating that the previsous message was delivered successfully - */ - public static final int DELIVERED = 0x5; - - /** - * Undefined message - */ - public static final int UNDEF = 0x6; - - /* - * The Baloon Icon color. (we probably won't ever use it) - */ - private int baloonColor = 0x7BB5EE; - - /* - * The Text Color. - */ - private int textColor = 0x000000; - - /* - * The font of the message. - */ - private String textFont = "Helvetica"; - - /* - * The size of the caracters composing the message. - */ - private int textSize = 12; - - /* - * The source contact id announced in the message. TODO: Could be set & - * checked to identify more precisely the contact in case several users - * would be sharing the same IP. - */ - private String contactID; - - /** - * Creates a message instance according to the specified parameters. - * - * @param content the message body - * @param contentEncoding message encoding or null for UTF8 - * @param contentType of the message - * @param type Type of message - */ - public MessageZeroconfImpl(String content, String contentEncoding, - String contentType, int type) - { - super(content, contentType, contentEncoding, null); - - this.type = type; - } - - /** - * Creates a message instance according to the specified parameters. - * - * @param type Type of message - * @param content the message body - * @param contentEncoding message encoding or null for UTF8 - */ - public MessageZeroconfImpl(String content, String contentEncoding, int type) - { - this(content, contentEncoding, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE, type); - } - - /** - * Returns the type of message. Always text/plain for Zeroconf, so null. - * - * @return null - */ - public int getType() - { - return type; - } - - /** - * Gets the baloon color declared in messages sent by iChat-like clients - * - * @return baloon color - */ - public int getBaloonColor() - { - return baloonColor; - } - - /** - * Sets the baloon color declared in messages sent by iChat-like clients - * - * @param baloonColor baloon color - */ - public void setBaloonColor(int baloonColor) - { - this.baloonColor = baloonColor; - } - - /** - * Returns the text color - * - * @return Text color - */ - public int getTextColor() - { - return textColor; - } - - /** - * Sets the text color - * - * @param textColor Text color - */ - public void setTextColor(int textColor) - { - this.textColor = textColor; - } - - /** - * Returns the text font - * - * @return Text font - */ - public String getTextFont() - { - return textFont; - } - - /** - * Sets the text color - * - * @param textFont Text font - */ - public void setTextFont(String textFont) - { - this.textFont = textFont; - } - - /** - * Returns the text size - * - * @return Text size - */ - public int getTextSize() - { - return textSize; - } - - /** - * Sets the text size - * - * @param textSize Text size - */ - public void setTextSize(int textSize) - { - this.textSize = textSize; - } - - /** - * Returns the contact's ID - * - * @return String representing the contact's ID - */ - public String getContactID() - { - return contactID; - } - - /** - * Sets the contact's ID - * - * @param contactID String representing the contact's ID - */ - public void setContactID(String contactID) - { - this.contactID = contactID; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java deleted file mode 100644 index d49118b..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.io.*; -import java.net.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -/** - * Instant messaging functionalities for the Zeroconf protocol. - * - * @author Christian Vincenot - * - */ -public class OperationSetBasicInstantMessagingZeroconfImpl - extends AbstractOperationSetBasicInstantMessaging -{ - private static final Logger logger - = Logger.getLogger(OperationSetBasicInstantMessagingZeroconfImpl.class); - - /** - * The currently valid persistent presence operation set.. - */ - private final OperationSetPersistentPresenceZeroconfImpl opSetPersPresence; - - /** - * The protocol provider that created us. - */ - private final ProtocolProviderServiceZeroconfImpl parentProvider; - - /** - * Creates an instance of this operation set keeping a reference to the - * parent protocol provider and presence operation set. - * - * @param provider The provider instance that creates us. - * @param opSetPersPresence the currently valid - * <tt>OperationSetPersistentPresenceZeroconfImpl</tt> instance. - */ - public OperationSetBasicInstantMessagingZeroconfImpl( - ProtocolProviderServiceZeroconfImpl provider, - OperationSetPersistentPresenceZeroconfImpl opSetPersPresence) - { - this.opSetPersPresence = opSetPersPresence; - this.parentProvider = provider; - } - - @Override - public Message createMessage(String content, String contentType, - String encoding, String subject) - { - return new MessageZeroconfImpl(content, encoding, contentType, - MessageZeroconfImpl.MESSAGE); - } - - /** - * Sends the <tt>message</tt> to the destination indicated by the - * <tt>to</tt> contact. - * - * @param to the <tt>Contact</tt> to send <tt>message</tt> to - * @param message the <tt>Message</tt> to send. - * @throws IllegalStateException if the underlying Zeroconf stack is not - * registered and initialized. - * @throws IllegalArgumentException if <tt>to</tt> is not an instance - * belonging to the underlying implementation. - */ - public void sendInstantMessage(Contact to, Message message) throws - IllegalStateException, IllegalArgumentException - { - if( !(to instanceof ContactZeroconfImpl) ) - throw new IllegalArgumentException( - "The specified contact is not a Zeroconf contact." - + to); - - MessageZeroconfImpl msg = - (MessageZeroconfImpl)createMessage(message.getContent()); - - deliverMessage(msg, (ContactZeroconfImpl)to); - } - - /** - * In case the to the <tt>to</tt> Contact corresponds to another zeroconf - * protocol provider registered with SIP Communicator, we deliver - * the message to them, in case the <tt>to</tt> Contact represents us, we - * fire a <tt>MessageReceivedEvent</tt>, and if <tt>to</tt> is simply - * a contact in our contact list, then we simply echo the message. - * - * @param message the <tt>Message</tt> the message to deliver. - * @param to the <tt>Contact</tt> that we should deliver the message to. - */ - private void deliverMessage(Message message, ContactZeroconfImpl to) - { - ClientThread thread = to.getClientThread(); - try - { - if (thread == null) - { - Socket sock; - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF: Creating a chat connexion to " - +to.getIpAddress()+":"+to.getPort()); - sock = new Socket(to.getIpAddress(), to.getPort()); - thread = new ClientThread(sock, to.getBonjourService()); - thread.setStreamOpen(); - thread.setContact(to); - to.setClientThread(thread); - thread.sendHello(); - if (to.getClientType() == ContactZeroconfImpl.GAIM) - { - try - { - Thread.sleep(300); - } - catch (InterruptedException ex) - { - logger.error(ex); - } - } - } - - //System.out.println("ZEROCONF: Message content => "+ - //message.getContent()); - thread.sendMessage((MessageZeroconfImpl) message); - - fireMessageDelivered(message, to); - } - catch (IOException ex) - { - logger.error(ex); - } - } - - /** - * Notifies all registered message listeners that a message has been - * received. - * - * @param message the <tt>Message</tt> that has been received. - * @param from the <tt>Contact</tt> that <tt>message</tt> was received from. - */ - @Override - public void fireMessageReceived(Message message, Contact from) - { - super.fireMessageReceived(message, from); - } - - /** - * Determines whether the protocol provider (or the protocol itself) support - * sending and receiving offline messages. Most often this method would - * return true for protocols that support offline messages and false for - * those that don't. It is however possible for a protocol to support these - * messages and yet have a particular account that does not (i.e. feature - * not enabled on the protocol server). In cases like this it is possible - * for this method to return true even when offline messaging is not - * supported, and then have the sendMessage method throw an - * OperationFailedException with code - OFFLINE_MESSAGES_NOT_SUPPORTED. - * - * @return <tt>true</tt> if the protocol supports offline messages and - * <tt>false</tt> otherwise. - */ - public boolean isOfflineMessagingSupported() - { - return true; - } - - /** - * Determines whether the protocol supports the supplied content type - * - * @param contentType the type we want to check - * @return <tt>true</tt> if the protocol supports it and - * <tt>false</tt> otherwise. - */ - public boolean isContentTypeSupported(String contentType) - { - return contentType.equals(DEFAULT_MIME_TYPE); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetPersistentPresenceZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetPersistentPresenceZeroconfImpl.java deleted file mode 100644 index 412512e..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetPersistentPresenceZeroconfImpl.java +++ /dev/null @@ -1,852 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.net.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; - -import org.osgi.framework.*; - -/** - * A Zeroconf implementation of a persistent presence operation set. In order - * to simulate server persistence, this operation set would simply accept all - * unresolved contacts and resolve them immediately. A real world protocol - * implementation would save it on a server using methods provided by the - * protocol stack. - * - * @author Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - */ -public class OperationSetPersistentPresenceZeroconfImpl - extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceZeroconfImpl> -{ - private static final Logger logger = - Logger.getLogger(OperationSetPersistentPresenceZeroconfImpl.class); - - /** - * The root of the zeroconf contact list. - */ - private ContactGroupZeroconfImpl contactListRoot = null; - - /** - * The currently active status message. - */ - private String statusMessage = "The truth is out there..."; - - /** - * Our default presence status. - */ - private PresenceStatus presenceStatus = ZeroconfStatusEnum.OFFLINE; - - /** - * The <tt>AuthorizationHandler</tt> instance that we'd have to transmit - * authorization requests to for approval. - */ - private AuthorizationHandler authorizationHandler = null; - - /** - * Creates an instance of this operation set keeping a reference to the - * specified parent <tt>provider</tt>. - * @param provider the ProtocolProviderServiceZeroconfImpl instance that - * created us. - */ - public OperationSetPersistentPresenceZeroconfImpl( - ProtocolProviderServiceZeroconfImpl provider) - { - super(provider); - - contactListRoot = new ContactGroupZeroconfImpl("RootGroup", provider); - - //add our unregistration listener - parentProvider.addRegistrationStateChangeListener( - new UnregistrationListener()); - } - - /** - * Creates a group with the specified name and parent in the server - * stored contact list. - * - * @param parent the group where the new group should be created - * @param groupName the name of the new group to create. - */ - public void createServerStoredContactGroup(ContactGroup parent, - String groupName) - { - ContactGroupZeroconfImpl newGroup - = new ContactGroupZeroconfImpl(groupName, parentProvider); - - ((ContactGroupZeroconfImpl)parent).addSubgroup(newGroup); - - this.fireServerStoredGroupEvent( - newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); - } - - /** - * A Zeroconf Provider method to use for fast filling of a contact list. - * - * @param contactGroup the group to add - */ - public void addZeroconfGroup(ContactGroupZeroconfImpl contactGroup) - { - contactListRoot.addSubgroup(contactGroup); - } - - /** - * A Zeroconf Provider method to use for fast filling of a contact list. - * This method would add both the group and fire an event. - * - * @param parent the group where <tt>contactGroup</tt> should be added. - * @param contactGroup the group to add - */ - public void addZeroconfGroupAndFireEvent( - ContactGroupZeroconfImpl parent - , ContactGroupZeroconfImpl contactGroup) - { - parent.addSubgroup(contactGroup); - - this.fireServerStoredGroupEvent( - contactGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); - } - - - /** - * Returns a reference to the contact with the specified ID in case we - * have a subscription for it and null otherwise/ - * - * @param contactID a String identifier of the contact which we're - * seeking a reference of. - * @return a reference to the Contact with the specified - * <tt>contactID</tt> or null if we don't have a subscription for the - * that identifier. - */ - public Contact findContactByID(String contactID) - { - return contactListRoot.findContactByID(contactID); - } - - /** - * Sets the specified status message. - * @param statusMessage a String containing the new status message. - */ - public void setStatusMessage(String statusMessage) - { - this.statusMessage = statusMessage; - } - - /** - * Returns the status message that was last set through - * setCurrentStatusMessage. - * - * @return the last status message that we have requested and the aim - * server has confirmed. - */ - public String getCurrentStatusMessage() - { - return statusMessage; - } - - /** - * Returns a PresenceStatus instance representing the state this provider - * is currently in. - * - * @return the PresenceStatus last published by this provider. - */ - public PresenceStatus getPresenceStatus() - { - return presenceStatus; - } - - /** - * Returns the root group of the server stored contact list. - * - * @return the root ContactGroup for the ContactList stored by this - * service. - */ - public ContactGroup getServerStoredContactListRoot() - { - return contactListRoot; - } - - /** - * Returns the set of PresenceStatus objects that a user of this service - * may request the provider to enter. - * - * @return Iterator a PresenceStatus array containing "enterable" status - * instances. - */ - public Iterator<PresenceStatus> getSupportedStatusSet() - { - return ZeroconfStatusEnum.supportedStatusSet(); - } - - /** - * Removes the specified contact from its current parent and places it - * under <tt>newParent</tt>. - * - * @param contactToMove the <tt>Contact</tt> to move - * @param newParent the <tt>ContactGroup</tt> where <tt>Contact</tt> - * would be placed. - */ - public void moveContactToGroup(Contact contactToMove, - ContactGroup newParent) - { - ContactZeroconfImpl zeroconfContact - = (ContactZeroconfImpl)contactToMove; - - ContactGroupZeroconfImpl parentZeroconfGroup - = findContactParent(zeroconfContact); - - parentZeroconfGroup.removeContact(zeroconfContact); - - //if this is a volatile contact then we haven't really subscribed to - //them so we'd need to do so here - if(!zeroconfContact.isPersistent()) - { - //first tell everyone that the volatile contact was removed - fireSubscriptionEvent(zeroconfContact - , parentZeroconfGroup - , SubscriptionEvent.SUBSCRIPTION_REMOVED); - - try - { - //now subscribe - this.subscribe(newParent, contactToMove.getAddress()); - - //now tell everyone that we've added the contact - fireSubscriptionEvent(zeroconfContact - , newParent - , SubscriptionEvent.SUBSCRIPTION_CREATED); - } - catch (Exception ex) - { - logger.error("Failed to move contact " - + zeroconfContact.getAddress() - , ex); - } - } - else - { - ( (ContactGroupZeroconfImpl) newParent) - .addContact(zeroconfContact); - - fireSubscriptionMovedEvent(contactToMove - , parentZeroconfGroup - , newParent); - } - } - - /** - * Requests the provider to enter into a status corresponding to the - * specified paramters. - * - * @param status the PresenceStatus as returned by - * getRequestableStatusSet - * @param statusMessage the message that should be set as the reason to - * enter that status - * @throws IllegalArgumentException if the status requested is not a - * valid PresenceStatus supported by this provider. - * @throws IllegalStateException if the provider is not currently - * registered. - * @throws OperationFailedException with code NETWORK_FAILURE if - * publishing the status fails due to a network error. - */ - public void publishPresenceStatus(PresenceStatus status, - String statusMessage) - throws IllegalArgumentException, - IllegalStateException, - OperationFailedException - { - PresenceStatus oldPresenceStatus = this.presenceStatus; - this.presenceStatus = status; - this.statusMessage = statusMessage; - - //ICI: changer le statut du plugin Zeroconf!! - parentProvider.getBonjourService().changeStatus(status); - - this.fireProviderStatusChangeEvent(oldPresenceStatus); - - } - - /** - * Get the PresenceStatus for a particular contact. - * - * @param contactIdentifier the identifier of the contact whose status - * we're interested in. - * @return PresenceStatus the <tt>PresenceStatus</tt> of the specified - * <tt>contact</tt> - * @throws IllegalArgumentException if <tt>contact</tt> is not a contact - * known to the underlying protocol provider - * @throws IllegalStateException if the underlying protocol provider is - * not registered/signed on a public service. - * @throws OperationFailedException with code NETWORK_FAILURE if - * retrieving the status fails due to errors experienced during - * network communication - */ - public PresenceStatus queryContactStatus(String contactIdentifier) - throws IllegalArgumentException, - IllegalStateException, - OperationFailedException - { - return findContactByID(contactIdentifier).getPresenceStatus(); - } - - /** - * Sets the presence status of <tt>contact</tt> to <tt>newStatus</tt>. - * - * @param contact the <tt>ContactZeroconfImpl</tt> whose status we'd like - * to set. - * @param newStatus the new status we'd like to set to <tt>contact</tt>. - */ - public void changePresenceStatusForContact(ContactZeroconfImpl contact, - PresenceStatus newStatus) - { - PresenceStatus oldStatus = contact.getPresenceStatus(); - contact.setPresenceStatus(newStatus); - - fireContactPresenceStatusChangeEvent( - contact, findContactParent(contact), oldStatus); - } - - /** - * Sets the presence status of all <tt>contact</tt>s in our contact list - * (except those that correspond to another provider registered with SC) - * to <tt>newStatus</tt>. - * - * @param newStatus the new status we'd like to set to <tt>contact</tt>. - * @param parent the group in which we'd have to update the status of all - * direct and indirect child contacts. - */ - protected void changePresenceStatusForAllContacts(ContactGroup parent, - PresenceStatus newStatus) - { - //first set the status for contacts in this group - Iterator<Contact> childContacts = parent.contacts(); - - while(childContacts.hasNext()) - { - ContactZeroconfImpl contact - = (ContactZeroconfImpl)childContacts.next(); - - if(findProviderForZeroconfUserID(contact.getAddress()) != null) - { - //this is a contact corresponding to another SIP Communicator - //provider so we won't change it's status here. - continue; - } - PresenceStatus oldStatus = contact.getPresenceStatus(); - contact.setPresenceStatus(newStatus); - - fireContactPresenceStatusChangeEvent( - contact, parent, oldStatus); - } - - //now call this method recursively for all subgroups - Iterator<ContactGroup> subgroups = parent.subgroups(); - - while(subgroups.hasNext()) - { - ContactGroup subgroup = subgroups.next(); - changePresenceStatusForAllContacts(subgroup, newStatus); - } - } - - /** - * Returns the group that is parent of the specified zeroconfGroup or null - * if no parent was found. - * @param zeroconfGroup the group whose parent we're looking for. - * @return the ContactGroupZeroconfImpl instance that zeroconfGroup - * belongs to or null if no parent was found. - */ - public ContactGroupZeroconfImpl findGroupParent( - ContactGroupZeroconfImpl zeroconfGroup) - { - return contactListRoot.findGroupParent(zeroconfGroup); - } - - /** - * Returns the group that is parent of the specified zeroconfContact or - * null if no parent was found. - * @param zeroconfContact the contact whose parent we're looking for. - * @return the ContactGroupZeroconfImpl instance that zeroconfContact - * belongs to or null if no parent was found. - */ - public ContactGroupZeroconfImpl findContactParent( - ContactZeroconfImpl zeroconfContact) - { - return (ContactGroupZeroconfImpl)zeroconfContact - .getParentContactGroup(); - } - - - /** - * Removes the specified group from the server stored contact list. - * - * @param group the group to remove. - * - * @throws IllegalArgumentException if <tt>group</tt> was not found in this - * protocol's contact list. - */ - public void removeServerStoredContactGroup(ContactGroup group) - throws IllegalArgumentException - { - ContactGroupZeroconfImpl zeroconfGroup - = (ContactGroupZeroconfImpl)group; - - ContactGroupZeroconfImpl parent = findGroupParent(zeroconfGroup); - - if(parent == null){ - throw new IllegalArgumentException( - "group " + group - + " does not seem to belong to this protocol's contact list."); - } - - parent.removeSubGroup(zeroconfGroup); - - this.fireServerStoredGroupEvent( - zeroconfGroup, ServerStoredGroupEvent.GROUP_REMOVED_EVENT); - } - - /** - * Renames the specified group from the server stored contact list. - * - * @param group the group to rename. - * @param newName the new name of the group. - */ - public void renameServerStoredContactGroup(ContactGroup group, - String newName) - { - ((ContactGroupZeroconfImpl)group).setGroupName(newName); - - this.fireServerStoredGroupEvent( - group, - ServerStoredGroupEvent.GROUP_RENAMED_EVENT); - } - - /** - * Handler for incoming authorization requests. - * - * @param handler an instance of an AuthorizationHandler for - * authorization requests coming from other users requesting - * permission add us to their contact list. - */ - public void setAuthorizationHandler(AuthorizationHandler handler) - { - this.authorizationHandler = handler; - } - - /** - * Persistently adds a subscription for the presence status of the - * contact corresponding to the specified contactIdentifier and indicates - * that it should be added to the specified group of the server stored - * contact list. - * - * @param parent the parent group of the server stored contact list - * where the contact should be added. <p> - * @param contactIdentifier the contact whose status updates we are - * subscribing for. - * @throws IllegalArgumentException if <tt>contact</tt> or - * <tt>parent</tt> are not a contact known to the underlying protocol - * provider. - * @throws IllegalStateException if the underlying protocol provider is - * not registered/signed on a public service. - * @throws OperationFailedException with code NETWORK_FAILURE if - * subscribing fails due to errors experienced during network - * communication - */ - public void subscribe(ContactGroup parent, String contactIdentifier) - throws IllegalArgumentException, - IllegalStateException, - OperationFailedException - { - /* ContactZeroconfImpl contact = new ContactZeroconfImpl( - contactIdentifier, - parentProvider, - null, null, null, 0); - - ((ContactGroupZeroconfImpl)parent).addContact(contact); - - fireSubscriptionEvent(contact, - parent, - SubscriptionEvent.SUBSCRIPTION_CREATED); - //if the newly added contact corresponds to another provider - set their - //status accordingly - ProtocolProviderServiceZeroconfImpl gibProvider - = findProviderForZeroconfUserID(contactIdentifier); - if(gibProvider != null) - { - OperationSetPersistentPresence opSetPresence - = (OperationSetPersistentPresence)gibProvider.getOperationSet( - OperationSetPersistentPresence.class); - - changePresenceStatusForContact( - contact - , (ZeroconfStatusEnum)opSetPresence.getPresenceStatus()); - } - else - { - //otherwise - since we are not a real protocol, we set the contact - //presence status ourselves - changePresenceStatusForContact(contact, getPresenceStatus()); - } - - //notify presence listeners for the status change. - fireContactPresenceStatusChangeEvent(contact - , parent - , ZeroconfStatusEnum.OFFLINE); - */} - - - - /** - * Adds a subscription for the presence status of the contact - * corresponding to the specified contactIdentifier. - * - * @param contactIdentifier the identifier of the contact whose status - * updates we are subscribing for. <p> - * @throws IllegalArgumentException if <tt>contact</tt> is not a contact - * known to the underlying protocol provider - * @throws IllegalStateException if the underlying protocol provider is - * not registered/signed on a public service. - * @throws OperationFailedException with code NETWORK_FAILURE if - * subscribing fails due to errors experienced during network - * communication - */ - public void subscribe(String contactIdentifier) - throws IllegalArgumentException, - IllegalStateException, - OperationFailedException - { - subscribe(contactListRoot, contactIdentifier); - } - - /** - * Removes a subscription for the presence status of the specified - * contact. - * - * @param contact the contact whose status updates we are unsubscribing - * from. - * @throws IllegalArgumentException if <tt>contact</tt> is not a contact - * known to the underlying protocol provider - * @throws IllegalStateException if the underlying protocol provider is - * not registered/signed on a public service. - * @throws OperationFailedException with code NETWORK_FAILURE if - * unsubscribing fails due to errors experienced during network - * communication - */ - public void unsubscribe(Contact contact) - throws IllegalArgumentException, - IllegalStateException, - OperationFailedException - { - String name = contact.getAddress(); - - ContactGroupZeroconfImpl parentGroup - = (ContactGroupZeroconfImpl)((ContactZeroconfImpl)contact) - .getParentContactGroup(); - - //parentGroup.removeContact((ContactZeroconfImpl)contact); - - BonjourService service = - ((ProtocolProviderServiceZeroconfImpl)contact.getProtocolProvider()) - .getBonjourService(); - //TODO: better check with IP - service.removeContact(name,null); - - fireSubscriptionEvent(contact, - ((ContactZeroconfImpl)contact).getParentContactGroup() - , SubscriptionEvent.SUBSCRIPTION_REMOVED); - } - - /** - * Creates and returns a unresolved contact from the specified - * <tt>address</tt> and <tt>persistentData</tt>. The method will not try - * to establish a network connection and resolve the newly created Contact - * against the server. The protocol provider may will later try and resolve - * the contact. When this happens the corresponding event would notify - * interested subscription listeners. - * - * @param address an identifier of the contact that we'll be creating. - * @param persistentData a String returned Contact's getPersistentData() - * method during a previous run and that has been persistently stored - * locally. - * @return the unresolved <tt>Contact</tt> created from the specified - * <tt>address</tt> and <tt>persistentData</tt> - */ - public Contact createUnresolvedContact(String address, - String persistentData) - { - return createUnresolvedContact(address - , persistentData - , getServerStoredContactListRoot()); - } - - /** - * Creates and returns a unresolved contact from the specified - * <tt>address</tt> and <tt>persistentData</tt>. The method will not try - * to establish a network connection and resolve the newly created Contact - * against the server. The protocol provider may will later try and resolve - * the contact. When this happens the corresponding event would notify - * interested subscription listeners. - * - * @param address an identifier of the contact that we'll be creating. - * @param persistentData a String returned Contact's getPersistentData() - * method during a previous run and that has been persistently stored - * locally. - * @param parent the group where the unresolved contact is - * supposed to belong to. - * - * @return the unresolved <tt>Contact</tt> created from the specified - * <tt>address</tt> and <tt>persistentData</tt> - */ - public Contact createUnresolvedContact(String address, - String persistentData, - ContactGroup parent) - { - return null; - } - - /** - * Looks for a zeroconf protocol provider registered for a user id matching - * <tt>zeroconfUserID</tt>. - * - * @param zeroconfUserID the ID of the Zeroconf user whose corresponding - * protocol provider we'd like to find. - * @return ProtocolProviderServiceZeroconfImpl a zeroconf protocol - * provider registered for a user with id <tt>zeroconfUserID</tt> or null - * if there is no such protocol provider. - */ - public ProtocolProviderServiceZeroconfImpl - findProviderForZeroconfUserID(String zeroconfUserID) - { - BundleContext bc = ZeroconfActivator.getBundleContext(); - - String osgiQuery = "(&" + - "(" + ProtocolProviderFactory.PROTOCOL + - "=" + ProtocolNames.ZEROCONF + ")" + - "(" + ProtocolProviderFactory.USER_ID + - "=" + zeroconfUserID + "))"; - - ServiceReference[] refs = null; - try - { - refs = bc.getServiceReferences( - ProtocolProviderService.class.getName(), - osgiQuery); - } - catch (InvalidSyntaxException ex) - { - logger.error("Failed to execute the following osgi query: " - + osgiQuery - , ex); - } - - if(refs != null && refs.length > 0) - { - return (ProtocolProviderServiceZeroconfImpl)bc.getService(refs[0]); - } - - return null; - } - - /** - * Creates and returns a unresolved contact group from the specified - * <tt>address</tt> and <tt>persistentData</tt>. The method will not try - * to establish a network connection and resolve the newly created - * <tt>ContactGroup</tt> against the server or the contact itself. The - * protocol provider will later resolve the contact group. When this happens - * the corresponding event would notify interested subscription listeners. - * - * @param groupUID an identifier, returned by ContactGroup's getGroupUID, - * that the protocol provider may use in order to create the group. - * @param persistentData a String returned ContactGroups's - * getPersistentData() method during a previous run and that has been - * persistently stored locally. - * @param parentGroup the group under which the new group is to be created - * or null if this is group directly underneath the root. - * @return the unresolved <tt>ContactGroup</tt> created from the specified - * <tt>uid</tt> and <tt>persistentData</tt> - */ - public ContactGroup createUnresolvedContactGroup(String groupUID, - String persistentData, ContactGroup parentGroup) - { - ContactGroupZeroconfImpl newGroup - = new ContactGroupZeroconfImpl( - ContactGroupZeroconfImpl.createNameFromUID(groupUID) - , parentProvider); - newGroup.setResolved(false); - - //if parent is null then we're adding under root. - if(parentGroup == null) - parentGroup = getServerStoredContactListRoot(); - - ((ContactGroupZeroconfImpl)parentGroup).addSubgroup(newGroup); - - this.fireServerStoredGroupEvent( - newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT); - - return newGroup; - } - - private class UnregistrationListener - implements RegistrationStateChangeListener - { - /** - * The method is called by a ProtocolProvider implementation whenver - * a change in the registration state of the corresponding provider had - * occurred. The method is particularly interested in events stating - * that the zeroconf provider has unregistered so that it would fire - * status change events for all contacts in our buddy list. - * - * @param evt ProviderStatusChangeEvent the event describing the status - * change. - */ - public void registrationStateChanged(RegistrationStateChangeEvent evt) - { - - if (logger.isDebugEnabled()) - logger.debug("ZEROCONF : The Zeroconf provider changed state from: " - + evt.getOldState() - + " to: " + evt.getNewState()); - - //send event notifications saying that all our buddies are - //offline. The Zeroconf protocol does not implement top level buddies - //nor subgroups for top level groups so a simple nested loop - //would be enough. - Iterator<ContactGroup> groupsIter - = getServerStoredContactListRoot().subgroups(); - while (groupsIter.hasNext()) - { - ContactGroup group = groupsIter.next(); - Iterator<Contact> contactsIter = group.contacts(); - - while (contactsIter.hasNext()) - { - ContactZeroconfImpl contact - = (ContactZeroconfImpl) contactsIter.next(); - - PresenceStatus oldContactStatus - = contact.getPresenceStatus(); - - /* We set contacts to OFFLINE and send an event so that external listeners - * can be aware that the contacts are reachable anymore. Dunno if that's - * a good idea. Can be erased if not. Contacts clean is directly done by the - * contact status change handler. - */ - if (!oldContactStatus.isOnline()) - { - //contact.setPresenceStatus(ZeroconfStatusEnum.OFFLINE); - fireContactPresenceStatusChangeEvent( - contact - , contact.getParentContactGroup() - , oldContactStatus); - } - } - } - } - } - - /** - * Returns the volatile group or null if this group has not yet been - * created. - * - * @return a volatile group existing in our contact list or <tt>null</tt> - * if such a group has not yet been created. - */ - public ContactGroupZeroconfImpl getNonPersistentGroup() - { - for (int i = 0; - i < getServerStoredContactListRoot().countSubgroups(); - i++) - { - ContactGroupZeroconfImpl gr = - (ContactGroupZeroconfImpl)getServerStoredContactListRoot() - .getGroup(i); - - if(!gr.isPersistent()) - return gr; - } - - return null; - } - - - /** - * Creates a non persistent contact for the specified address. This would - * also create (if necessary) a group for volatile contacts that would not - * be added to the server stored contact list. This method would have no - * effect on the server stored contact list. - * @return the newly created volatile contact. - * @param bonjourService BonjourService responsible for the chat with this contact - * @param name Display name of the contact - * @param ip IP address of the contact - * @param port Port declared by the contact for direct chat - * @param contactAddress the address of the volatile contact we'd like to - * create. - */ - public ContactZeroconfImpl createVolatileContact(String contactAddress, - BonjourService bonjourService, - String name, - InetAddress ip, - int port) - { - //First create the new volatile contact; - ContactZeroconfImpl newVolatileContact - = new ContactZeroconfImpl(contactAddress, - this.parentProvider, bonjourService, name, ip, port); - newVolatileContact.setPersistent(false); - - - //Check whether a volatile group already exists and if not create - //one - ContactGroupZeroconfImpl theVolatileGroup = getNonPersistentGroup(); - - - //if the parent volatile group is null then we create it - if (theVolatileGroup == null) - { - theVolatileGroup = new ContactGroupZeroconfImpl( - "Bonjour" - , parentProvider); - theVolatileGroup.setResolved(false); - theVolatileGroup.setPersistent(false); - - this.contactListRoot.addSubgroup(theVolatileGroup); - - fireServerStoredGroupEvent(theVolatileGroup - , ServerStoredGroupEvent.GROUP_CREATED_EVENT); - } - - //now add the volatile contact instide it - theVolatileGroup.addContact(newVolatileContact); - fireSubscriptionEvent(newVolatileContact - , theVolatileGroup - , SubscriptionEvent.SUBSCRIPTION_CREATED); - - return newVolatileContact; - } - - public Contact getLocalContact() - { - return null; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetTypingNotificationsZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetTypingNotificationsZeroconfImpl.java deleted file mode 100644 index db397b8..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetTypingNotificationsZeroconfImpl.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import net.java.sip.communicator.service.protocol.*; - -/** - * Implements typing notifications for the Zeroconf protocol. The operation - * set would simply mirror all outgoing typing notifications and make them - * appear as incoming events generated by the contact that we are currently - * writing a message to. - * - * @author Christian Vincenot - * @author Maxime Catelin - * @author Jonathan Martin - */ -public class OperationSetTypingNotificationsZeroconfImpl - extends AbstractOperationSetTypingNotifications<ProtocolProviderServiceZeroconfImpl> -{ - - /** - * Creates a new instance of this operation set and keeps the parent - * provider as a reference. - * - * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt> - * that created us and that we'll use for retrieving the underlying aim - * connection. - */ - OperationSetTypingNotificationsZeroconfImpl( - ProtocolProviderServiceZeroconfImpl provider) - { - super(provider); - } - - /** - * Sends a notification to <tt>notifiedContatct</tt> that we have entered - * <tt>typingState</tt>. - * - * @param notifiedContact the <tt>Contact</tt> to notify - * @param typingState the typing state that we have entered. - * - * @throws java.lang.IllegalStateException if the underlying stack is - * not registered and initialized. - * @throws java.lang.IllegalArgumentException if <tt>notifiedContact</tt> is - * not an instance belonging to the underlying implementation. - */ - public void sendTypingNotification(Contact notifiedContact, int typingState) - throws IllegalStateException, IllegalArgumentException - { - if( !(notifiedContact instanceof ContactZeroconfImpl) ) - throw new IllegalArgumentException( - "The specified contact is not a Zeroconf contact." - + notifiedContact); - - ContactZeroconfImpl to = (ContactZeroconfImpl)notifiedContact; - - ClientThread thread = to.getClientThread(); - if (thread == null) return;/*throw new IllegalStateException( - "No communication channel opened to chat with this contact");*/ - - if (typingState != STATE_TYPING) - return; - - MessageZeroconfImpl message = - new MessageZeroconfImpl("",null, MessageZeroconfImpl.TYPING); - thread.sendMessage(message); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolIconZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolIconZeroconfImpl.java deleted file mode 100644 index 4c6ce07..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolIconZeroconfImpl.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.io.*; -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -import org.jitsi.service.resources.*; -import org.osgi.framework.*; - -/** - * Represents the Zeroconf protocol icon. Implements the <tt>ProtocolIcon</tt> - * interface in order to provide a Zeroconf logo image in two different sizes. - * - * @author Christian Vincenot - * @author Jonathan Martin - */ -public class ProtocolIconZeroconfImpl - implements ProtocolIcon -{ - private static Logger logger - = Logger.getLogger(ProtocolIconZeroconfImpl.class); - - private static ResourceManagementService resourcesService; - - /** - * A hash table containing the protocol icon in different sizes. - */ - private static Hashtable<String, byte[]> iconsTable - = new Hashtable<String, byte[]>(); - static - { - iconsTable.put(ProtocolIcon.ICON_SIZE_16x16, - getImageInBytes("service.protocol.zeroconf.ZEROCONF_16x16")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_32x32, - getImageInBytes("service.protocol.zeroconf.ZEROCONF_32x32")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_48x48, - getImageInBytes("service.protocol.zeroconf.ZEROCONF_48x48")); - - iconsTable.put(ProtocolIcon.ICON_SIZE_64x64, - getImageInBytes("service.protocol.zeroconf.ZEROCONF_64x64")); - } - - /** - * A hash table containing the protocol icon in different sizes. - */ - private static Hashtable<String, String> iconPathsTable - = new Hashtable<String, String>(); - static - { - iconPathsTable.put(ProtocolIcon.ICON_SIZE_16x16, - getResources().getImagePath( - "service.protocol.zeroconf.ZEROCONF_16x16")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_32x32, - getResources().getImagePath( - "service.protocol.zeroconf.ZEROCONF_32x32")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_48x48, - getResources().getImagePath( - "service.protocol.zeroconf.ZEROCONF_48x48")); - - iconPathsTable.put(ProtocolIcon.ICON_SIZE_64x64, - getResources().getImagePath( - "service.protocol.zeroconf.ZEROCONF_64x64")); - } - - /** - * Implements the <tt>ProtocolIcon.getSupportedSizes()</tt> method. Returns - * an iterator to a set containing the supported icon sizes. - * @return an iterator to a set containing the supported icon sizes - */ - public Iterator<String> getSupportedSizes() - { - return iconsTable.keySet().iterator(); - } - - /** - * Returns TRUE if a icon with the given size is supported, FALSE-otherwise. - * @param iconSize Icon size - * @return True if this size is supported, false otherwise - */ - public boolean isSizeSupported(String iconSize) - { - return iconsTable.containsKey(iconSize); - } - - /** - * Returns the icon image in the given size. - * @param iconSize the icon size; one of ICON_SIZE_XXX constants - * @return Icon image - */ - public byte[] getIcon(String iconSize) - { - return iconsTable.get(iconSize); - } - - /** - * Returns a path to the icon with the given size. - * @param iconSize the size of the icon we're looking for - * @return the path to the icon with the given size - */ - public String getIconPath(String iconSize) - { - return iconPathsTable.get(iconSize); - } - - /** - * Returns the icon image used to represent the protocol connecting state. - * @return the icon image used to represent the protocol connecting state - */ - public byte[] getConnectingIcon() - { - return getImageInBytes("zeroconfOnlineIcon"); - } - - /** - * Returns the byte representation of the image corresponding to the given - * identifier. - * - * @param imageID the identifier of the image - * @return the byte representation of the image corresponding to the given - * identifier. - */ - public static byte[] getImageInBytes(String imageID) - { - InputStream in = getResources(). - getImageInputStream(imageID); - - if (in == null) - return null; - byte[] image = null; - try - { - image = new byte[in.available()]; - - in.read(image); - } - catch (IOException e) - { - logger.error("Failed to load image:" + imageID, e); - } - - return image; - } - - /** - * Returns the <tt>ResourceManagementService</tt>. - * - * @return the <tt>ResourceManagementService</tt> - */ - public static ResourceManagementService getResources() - { - if (resourcesService == null) - { - ServiceReference serviceReference = ZeroconfActivator.bundleContext - .getServiceReference(ResourceManagementService.class.getName()); - - if(serviceReference == null) - return null; - - resourcesService - = (ResourceManagementService)ZeroconfActivator.bundleContext - .getService(serviceReference); - } - - return resourcesService; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderFactoryZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderFactoryZeroconfImpl.java deleted file mode 100644 index e68033f..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderFactoryZeroconfImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -import org.osgi.framework.*; - -/** - * The Zeroconf protocol provider factory creates instances of the Zeroconf - * protocol provider service. One Service instance corresponds to one account. - * - * - * @author Christian Vincenot - * @author Maxime Catelin - */ -public class ProtocolProviderFactoryZeroconfImpl - extends ProtocolProviderFactory -{ - - /** - * Creates an instance of the ProtocolProviderFactoryZeroconfImpl. - */ - public ProtocolProviderFactoryZeroconfImpl() - { - super(ZeroconfActivator.getBundleContext(), ProtocolNames.ZEROCONF); - } - - /** - * Initializaed and creates an account corresponding to the specified - * accountProperties and registers the resulting ProtocolProvider in the - * <tt>context</tt> BundleContext parameter. - * - * @param userIDStr tha/a user identifier uniquely representing the newly - * created account within the protocol namespace. - * @param accountProperties a set of protocol (or implementation) - * specific properties defining the new account. - * @return the AccountID of the newly created account. - */ - @Override - public AccountID installAccount( String userIDStr, - Map<String, String> accountProperties) - { - BundleContext context - = ZeroconfActivator.getBundleContext(); - if (context == null) - throw new NullPointerException( - "The specified BundleContext was null"); - - if (userIDStr == null) - throw new NullPointerException( - "The specified AccountID was null"); - - if (accountProperties == null) - throw new NullPointerException( - "The specified property map was null"); - - accountProperties.put(USER_ID, userIDStr); - - AccountID accountID = - new ZeroconfAccountID(userIDStr, accountProperties); - - //make sure we haven't seen this account id before. - if (registeredAccounts.containsKey(accountID)) - throw new IllegalStateException( - "An account for id " + userIDStr + " was already installed!"); - - //first store the account and only then load it as the load generates - //an osgi event, the osgi event triggers (through the UI) a call to the - //ProtocolProviderService.register() method and it needs to access - //the configuration service and check for a stored password. - this.storeAccount(accountID, false); - - accountID = loadAccount(accountProperties); - - return accountID; - } - - @Override - protected AccountID createAccountID(String userID, Map<String, String> accountProperties) - { - return new ZeroconfAccountID(userID, accountProperties); - } - - @Override - protected ProtocolProviderService createService(String userID, - AccountID accountID) - { - ProtocolProviderServiceZeroconfImpl service = - new ProtocolProviderServiceZeroconfImpl(); - - service.initialize(userID, accountID); - return service; - } - - @Override - public void modifyAccount( ProtocolProviderService protocolProvider, - Map<String, String> accountProperties) - throws NullPointerException - { - // TODO Auto-generated method stub - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderServiceZeroconfImpl.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderServiceZeroconfImpl.java deleted file mode 100644 index 7fe916f..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderServiceZeroconfImpl.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; - -/** - * An implementation of the protocol provider service over the Zeroconf protocol - * - * @author Christian Vincenot - * @author Maxime Catelin - */ -public class ProtocolProviderServiceZeroconfImpl - extends AbstractProtocolProviderService -{ - /** - * The logger for this class. - */ - private static final Logger logger = - Logger.getLogger(ProtocolProviderServiceZeroconfImpl.class); - - /** - * We use this to lock access to initialization. - */ - private final Object initializationLock = new Object(); - - /** - * The id of the account that this protocol provider represents. - */ - private AccountID accountID = null; - - /** - * Indicates whether or not the provider is initialized and ready for use. - */ - private boolean isInitialized = false; - - /** - * The logo corresponding to the zeroconf protocol. - */ - private final ProtocolIconZeroconfImpl zeroconfIcon - = new ProtocolIconZeroconfImpl(); - - /** - * The registration state that we are currently in. Note that in a real - * world protocol implementation this field won't exist and the registration - * state would be retrieved from the protocol stack. - */ - private RegistrationState currentRegistrationState - = RegistrationState.UNREGISTERED; - - /** - * The BonjourService corresponding to this ProtocolProviderService - */ - - private BonjourService bonjourService; - - /** - * The default constructor for the Zeroconf protocol provider. - */ - public ProtocolProviderServiceZeroconfImpl() - { - if (logger.isTraceEnabled()) - logger.trace("Creating a zeroconf provider."); - } - - /** - * Returns the AccountID that uniquely identifies the account represented - * by this instance of the ProtocolProviderService. - * - * @return the id of the account represented by this provider. - */ - public AccountID getAccountID() - { - return accountID; - } - - /** - * Returns the Bonjour Service that handles the Bonjour protocol stack. - * - *@return the Bonjour Service linked with this Protocol Provider - */ - public BonjourService getBonjourService() - { - return bonjourService; - } - - /** - * Initializes the service implementation, and puts it in a sate where it - * could interoperate with other services. It is strongly recomended that - * properties in this Map be mapped to property names as specified by - * <tt>AccountProperties</tt>. - * - * @param userID the user id of the zeroconf account we're currently - * initializing - * @param accountID the identifier of the account that this protocol - * provider represents. - * - * @see net.java.sip.communicator.service.protocol.AccountID - */ - protected void initialize(String userID, - AccountID accountID) - { - synchronized(initializationLock) - { - this.accountID = accountID; - - - //initialize the presence operationset - OperationSetPersistentPresenceZeroconfImpl persistentPresence = - new OperationSetPersistentPresenceZeroconfImpl(this); - - addSupportedOperationSet( - OperationSetPersistentPresence.class, - persistentPresence); - //register it once again for those that simply need presence and - //won't be smart enough to check for a persistent presence - //alternative - addSupportedOperationSet( - OperationSetPresence.class, - persistentPresence); - - //initialize the IM operation set - addSupportedOperationSet( - OperationSetBasicInstantMessaging.class, - new OperationSetBasicInstantMessagingZeroconfImpl( - this, - persistentPresence)); - - //initialize the typing notifications operation set - addSupportedOperationSet( - OperationSetTypingNotifications.class, - new OperationSetTypingNotificationsZeroconfImpl(this)); - - isInitialized = true; - } - } - - /** - * Returns the short name of the protocol that the implementation of this - * provider is based upon (like SIP, Jabber, ICQ/AIM, or others for - * example). - * - * @return a String containing the short name of the protocol this - * service is implementing (most often that would be a name in - * ProtocolNames). - */ - public String getProtocolName() - { - return ProtocolNames.ZEROCONF; - } - - /** - * Returns the state of the registration of this protocol provider with - * the corresponding registration service. - * - * @return ProviderRegistrationState - */ - public RegistrationState getRegistrationState() - { - return currentRegistrationState; - } - - /** - * Starts the registration process. - * - * @param authority the security authority that will be used for - * resolving any security challenges that may be returned during the - * registration or at any moment while wer're registered. - * @throws OperationFailedException with the corresponding code it the - * registration fails for some reason (e.g. a networking error or an - * implementation problem). - */ - public void register(SecurityAuthority authority) - throws OperationFailedException - { - //we don't need a password here since there's no server in - //zeroconf. - - RegistrationState oldState = currentRegistrationState; - currentRegistrationState = RegistrationState.REGISTERED; - - - //ICI : creer le service Zeroconf !! - if (logger.isInfoEnabled()) - logger.info("ZEROCONF: Starting the service"); - this.bonjourService = new BonjourService(5298, this); - - //bonjourService.changeStatus(ZeroconfStatusEnum.ONLINE); - - fireRegistrationStateChanged( - oldState - , currentRegistrationState - , RegistrationStateChangeEvent.REASON_USER_REQUEST - , null); - } - - /** - * Makes the service implementation close all open sockets and release - * any resources that it might have taken and prepare for - * shutdown/garbage collection. - */ - public void shutdown() - { - if(!isInitialized) - { - return; - } - if (logger.isTraceEnabled()) - logger.trace("Killing the Zeroconf Protocol Provider."); - - if(isRegistered()) - { - try - { - //do the unregistration - unregister(); - } - catch (OperationFailedException ex) - { - //we're shutting down so we need to silence the exception here - logger.error( - "Failed to properly unregister before shutting down. " - + getAccountID() - , ex); - } - } - - isInitialized = false; - } - - /** - * Ends the registration of this protocol provider with the current - * registration service. - * - * @throws OperationFailedException with the corresponding code it the - * registration fails for some reason (e.g. a networking error or an - * implementation problem). - */ - public void unregister() - throws OperationFailedException - { - RegistrationState oldState = currentRegistrationState; - currentRegistrationState = RegistrationState.UNREGISTERED; - - if(bonjourService != null) - bonjourService.shutdown(); - - fireRegistrationStateChanged( - oldState - , currentRegistrationState - , RegistrationStateChangeEvent.REASON_USER_REQUEST - , null); - } - - /* - * (non-Javadoc) - * - * @see net.java.sip.communicator.service.protocol.ProtocolProviderService# - * isSignallingTransportSecure() - */ - public boolean isSignalingTransportSecure() - { - return false; - } - - /** - * Returns the "transport" protocol of this instance used to carry the - * control channel for the current protocol service. - * - * @return The "transport" protocol of this instance: TCP. - */ - public TransportProtocol getTransportProtocol() - { - return TransportProtocol.TCP; - } - - /** - * Returns the zeroconf protocol icon. - * @return the zeroconf protocol icon - */ - public ProtocolIcon getProtocolIcon() - { - return zeroconfIcon; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfAccountID.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfAccountID.java deleted file mode 100644 index 64e22c7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfAccountID.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * The Zeroconf implementation of a sip-communicator AccountID - * - * @author Christian Vincenot - */ -public class ZeroconfAccountID - extends AccountID -{ - /* Firstname, lastname, mail address */ - private String first = null; - private String last = null; - private String mail = null; - - private boolean rememberContacts = false; - - /** - * Creates a zeroconf account id from the specified id and account - * properties. - * @param userID id identifying this account - * @param accountProperties any other properties necessary for the account. - */ - ZeroconfAccountID(String userID, Map<String, String> accountProperties) - { - super(userID, - accountProperties, - ProtocolNames.ZEROCONF, - "zeroconf.org"); - - first = accountProperties.get("first"); - last = accountProperties.get("last"); - mail = accountProperties.get("mail"); - - rememberContacts = - new Boolean(accountProperties.get("rememberContacts")) - .booleanValue(); - } - - /** - * Returns a String representing the firstname of this user. - * @return String representing the firstname of this user. - */ - public String getFirst() - { - return first; - } - - /** - * Returns a String representing the lastname of this user. - * @return String representing the lastname of this user. - */ - public String getLast() - { - return last; - } - - /** - * Returns a String representing the mail address of this user. - * @return String representing the mail address of this user. - */ - public String getMail() - { - return mail; - } - - /** - * Returns a boolean indicating if we store the contacts we meet or not. - * @return boolean indicating if we store the contacts we meet or not. - */ - public boolean isRememberContacts() - { - return rememberContacts; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfActivator.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfActivator.java deleted file mode 100644 index 2544bab..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfActivator.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -import org.osgi.framework.*; - -/** - * Loads the Zeroconf provider factory and registers its services in the OSGI - * bundle context. - * - * @author Christian Vincenot - * @author Maxime Catelin - */ -public class ZeroconfActivator - implements BundleActivator -{ - private static final Logger logger - = Logger.getLogger(ZeroconfActivator.class); - - /** - * A reference to the registration of our Zeroconf protocol provider - * factory. - */ - private ServiceRegistration zeroconfPpFactoryServReg = null; - - /** - * A reference to the Zeroconf protocol provider factory. - */ - private static ProtocolProviderFactoryZeroconfImpl - zeroconfProviderFactory = null; - - /** - * The currently valid bundle context. - */ - static BundleContext bundleContext = null; - - - /** - * Called when this bundle is started. In here we'll export the - * zeroconf ProtocolProviderFactory implementation so that it could be - * possible to register accounts with it in SIP Communicator. - * - * @param context The execution context of the bundle being started. - * @throws Exception If this method throws an exception, this bundle is - * marked as stopped and the Framework will remove this bundle's - * listeners, unregister all services registered by this bundle, and - * release all services used by this bundle. - */ - public void start(BundleContext context) - throws Exception - { -// logger.setLevelAll(); - - bundleContext = context; - - Hashtable<String, String> hashtable = new Hashtable<String, String>(); - hashtable.put(ProtocolProviderFactory.PROTOCOL, "Zeroconf"); - - zeroconfProviderFactory = new ProtocolProviderFactoryZeroconfImpl(); - - //register the zeroconf provider factory. - zeroconfPpFactoryServReg = context.registerService( - ProtocolProviderFactory.class.getName(), - zeroconfProviderFactory, - hashtable); - - if (logger.isInfoEnabled()) - logger.info("Zeroconf protocol implementation [STARTED]."); - } - - /** - * Returns a reference to the bundle context that we were started with. - * @return a reference to the BundleContext instance that we were started - * witn. - */ - public static BundleContext getBundleContext() - { - return bundleContext; - } - - /** - * Retrurns a reference to the protocol provider factory that we have - * registered. - * @return a reference to the <tt>ProtocolProviderFactoryJabberImpl</tt> - * instance that we have registered from this package. - */ - public static ProtocolProviderFactoryZeroconfImpl getProtocolProviderFactory() - { - return zeroconfProviderFactory; - } - - - /** - * Called when this bundle is stopped so the Framework can perform the - * bundle-specific activities necessary to stop the bundle. - * - * @param context The execution context of the bundle being stopped. - * @throws Exception If this method throws an exception, the bundle is - * still marked as stopped, and the Framework will remove the bundle's - * listeners, unregister all services registered by the bundle, and - * release all services used by the bundle. - */ - public void stop(BundleContext context) - throws Exception - { - zeroconfProviderFactory.stop(); - zeroconfPpFactoryServReg.unregister(); - - if (logger.isInfoEnabled()) - logger.info("Zeroconf protocol implementation [STOPPED]."); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfStatusEnum.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfStatusEnum.java deleted file mode 100644 index 3fe2745..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfStatusEnum.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright @ 2015 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf; - -import java.util.*; - -import net.java.sip.communicator.service.protocol.*; - -/** - * An implementation of <tt>PresenceStatus</tt> that enumerates all states that - * a Zeroconf contact can fall into. - * - * @author Christian Vincenot - * @author Jonathan Martin - */ -public class ZeroconfStatusEnum - extends PresenceStatus -{ - - /** - * Indicates an Offline status or status with 0 connectivity. - */ - public static final ZeroconfStatusEnum OFFLINE - = new ZeroconfStatusEnum( - 0, - "Offline", - ProtocolIconZeroconfImpl.getImageInBytes( - "service.protocol.zeroconf.OFFLINE_STATUS_ICON")); - - /** - * The DND status. Indicates that the user has connectivity but prefers - * not to be contacted. - */ - public static final ZeroconfStatusEnum DO_NOT_DISTURB - = new ZeroconfStatusEnum( - 30, - "Do Not Disturb",//, "Do Not Disturb", - ProtocolIconZeroconfImpl.getImageInBytes( - "service.protocol.zeroconf.DND_STATUS_ICON")); - - /** - * The Invisible status. Indicates that the user has connectivity even - * though it may appear otherwise to others, to whom she would appear to be - * offline. - */ - public static final ZeroconfStatusEnum INVISIBLE - = new ZeroconfStatusEnum( - 45, - "Invisible", - ProtocolIconZeroconfImpl.getImageInBytes( - "service.protocol.zeroconf.INVISIBLE_STATUS_ICON")); - - /** - * The Online status. Indicate that the user is able and willing to - * communicate. - */ - public static final ZeroconfStatusEnum ONLINE - = new ZeroconfStatusEnum( - 65, - "Available",//, "Online" - ProtocolIconZeroconfImpl.getImageInBytes( - "service.protocol.zeroconf.ONLINE_STATUS_ICON")); - - - /** - * Initialize the list of supported status states. - */ - private static List<PresenceStatus> supportedStatusSet = new LinkedList<PresenceStatus>(); - static - { - supportedStatusSet.add(OFFLINE); - supportedStatusSet.add(DO_NOT_DISTURB); - - /* INVISIBLE STATUS could be supported by unregistering JmDNS and - * accepting unknown contacts' messages */ - //supportedStatusSet.add(INVISIBLE); - - supportedStatusSet.add(ONLINE); - } - - /** - * Creates an instance of <tt>ZeroconfPresneceStatus</tt> with the - * specified parameters. - * @param status the connectivity level of the new presence status instance - * @param statusName the name of the presence status. - * @param statusIcon the icon associated with this status - */ - private ZeroconfStatusEnum(int status, - String statusName, - byte[] statusIcon) - { - super(status, statusName, statusIcon); - } - - /** - * Returns an iterator over all status instances supproted by the zeroconf - * provider. - * @return an <tt>Iterator</tt> over all status instances supported by the - * zeroconf provider. - */ - static Iterator<PresenceStatus> supportedStatusSet() - { - return supportedStatusSet.iterator(); - } - - /** - * @param status String representation of the status - * @return ZeroconfStatusEnum corresponding the supplied String value - */ - static ZeroconfStatusEnum statusOf(String status) - { - Iterator<PresenceStatus> statusIter = supportedStatusSet(); - while (statusIter.hasNext()) - { - ZeroconfStatusEnum state = (ZeroconfStatusEnum)statusIter.next(); - if (state.statusName.equalsIgnoreCase(status)) - return state; - } - return null; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java deleted file mode 100644 index 0baaff4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -import net.java.sip.communicator.util.*; - -/** - * A table of DNS entries. This is a hash table which - * can handle multiple entries with the same name. - * <p/> - * Storing multiple entries with the same name is implemented using a - * linked list of <code>CacheNode</code>'s. - * <p/> - * The current implementation of the API of DNSCache does expose the - * cache nodes to clients. Clients must explicitly deal with the nodes - * when iterating over entries in the cache. Here's how to iterate over - * all entries in the cache: - * <pre> - * for (Iterator i=dnscache.iterator(); i.hasNext(); ) - * { - * for ( DNSCache.CacheNode n = (DNSCache.CacheNode) i.next(); - * n != null; - * n.next()) - * { - * DNSEntry entry = n.getValue(); - * ...do something with entry... - * } - * } - * </pre> - * <p/> - * And here's how to iterate over all entries having a given name: - * <pre> - * for ( DNSCache.CacheNode n = (DNSCache.CacheNode) dnscache.find(name); - * n != null; - * n.next()) - * { - * DNSEntry entry = n.getValue(); - * ...do something with entry... - * } - * </pre> - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer, Rick Blair - */ -class DNSCache -{ - private static Logger logger = Logger.getLogger(DNSCache.class.toString()); - // Implementation note: - // We might completely hide the existence of CacheNode's in a future version - // of DNSCache. But this will require to implement two (inner) classes for - // the iterators that will be returned by method <code>iterator()</code> and - // method <code>find(name)</code>. - // Since DNSCache is not a public class, it does not seem worth the effort - // to clean its API up that much. - - // [PJYF Oct 15 2004] This should implements Collections - // that would be amuch cleaner implementation - - /** - * The number of DNSEntry's in the cache. - */ - private int size; - - /** - * The hashtable used internally to store the entries of the cache. - * Keys are instances of String. The String contains an unqualified service - * name. - * Values are linked lists of CacheNode instances. - */ - private HashMap<String, CacheNode> hashtable; - - /** - * Cache nodes are used to implement storage of multiple DNSEntry's of the - * same name in the cache. - */ - public static class CacheNode - { - private DNSEntry value; - private CacheNode next; - - public CacheNode(DNSEntry value) - { - this.value = value; -// String SLevel = System.getProperty("jmdns.debug"); -// if (SLevel == null) -// SLevel = "INFO"; -// logger.setLevel(Level.parse(SLevel)); - } - - public CacheNode next() - { - return next; - } - - public DNSEntry getValue() - { - return value; - } - } - - - /** - * Create a table with a given initial size. - * @param size initial size. - */ - public DNSCache(final int size) - { - hashtable = new HashMap<String, CacheNode>(size); - -// String SLevel = System.getProperty("jmdns.debug"); -// if (SLevel == null) SLevel = "INFO"; -// logger.setLevel(Level.parse(SLevel)); - } - - /** - * Clears the cache. - */ - public synchronized void clear() - { - hashtable.clear(); - size = 0; - } - - /** - * Adds an entry to the table. - * @param entry added to the table. - */ - public synchronized void add(final DNSEntry entry) - { - //logger.log("DNSCache.add("+entry.getName()+")"); - CacheNode newValue = new CacheNode(entry); - CacheNode node = hashtable.get(entry.getName()); - if (node == null) - { - hashtable.put(entry.getName(), newValue); - } - else - { - newValue.next = node.next; - node.next = newValue; - } - size++; - } - - /** - * Remove a specific entry from the table. - * @param entry removed from table. - * @return Returns true if the entry was found. - */ - public synchronized boolean remove(DNSEntry entry) - { - CacheNode node = hashtable.get(entry.getName()); - if (node != null) - { - if (node.value == entry) - { - if (node.next == null) - { - hashtable.remove(entry.getName()); - } - else - { - hashtable.put(entry.getName(), node.next); - } - size--; - return true; - } - - CacheNode previous = node; - node = node.next; - while (node != null) - { - if (node.value == entry) - { - previous.next = node.next; - size--; - return true; - } - previous = node; - node = node.next; - } - ; - } - return false; - } - - /** - * Get a matching DNS entry from the table (using equals). - * @param entry to be found in table. - * @return Returns the entry that was found. - */ - public synchronized DNSEntry get(DNSEntry entry) - { - for (CacheNode node = find(entry.getName()); node != null; node = node.next) - { - if (node.value.equals(entry)) - { - return node.value; - } - } - return null; - } - - /** - * Get a matching DNS entry from the table. - * @param name - * @param type - * @param clazz - * @return Return the entry if found, null otherwise. - */ - public synchronized DNSEntry get(String name, int type, int clazz) - { - for (CacheNode node = find(name); node != null; node = node.next) - { - if (node.value.type == type && node.value.clazz == clazz) - { - return node.value; - } - } - return null; - } - - /** - * Iterates over all cache nodes. - * The iterator returns instances of DNSCache.CacheNode. - * Each instance returned is the first node of a linked list. - * To retrieve all entries, one must iterate over this linked list. See - * code snippets in the header of the class. - * @return Returns iterator with instances of DNSCache.CacheNode. - */ - public Iterator<DNSCache.CacheNode> iterator() - { - return Collections.unmodifiableCollection(hashtable.values()).iterator(); - } - - /** - * Iterate only over items with matching name. - * If an instance is returned, it is the first node of a linked list. - * To retrieve all entries, one must iterate over this linked list. - * @param name to be found. - * @return Returns an instance of DNSCache.CacheNode or null. - */ - public synchronized CacheNode find(String name) - { - return hashtable.get(name); - } - - /** - * List all entries for debugging. - */ - public synchronized void print() - { - for (Iterator<CacheNode> i = iterator(); i.hasNext();) - { - for (CacheNode n = i.next(); n != null; n = n.next) - { - if (logger.isInfoEnabled()) - logger.info(n.value.toString()); - } - } - } - - @Override - public synchronized String toString() - { - StringBuffer aLog = new StringBuffer(); - aLog.append("\t---- cache ----"); - for (Iterator<CacheNode> i = iterator(); i.hasNext();) - { - for (CacheNode n = i.next(); n != null; n = n.next) - { - aLog.append("\n\t\t" + n.value); - } - } - return aLog.toString(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java deleted file mode 100644 index 8792c8d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff, Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -/** - * DNS constants. - * - * @version %I%, %G% - * @author Arthur van Hoff, Jeff Sonstein, - * Werner Randelshofer, Pierre Frisch, Rick Blair - */ -public final class DNSConstants -{ - - // changed to final class - jeffs - final static String MDNS_GROUP = "224.0.0.251"; - final static String MDNS_GROUP_IPV6 = "FF02::FB"; - final static int MDNS_PORT = 5353; - final static int DNS_PORT = 53; - // default one hour TTL - final static int DNS_TTL = 60 * 60; - // two hour TTL (draft-cheshire-dnsext-multicastdns.txt ch 13) - // final static int DNS_TTL = 120 * 60; - - final static int MAX_MSG_TYPICAL = 1460; - final static int MAX_MSG_ABSOLUTE = 8972; - - final static int FLAGS_QR_MASK = 0x8000; // Query response mask - final static int FLAGS_QR_QUERY = 0x0000; // Query - final static int FLAGS_QR_RESPONSE = 0x8000;// Response - - public final static int FLAGS_AA = 0x0400; // Authorative answer - final static int FLAGS_TC = 0x0200; // Truncated - final static int FLAGS_RD = 0x0100; // Recursion desired - public final static int FLAGS_RA = 0x8000; // Recursion available - - final static int FLAGS_Z = 0x0040; // Zero - final static int FLAGS_AD = 0x0020; // Authentic data - final static int FLAGS_CD = 0x0010; // Checking disabled - - // Final Static Internet - public final static int CLASS_IN = 1; - // CSNET - final static int CLASS_CS = 2; - // CHAOS - final static int CLASS_CH = 3; - // Hesiod - final static int CLASS_HS = 4; - // Used in DNS UPDATE [RFC 2136] - final static int CLASS_NONE = 254; - // Not a DNS class, but a DNS query class, meaning "all classes" - final static int CLASS_ANY = 255; - // Multicast DNS uses the bottom 15 bits to identify the record class... - final static int CLASS_MASK = 0x7FFF; - // ... and the top bit indicates that all other cached records are now invalid - public final static int CLASS_UNIQUE = 0x8000; - - final static int TYPE_IGNORE = 0; // This is a hack to stop further processing - public final static int TYPE_A = 1; // Address - final static int TYPE_NS = 2; // Name Server - final static int TYPE_MD = 3; // Mail Destination - final static int TYPE_MF = 4; // Mail Forwarder - final static int TYPE_CNAME = 5; // Canonical Name - final static int TYPE_SOA = 6; // Start of Authority - final static int TYPE_MB = 7; // Mailbox - final static int TYPE_MG = 8; // Mail Group - final static int TYPE_MR = 9; // Mail Rename - final static int TYPE_NULL = 10; // NULL RR - final static int TYPE_WKS = 11; // Well-known-service - final static int TYPE_PTR = 12; // Domain Name pofinal static inter - final static int TYPE_HINFO = 13; // Host information - final static int TYPE_MINFO = 14; // Mailbox information - final static int TYPE_MX = 15; // Mail exchanger - public final static int TYPE_TXT = 16;// Arbitrary text string - final static int TYPE_RP = 17; // for Responsible Person [RFC1183] - final static int TYPE_AFSDB = 18; // for AFS Data Base location [RFC1183] - final static int TYPE_X25 = 19; // for X.25 PSDN address [RFC1183] - final static int TYPE_ISDN = 20; // for ISDN address [RFC1183] - final static int TYPE_RT = 21; // for Route Through [RFC1183] - final static int TYPE_NSAP = 22; // for NSAP address, NSAP style A record [RFC1706] - final static int TYPE_NSAP_PTR = 23;// - final static int TYPE_SIG = 24; // for security signature [RFC2931] - final static int TYPE_KEY = 25; // for security key [RFC2535] - final static int TYPE_PX = 26; // X.400 mail mapping information [RFC2163] - final static int TYPE_GPOS = 27; // Geographical Position [RFC1712] - final static int TYPE_AAAA = 28; // IP6 Address [Thomson] - final static int TYPE_LOC = 29; // Location Information [Vixie] - final static int TYPE_NXT = 30; // Next Domain - OBSOLETE [RFC2535, RFC3755] - final static int TYPE_EID = 31; // Endpoint Identifier [Patton] - final static int TYPE_NIMLOC = 32; // Nimrod Locator [Patton] - public final static int TYPE_SRV = 33;// Server Selection [RFC2782] - final static int TYPE_ATMA = 34; // ATM Address [Dobrowski] - final static int TYPE_NAPTR = 35; // Naming Authority Pointer [RFC2168, RFC2915] - final static int TYPE_KX = 36; // Key Exchanger [RFC2230] - final static int TYPE_CERT = 37; // CERT [RFC2538] - final static int TYPE_A6 = 38; // A6 [RFC2874] - final static int TYPE_DNAME = 39; // DNAME [RFC2672] - final static int TYPE_SINK = 40; // SINK [Eastlake] - final static int TYPE_OPT = 41; // OPT [RFC2671] - final static int TYPE_APL = 42; // APL [RFC3123] - final static int TYPE_DS = 43; // Delegation Signer [RFC3658] - final static int TYPE_SSHFP = 44; // SSH Key Fingerprint [RFC-ietf-secsh-dns-05.txt] - final static int TYPE_RRSIG = 46; // RRSIG [RFC3755] - final static int TYPE_NSEC = 47; // NSEC [RFC3755] - final static int TYPE_DNSKEY = 48; // DNSKEY [RFC3755] - final static int TYPE_UINFO = 100; // [IANA-Reserved] - final static int TYPE_UID = 101; // [IANA-Reserved] - final static int TYPE_GID = 102; // [IANA-Reserved] - final static int TYPE_UNSPEC = 103; // [IANA-Reserved] - final static int TYPE_TKEY = 249; // Transaction Key [RFC2930] - final static int TYPE_TSIG = 250; // Transaction Signature [RFC2845] - final static int TYPE_IXFR = 251; // Incremental transfer [RFC1995] - final static int TYPE_AXFR = 252; // Transfer of an entire zone [RFC1035] - final static int TYPE_MAILA = 253; // Mailbox-related records (MB, MG or MR) [RFC1035] - final static int TYPE_MAILB = 254; // Mail agent RRs (Obsolete - see MX) [RFC1035] - final static int TYPE_ANY = 255; // Request for all records [RFC1035] - - //Time Intervals for various functions - - //milliseconds before send shared query - final static int SHARED_QUERY_TIME = 20; - //milliseconds between query loops. - final static int QUERY_WAIT_INTERVAL = 225; - //milliseconds between probe loops. - final static int PROBE_WAIT_INTERVAL = 250; - //minimal wait interval for response. - final static int RESPONSE_MIN_WAIT_INTERVAL = 20; - //maximal wait interval for response - final static int RESPONSE_MAX_WAIT_INTERVAL = 115; - //milliseconds to wait after conflict. - final static int PROBE_CONFLICT_INTERVAL = 1000; - //After x tries go 1 time a sec. on probes. - final static int PROBE_THROTTLE_COUNT = 10; - //We only increment the throttle count, if - // the previous increment is inside this interval. - final static int PROBE_THROTTLE_COUNT_INTERVAL = 5000; - //milliseconds between Announce loops. - final static int ANNOUNCE_WAIT_INTERVAL = 1000; - //milliseconds between cache cleanups. - final static int RECORD_REAPER_INTERVAL = 10000; - - final static int KNOWN_ANSWER_TTL = 120; - // 50% of the TTL in milliseconds - final static int ANNOUNCED_RENEWAL_TTL_INTERVAL = DNS_TTL * 500; -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java deleted file mode 100644 index 7132756..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.logging.*; - -/** - * DNS entry with a name, type, and class. This is the base - * class for questions and records. - * - * @version %I%, %G% - * @author Arthur van Hoff, Pierre Frisch, Rick Blair - * @author Christian Vincenot - */ -public class DNSEntry -{ - private static Logger logger = Logger.getLogger(DNSEntry.class.toString()); - String key; - String name; - int type; - int clazz; - boolean unique; - - /** - * Create an entry. - */ - DNSEntry(String name, int type, int clazz) - { - this.key = name.toLowerCase(); - this.name = name; - this.type = type; - this.clazz = clazz & DNSConstants.CLASS_MASK; - this.unique = (clazz & DNSConstants.CLASS_UNIQUE) != 0; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Check if two entries have exactly the same name, type, and class. - */ - @Override - public boolean equals(Object obj) - { - if (obj instanceof DNSEntry) - { - DNSEntry other = (DNSEntry) obj; - return name.equals(other.name) && - type == other.type && - clazz == other.clazz; - } - return false; - } - - public String getName() - { - return name; - } - - public int getType() - { - return type; - } - - public int getClazz() - { - return clazz; - } - - - public boolean isUnique() - { - return unique; - } - - /** - * Overriden, to return a value which is consistent with the value returned - * by equals(Object). - */ - @Override - public int hashCode() - { - return name.hashCode() + type + clazz; - } - - /** - * Get a string given a clazz. - */ - static String getClazz(int clazz) - { - switch (clazz & DNSConstants.CLASS_MASK) - { - case DNSConstants.CLASS_IN: - return "in"; - case DNSConstants.CLASS_CS: - return "cs"; - case DNSConstants.CLASS_CH: - return "ch"; - case DNSConstants.CLASS_HS: - return "hs"; - case DNSConstants.CLASS_NONE: - return "none"; - case DNSConstants.CLASS_ANY: - return "any"; - default: - return "?"; - } - } - - /** - * Get a string given a type. - */ - static String getType(int type) - { - switch (type) - { - case DNSConstants.TYPE_A: - return "a"; - case DNSConstants.TYPE_AAAA: - return "aaaa"; - case DNSConstants.TYPE_NS: - return "ns"; - case DNSConstants.TYPE_MD: - return "md"; - case DNSConstants.TYPE_MF: - return "mf"; - case DNSConstants.TYPE_CNAME: - return "cname"; - case DNSConstants.TYPE_SOA: - return "soa"; - case DNSConstants.TYPE_MB: - return "mb"; - case DNSConstants.TYPE_MG: - return "mg"; - case DNSConstants.TYPE_MR: - return "mr"; - case DNSConstants.TYPE_NULL: - return "null"; - case DNSConstants.TYPE_WKS: - return "wks"; - case DNSConstants.TYPE_PTR: - return "ptr"; - case DNSConstants.TYPE_HINFO: - return "hinfo"; - case DNSConstants.TYPE_MINFO: - return "minfo"; - case DNSConstants.TYPE_MX: - return "mx"; - case DNSConstants.TYPE_TXT: - return "txt"; - case DNSConstants.TYPE_SRV: - return "srv"; - case DNSConstants.TYPE_ANY: - return "any"; - default: - return "?"; - } - } - - public String toString(String hdr, String other) - { - return hdr + "[" + getType(type) + "," + - getClazz(clazz) + (unique ? "-unique," : ",") + - name + ((other != null) ? "," + - other + "]" : "]"); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java deleted file mode 100644 index 3dcedc4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.*; - -/** - * Parse an incoming DNS message into its components. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch, Daniel Bobbert - */ -final class DNSIncoming -{ - private static Logger logger = Logger.getLogger(DNSIncoming.class.toString()); - // Implementation note: This vector should be immutable. - // If a client of DNSIncoming changes the contents of this vector, - // we get undesired results. To fix this, we have to migrate to - // the Collections API of Java 1.2. i.e we replace Vector by List. - // final static Vector EMPTY = new Vector(); - - private DatagramPacket packet; - private int off; - private int len; - private byte data[]; - - int id; - private int flags; - private int numQuestions; - int numAnswers; - private int numAuthorities; - private int numAdditionals; - private long receivedTime; - - List<DNSEntry> questions; - List<DNSRecord> answers; - - /** - * Parse a message from a datagram packet. - */ - DNSIncoming(DatagramPacket packet) throws IOException - { - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.packet = packet; - this.data = packet.getData(); - this.len = packet.getLength(); - this.off = packet.getOffset(); - this.questions = new LinkedList<DNSEntry>(); - this.answers = new LinkedList<DNSRecord>(); - this.receivedTime = System.currentTimeMillis(); - - try - { - id = readUnsignedShort(); - flags = readUnsignedShort(); - numQuestions = readUnsignedShort(); - numAnswers = readUnsignedShort(); - numAuthorities = readUnsignedShort(); - numAdditionals = readUnsignedShort(); - - // parse questions - if (numQuestions > 0) - { - questions = - Collections.synchronizedList( - new ArrayList<DNSEntry>(numQuestions)); - for (int i = 0; i < numQuestions; i++) - { - DNSQuestion question = - new DNSQuestion( - readName(), - readUnsignedShort(), - readUnsignedShort()); - - questions.add(question); - } - } - - // parse answers - int n = numAnswers + numAuthorities + numAdditionals; - if (n > 0) - { - //System.out.println("JMDNS received "+n+" answers!"); - answers = Collections.synchronizedList( - new ArrayList<DNSRecord>(n)); - for (int i = 0; i < n; i++) - { - String domain = readName(); - int type = readUnsignedShort(); - int clazz = readUnsignedShort(); - int ttl = readInt(); - int len = readUnsignedShort(); - int end = off + len; - DNSRecord rec = null; - - switch (type) - { - case DNSConstants.TYPE_A: // IPv4 - case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested - rec = new DNSRecord.Address( - domain, type, clazz, ttl, readBytes(off, len)); - break; - case DNSConstants.TYPE_CNAME: - case DNSConstants.TYPE_PTR: - rec = new DNSRecord.Pointer( - domain, type, clazz, ttl, readName()); - break; - case DNSConstants.TYPE_TXT: - rec = new DNSRecord.Text( - domain, type, clazz, ttl, readBytes(off, len)); - break; - case DNSConstants.TYPE_SRV: - //System.out.println("JMDNS: One is a SRV field!!"); - rec = new DNSRecord.Service( domain, - type, - clazz, - ttl, - readUnsignedShort(), - readUnsignedShort(), - readUnsignedShort(), - readName()); - break; - case DNSConstants.TYPE_HINFO: - // Maybe we should do something with those - break; - default : - logger.finer("DNSIncoming() unknown type:" + type); - break; - } - - if (rec != null) - { - // Add a record, if we were able to create one. - answers.add(rec); - } - else - { - // Addjust the numbers for the skipped record - if (answers.size() < numAnswers) - { - numAnswers--; - } - else - { - if (answers.size() < numAnswers + numAuthorities) - { - numAuthorities--; - } - else - { - if (answers.size() < numAnswers + - numAuthorities + - numAdditionals) - { - numAdditionals--; - } - } - } - } - off = end; - } - } - } - catch (IOException e) - { - logger.log(Level.WARNING, - "DNSIncoming() dump " + print(true) + "\n exception ", e); - throw e; - } - } - - /** - * Check if the message is a query. - */ - boolean isQuery() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_QUERY; - } - - /** - * Check if the message is truncated. - */ - boolean isTruncated() - { - return (flags & DNSConstants.FLAGS_TC) != 0; - } - - /** - * Check if the message is a response. - */ - boolean isResponse() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_RESPONSE; - } - - private int get(int off) throws IOException - { - if ((off < 0) || (off >= len)) - { - throw new IOException("parser error: offset=" + off); - } - return data[off] & 0xFF; - } - - private int readUnsignedShort() throws IOException - { - return (get(off++) << 8) + get(off++); - } - - private int readInt() throws IOException - { - return (readUnsignedShort() << 16) + readUnsignedShort(); - } - - private byte[] readBytes(int off, int len) throws IOException - { - byte bytes[] = new byte[len]; - System.arraycopy(data, off, bytes, 0, len); - return bytes; - } - - private void readUTF(StringBuffer buf, int off, int len) throws IOException - { - for (int end = off + len; off < end;) - { - int ch = get(off++); - switch (ch >> 4) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - break; - case 12: - case 13: - // 110x xxxx 10xx xxxx - ch = ((ch & 0x1F) << 6) | (get(off++) & 0x3F); - break; - case 14: - // 1110 xxxx 10xx xxxx 10xx xxxx - ch = ((ch & 0x0f) << 12) | - ((get(off++) & 0x3F) << 6) | - (get(off++) & 0x3F); - break; - default: - // 10xx xxxx, 1111 xxxx - ch = ((ch & 0x3F) << 4) | (get(off++) & 0x0f); - break; - } - buf.append((char) ch); - } - } - - private String readName() throws IOException - { - StringBuffer buf = new StringBuffer(); - int off = this.off; - int next = -1; - int first = off; - - while (true) - { - int len = get(off++); - if (len == 0) - { - break; - } - switch (len & 0xC0) - { - case 0x00: - //buf.append("[" + off + "]"); - readUTF(buf, off, len); - off += len; - buf.append('.'); - break; - case 0xC0: - //buf.append("<" + (off - 1) + ">"); - if (next < 0) - { - next = off + 1; - } - off = ((len & 0x3F) << 8) | get(off++); - if (off >= first) - { - throw new IOException( - "bad domain name: possible circular name detected"); - } - first = off; - break; - default: - throw new IOException( - "bad domain name: '" + buf + "' at " + off); - } - } - this.off = (next >= 0) ? next : off; - return buf.toString(); - } - - /** - * Debugging. - */ - String print(boolean dump) - { - StringBuffer buf = new StringBuffer(); - buf.append(toString() + "\n"); - for (Iterator<DNSEntry> iterator = questions.iterator(); - iterator.hasNext();) - { - buf.append(" ques:" + iterator.next() + "\n"); - } - int count = 0; - for (Iterator<DNSRecord> iterator = answers.iterator(); - iterator.hasNext(); - count++) - { - if (count < numAnswers) - { - buf.append(" answ:"); - } - else - { - if (count < numAnswers + numAuthorities) - { - buf.append(" auth:"); - } - else - { - buf.append(" addi:"); - } - } - buf.append(iterator.next() + "\n"); - } - if (dump) - { - for (int off = 0, len = packet.getLength(); off < len; off += 32) - { - int n = Math.min(32, len - off); - if (off < 10) - { - buf.append(' '); - } - if (off < 100) - { - buf.append(' '); - } - buf.append(off); - buf.append(':'); - for (int i = 0; i < n; i++) - { - if ((i % 8) == 0) - { - buf.append(' '); - } - buf.append(Integer.toHexString((data[off + i] & 0xF0) >> 4)); - buf.append(Integer.toHexString((data[off + i] & 0x0F) >> 0)); - } - buf.append("\n"); - buf.append(" "); - for (int i = 0; i < n; i++) - { - if ((i % 8) == 0) - { - buf.append(' '); - } - buf.append(' '); - int ch = data[off + i] & 0xFF; - buf.append(((ch > ' ') && (ch < 127)) ? (char) ch : '.'); - } - buf.append("\n"); - - // limit message size - if (off + 32 >= 256) - { - buf.append("....\n"); - break; - } - } - } - return buf.toString(); - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append(isQuery() ? "dns[query," : "dns[response,"); - if (packet.getAddress() != null) - { - buf.append(packet.getAddress().getHostAddress()); - } - buf.append(':'); - buf.append(packet.getPort()); - buf.append(",len="); - buf.append(packet.getLength()); - buf.append(",id=0x"); - buf.append(Integer.toHexString(id)); - if (flags != 0) - { - buf.append(",flags=0x"); - buf.append(Integer.toHexString(flags)); - if ((flags & DNSConstants.FLAGS_QR_RESPONSE) != 0) - { - buf.append(":r"); - } - if ((flags & DNSConstants.FLAGS_AA) != 0) - { - buf.append(":aa"); - } - if ((flags & DNSConstants.FLAGS_TC) != 0) - { - buf.append(":tc"); - } - } - if (numQuestions > 0) - { - buf.append(",questions="); - buf.append(numQuestions); - } - if (numAnswers > 0) - { - buf.append(",answers="); - buf.append(numAnswers); - } - if (numAuthorities > 0) - { - buf.append(",authorities="); - buf.append(numAuthorities); - } - if (numAdditionals > 0) - { - buf.append(",additionals="); - buf.append(numAdditionals); - } - buf.append("]"); - return buf.toString(); - } - - /** - * Appends answers to this Incoming. - * - * @throws IllegalArgumentException If not a query or if Truncated. - */ - void append(DNSIncoming that) - { - if (this.isQuery() && this.isTruncated() && that.isQuery()) - { - if (that.numQuestions > 0) { - if (Collections.EMPTY_LIST.equals(this.questions)) - this.questions = - Collections.synchronizedList( - new ArrayList<DNSEntry>(that.numQuestions)); - - this.questions.addAll(that.questions); - this.numQuestions += that.numQuestions; - } - - if (Collections.EMPTY_LIST.equals(answers)) - { - answers = Collections.synchronizedList( - new ArrayList<DNSRecord>()); - } - - if (that.numAnswers > 0) - { - this.answers.addAll(this.numAnswers, - that.answers.subList(0, that.numAnswers)); - this.numAnswers += that.numAnswers; - } - if (that.numAuthorities > 0) - { - this.answers.addAll(this.numAnswers + this.numAuthorities, - that.answers.subList( - that.numAnswers, - that.numAnswers + that.numAuthorities)); - this.numAuthorities += that.numAuthorities; - } - if (that.numAdditionals > 0) - { - this.answers.addAll( - that.answers.subList( - that.numAnswers + that.numAuthorities, - that.numAnswers + that.numAuthorities + that.numAdditionals)); - this.numAdditionals += that.numAdditionals; - } - } - else - { - throw new IllegalArgumentException(); - } - } - - int elapseSinceArrival() - { - return (int) (System.currentTimeMillis() - receivedTime); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java deleted file mode 100644 index d317953..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -// REMIND: Listener should follow Java idiom for listener or have a different -// name. - -/** - * DNSListener. - * Listener for record updates. - * - * @author Werner Randelshofer, Rick Blair - * @version 1.0 May 22, 2004 Created. - */ -public interface DNSListener -{ - /** - * Update a DNS record. - * @param jmdns - * @param now - * @param record - */ - public void updateRecord(JmDNS jmdns, long now, DNSRecord record); -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java deleted file mode 100644 index 4d099b8..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.util.*; -import java.util.logging.*; - -/** - * An outgoing DNS message. - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Werner Randelshofer - */ -final class DNSOutgoing -{ - private static Logger logger = - Logger.getLogger(DNSOutgoing.class.toString()); - - int id; - int flags; - private boolean multicast; - private int numQuestions; - private int numAnswers; - private int numAuthorities; - private int numAdditionals; - private Hashtable<String, Integer> names; - - byte data[]; - int off; - int len; - - /** - * Create an outgoing multicast query or response. - */ - DNSOutgoing(int flags) - { - this(flags, true); - - } - - /** - * Create an outgoing query or response. - */ - DNSOutgoing(int flags, boolean multicast) - { - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.flags = flags; - this.multicast = multicast; - names = new Hashtable<String, Integer>(); - data = new byte[DNSConstants.MAX_MSG_TYPICAL]; - off = 12; - } - - /** - * Add a question to the message. - */ - void addQuestion(DNSQuestion rec) throws IOException - { - if (numAnswers > 0 || numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException("Questions must be added before answers"); - } - numQuestions++; - writeQuestion(rec); - } - - /** - * Add an answer if it is not suppressed. - */ - void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException - { - if (numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException( - "Answers must be added before authorities and additionals"); - } - if (!rec.suppressedBy(in)) - { - addAnswer(rec, 0); - } - } - - /** - * Add an additional answer to the record. Omit if there is no room. - */ - void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException - { - if ((off < DNSConstants.MAX_MSG_TYPICAL - 200) && !rec.suppressedBy(in)) - { - writeRecord(rec, 0); - numAdditionals++; - } - } - - /** - * Add an answer to the message. - */ - void addAnswer(DNSRecord rec, long now) throws IOException - { - if (numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException( - "Questions must be added before answers"); - } - if (rec != null) - { - if ((now == 0) || !rec.isExpired(now)) - { - writeRecord(rec, now); - numAnswers++; - } - } - } - - private LinkedList<DNSRecord> authorativeAnswers = new LinkedList<DNSRecord>(); - - /** - * Add an authorative answer to the message. - */ - void addAuthorativeAnswer(DNSRecord rec) throws IOException - { - if (numAdditionals > 0) - { - throw new IllegalStateException( - "Authorative answers must be added before additional answers"); - } - authorativeAnswers.add(rec); - writeRecord(rec, 0); - numAuthorities++; - - // VERIFY: - - } - - void writeByte(int value) throws IOException - { - if (off >= data.length) - { - throw new IOException("buffer full"); - } - data[off++] = (byte) value; - } - - void writeBytes(String str, int off, int len) throws IOException - { - for (int i = 0; i < len; i++) - { - writeByte(str.charAt(off + i)); - } - } - - void writeBytes(byte data[]) throws IOException - { - if (data != null) - { - writeBytes(data, 0, data.length); - } - } - - void writeBytes(byte data[], int off, int len) throws IOException - { - for (int i = 0; i < len; i++) - { - writeByte(data[off + i]); - } - } - - void writeShort(int value) throws IOException - { - writeByte(value >> 8); - writeByte(value); - } - - void writeInt(int value) throws IOException - { - writeShort(value >> 16); - writeShort(value); - } - - void writeUTF(String str, int off, int len) throws IOException - { - // compute utf length - int utflen = 0; - for (int i = 0; i < len; i++) - { - int ch = str.charAt(off + i); - if ((ch >= 0x0001) && (ch <= 0x007F)) - { - utflen += 1; - } - else - { - if (ch > 0x07FF) - { - utflen += 3; - } - else - { - utflen += 2; - } - } - } - // write utf length - writeByte(utflen); - // write utf data - for (int i = 0; i < len; i++) - { - int ch = str.charAt(off + i); - if ((ch >= 0x0001) && (ch <= 0x007F)) - { - writeByte(ch); - } - else - { - if (ch > 0x07FF) - { - writeByte(0xE0 | ((ch >> 12) & 0x0F)); - writeByte(0x80 | ((ch >> 6) & 0x3F)); - writeByte(0x80 | ((ch >> 0) & 0x3F)); - } - else - { - writeByte(0xC0 | ((ch >> 6) & 0x1F)); - writeByte(0x80 | ((ch >> 0) & 0x3F)); - } - } - } - } - - void writeName(String name) throws IOException - { - while (true) - { - int n = name.indexOf('.'); - if (n < 0) - { - n = name.length(); - } - if (n <= 0) - { - writeByte(0); - return; - } - Integer offset = names.get(name); - if (offset != null) - { - int val = offset.intValue(); - - if (val > off) - { - logger.log(Level.WARNING, - "DNSOutgoing writeName failed val=" + val + " name=" + name); - } - - writeByte((val >> 8) | 0xC0); - writeByte(val); - return; - } - names.put(name, off); - writeUTF(name, 0, n); - name = name.substring(n); - if (name.startsWith(".")) - { - name = name.substring(1); - } - } - } - - void writeQuestion(DNSQuestion question) throws IOException - { - writeName(question.name); - writeShort(question.type); - writeShort(question.clazz); - } - - void writeRecord(DNSRecord rec, long now) throws IOException - { - int save = off; - try - { - writeName(rec.name); - writeShort(rec.type); - writeShort(rec.clazz | - ((rec.unique && multicast) ? DNSConstants.CLASS_UNIQUE : 0)); - writeInt((now == 0) ? rec.ttl : rec.getRemainingTTL(now)); - writeShort(0); - int start = off; - rec.write(this); - int len = off - start; - data[start - 2] = (byte) (len >> 8); - data[start - 1] = (byte) (len & 0xFF); - } - catch (IOException e) - { - off = save; - throw e; - } - } - - /** - * Finish the message before sending it off. - */ - void finish() throws IOException - { - int save = off; - off = 0; - - writeShort(multicast ? 0 : id); - writeShort(flags); - writeShort(numQuestions); - writeShort(numAnswers); - writeShort(numAuthorities); - writeShort(numAdditionals); - off = save; - } - - boolean isQuery() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_QUERY; - } - - public boolean isEmpty() - { - return numQuestions == 0 && numAuthorities == 0 - && numAdditionals == 0 && numAnswers == 0; - } - - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append(isQuery() ? "dns[query," : "dns[response,"); - //buf.append(packet.getAddress().getHostAddress()); - buf.append(':'); - //buf.append(packet.getPort()); - //buf.append(",len="); - //buf.append(packet.getLength()); - buf.append(",id=0x"); - buf.append(Integer.toHexString(id)); - if (flags != 0) - { - buf.append(",flags=0x"); - buf.append(Integer.toHexString(flags)); - if ((flags & DNSConstants.FLAGS_QR_RESPONSE) != 0) - { - buf.append(":r"); - } - if ((flags & DNSConstants.FLAGS_AA) != 0) - { - buf.append(":aa"); - } - if ((flags & DNSConstants.FLAGS_TC) != 0) - { - buf.append(":tc"); - } - } - if (numQuestions > 0) - { - buf.append(",questions="); - buf.append(numQuestions); - } - if (numAnswers > 0) - { - buf.append(",answers="); - buf.append(numAnswers); - } - if (numAuthorities > 0) - { - buf.append(",authorities="); - buf.append(numAuthorities); - } - if (numAdditionals > 0) - { - buf.append(",additionals="); - buf.append(numAdditionals); - } - buf.append(",\nnames=" + names); - buf.append(",\nauthorativeAnswers=" + authorativeAnswers); - - buf.append("]"); - return buf.toString(); - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java deleted file mode 100644 index f6abaa7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.logging.*; - -/** - * A DNS question. - * - * @version %I%, %G% - * @author Arthur van Hoff - */ -public final class DNSQuestion - extends DNSEntry -{ - private static Logger logger = - Logger.getLogger(DNSQuestion.class.toString()); - - /** - * Create a question. - * @param name - * @param type - * @param clazz - */ - public DNSQuestion(String name, int type, int clazz) - { - super(name, type, clazz); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Check if this question is answered by a given DNS record. - */ - boolean answeredBy(DNSRecord rec) - { - return (clazz == rec.clazz) && - ((type == rec.type) || - (type == DNSConstants.TYPE_ANY)) && - name.equals(rec.name); - } - - /** - * For debugging only. - */ - @Override - public String toString() - { - return toString("question", null); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java deleted file mode 100644 index 673bbc4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java +++ /dev/null @@ -1,796 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.logging.*; - -/** - * DNS record - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Werner Randelshofer, Pierre Frisch - */ -public abstract class DNSRecord extends DNSEntry -{ - private static Logger logger = - Logger.getLogger(DNSRecord.class.toString()); - int ttl; - private long created; - - /** - * Create a DNSRecord with a name, type, clazz, and ttl. - */ - DNSRecord(String name, int type, int clazz, int ttl) - { - super(name, type, clazz); - this.ttl = ttl; - this.created = System.currentTimeMillis(); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * True if this record is the same as some other record. - * @param other obj to be compared to. - */ - @Override - public boolean equals(Object other) - { - return (other instanceof DNSRecord) && sameAs((DNSRecord) other); - } - - /** - * True if this record is the same as some other record. - */ - boolean sameAs(DNSRecord other) - { - return super.equals(other) && sameValue(other); - } - - /** - * True if this record has the same value as some other record. - */ - abstract boolean sameValue(DNSRecord other); - - /** - * True if this record has the same type as some other record. - */ - boolean sameType(DNSRecord other) - { - return type == other.type; - } - - /** - * Handles a query represented by this record. - * - * @return Returns true if a conflict with one of the services registered - * with JmDNS or with the hostname occured. - */ - abstract boolean handleQuery(JmDNS dns, long expirationTime); - - /** - * Handles a responserepresented by this record. - * - * @return Returns true if a conflict with one of the services registered - * with JmDNS or with the hostname occured. - */ - abstract boolean handleResponse(JmDNS dns); - - /** - * Adds this as an answer to the provided outgoing datagram. - */ - abstract DNSOutgoing addAnswer(JmDNS dns, DNSIncoming in, InetAddress addr, - int port, DNSOutgoing out) - throws IOException; - - /** - * True if this record is suppressed by the answers in a message. - */ - boolean suppressedBy(DNSIncoming msg) - { - try - { - for (int i = msg.numAnswers; i-- > 0;) - { - if (suppressedBy(msg.answers.get(i))) - { - return true; - } - } - return false; - } - catch (ArrayIndexOutOfBoundsException e) - { - logger.log(Level.WARNING, - "suppressedBy() message " + msg + " exception ", e); - // msg.print(true); - return false; - } - } - - /** - * True if this record would be supressed by an answer. - * This is the case if this record would not have a - * significantly longer TTL. - */ - boolean suppressedBy(DNSRecord other) - { - if (sameAs(other) && (other.ttl > ttl / 2)) - { - return true; - } - return false; - } - - /** - * Get the expiration time of this record. - */ - long getExpirationTime(int percent) - { - return created + (percent * ttl * 10L); - } - - /** - * Get the remaining TTL for this record. - */ - int getRemainingTTL(long now) - { - return (int) Math.max(0, (getExpirationTime(100) - now) / 1000); - } - - /** - * Check if the record is expired. - */ - boolean isExpired(long now) - { - return getExpirationTime(100) <= now; - } - - /** - * Check if the record is stale, ie it has outlived - * more than half of its TTL. - */ - boolean isStale(long now) - { - return getExpirationTime(50) <= now; - } - - /** - * Reset the TTL of a record. This avoids having to - * update the entire record in the cache. - */ - void resetTTL(DNSRecord other) - { - created = other.created; - ttl = other.ttl; - } - - /** - * Write this record into an outgoing message. - */ - abstract void write(DNSOutgoing out) throws IOException; - - /** - * Address record. - */ - static class Address extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Address.class.toString()); - InetAddress addr; - - Address(String name, int type, int clazz, int ttl, InetAddress addr) - { - super(name, type, clazz, ttl); - this.addr = addr; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - Address(String name, int type, int clazz, int ttl, byte[] rawAddress) - { - super(name, type, clazz, ttl); - try - { - this.addr = InetAddress.getByAddress(rawAddress); - } - catch (UnknownHostException exception) - { - logger.log(Level.WARNING, "Address() exception ", exception); - } - } - - @Override - void write(DNSOutgoing out) throws IOException - { - if (addr != null) - { - byte[] buffer = addr.getAddress(); - if (DNSConstants.TYPE_A == type) - { - // If we have a type A records we should - // answer with a IPv4 address - if (addr instanceof Inet4Address) - { - // All is good - } - else - { - // Get the last four bytes - byte[] tempbuffer = buffer; - buffer = new byte[4]; - System.arraycopy(tempbuffer, 12, buffer, 0, 4); - } - } - else - { - // If we have a type AAAA records we should - // answer with a IPv6 address - if (addr instanceof Inet4Address) - { - byte[] tempbuffer = buffer; - buffer = new byte[16]; - for (int i = 0; i < 16; i++) - { - if (i < 11) - { - buffer[i] = tempbuffer[i - 12]; - } - else - { - buffer[i] = 0; - } - } - } - } - int length = buffer.length; - out.writeBytes(buffer, 0, length); - } - } - - boolean same(DNSRecord other) - { - return ((sameName(other)) && ((sameValue(other)))); - } - - boolean sameName(DNSRecord other) - { - return name.equalsIgnoreCase(((Address) other).name); - } - - @Override - boolean sameValue(DNSRecord other) - { - return addr.equals(((Address) other).getAddress()); - } - - InetAddress getAddress() - { - return addr; - } - - /** - * Creates a byte array representation of this record. - * This is needed for tie-break tests according to - * draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2. - */ - private byte[] toByteArray() - { - try - { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(bout); - dout.write(name.getBytes("UTF8")); - dout.writeShort(type); - dout.writeShort(clazz); - //dout.writeInt(len); - byte[] buffer = addr.getAddress(); - for (int i = 0; i < buffer.length; i++) - { - dout.writeByte(buffer[i]); - } - dout.close(); - return bout.toByteArray(); - } - catch (IOException e) - { - throw new InternalError(); - } - } - - /** - * Does a lexicographic comparison of the byte array representation - * of this record and that record. - * This is needed for tie-break tests according to - * draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2. - */ - private int lexCompare(DNSRecord.Address that) - { - byte[] thisBytes = this.toByteArray(); - byte[] thatBytes = that.toByteArray(); - for ( int i = 0, n = Math.min(thisBytes.length, thatBytes.length); - i < n; - i++) - { - if (thisBytes[i] > thatBytes[i]) - { - return 1; - } - else - { - if (thisBytes[i] < thatBytes[i]) - { - return -1; - } - } - } - return thisBytes.length - thatBytes.length; - } - - /** - * Does the necessary actions, when this as a query. - */ - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - DNSRecord.Address dnsAddress = - dns.getLocalHost().getDNSAddressRecord(this); - if (dnsAddress != null) - { - if (dnsAddress.sameType(this) && - dnsAddress.sameName(this) && - (!dnsAddress.sameValue(this))) - { - logger.finer( - "handleQuery() Conflicting probe detected. dns state " + - dns.getState() + - " lex compare " + lexCompare(dnsAddress)); - // Tie-breaker test - if (dns.getState().isProbing() && lexCompare(dnsAddress) >= 0) - { - // We lost the tie-break. We have to choose a different name. - dns.getLocalHost().incrementHostName(); - dns.getCache().clear(); - for (ServiceInfo info : dns.services.values()) - info.revertState(); - } - dns.revertState(); - return true; - } - } - return false; - } - - /** - * Does the necessary actions, when this as a response. - */ - @Override - boolean handleResponse(JmDNS dns) - { - DNSRecord.Address dnsAddress = - dns.getLocalHost().getDNSAddressRecord(this); - if (dnsAddress != null) - { - if (dnsAddress.sameType(this) && - dnsAddress.sameName(this) && - (!dnsAddress.sameValue(this))) - { - logger.finer("handleResponse() Denial detected"); - - if (dns.getState().isProbing()) - { - dns.getLocalHost().incrementHostName(); - dns.getCache().clear(); - for (ServiceInfo info : dns.services.values()) - info.revertState(); - } - dns.revertState(); - return true; - } - } - return false; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString(" address '" + - (addr != null ? addr.getHostAddress() : "null") + "'"); - } - - } - - /** - * Pointer record. - */ - static class Pointer extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Pointer.class.toString()); - String alias; - - Pointer(String name, int type, int clazz, int ttl, String alias) - { - super(name, type, clazz, ttl); - this.alias = alias; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeName(alias); - } - - @Override - boolean sameValue(DNSRecord other) - { - return alias.equals(((Pointer) other).alias); - } - - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - // Nothing to do (?) - // I think there is no possibility - // for conflicts for this record type? - return false; - } - - @Override - boolean handleResponse(JmDNS dns) - { - // Nothing to do (?) - // I think there is no possibility for conflicts for this record type? - return false; - } - - String getAlias() - { - return alias; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString(alias); - } - } - - static class Text extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Text.class.toString()); - byte text[]; - - Text(String name, int type, int clazz, int ttl, byte text[]) - { - super(name, type, clazz, ttl); - this.text = text; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeBytes(text, 0, text.length); - } - - @Override - boolean sameValue(DNSRecord other) - { - Text txt = (Text) other; - if (txt.text.length != text.length) - { - return false; - } - for (int i = text.length; i-- > 0;) - { - if (txt.text[i] != text[i]) - { - return false; - } - } - return true; - } - - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - // Nothing to do (?) - // I think there is no possibility for conflicts for this record type? - return false; - } - - @Override - boolean handleResponse(JmDNS dns) - { - // Nothing to do (?) - // Shouldn't we care if we get a conflict at this level? - /* - ServiceInfo info = (ServiceInfo) dns.services.get(name.toLowerCase()); - if (info != null) - { - if (! Arrays.equals(text,info.text)) - { - info.revertState(); - return true; - } - } - */ - return false; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString((text.length > 10) ? - new String(text, 0, 7) + "..." : - new String(text)); - } - } - - /** - * Service record. - */ - static class Service extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Service.class.toString()); - int priority; - int weight; - int port; - String server; - - Service(String name, - int type, - int clazz, - int ttl, - int priority, - int weight, - int port, - String server) - { - super(name, type, clazz, ttl); - this.priority = priority; - this.weight = weight; - this.port = port; - this.server = server; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeShort(priority); - out.writeShort(weight); - out.writeShort(port); - out.writeName(server); - } - - private byte[] toByteArray() - { - try - { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(bout); - dout.write(name.getBytes("UTF8")); - dout.writeShort(type); - dout.writeShort(clazz); - //dout.writeInt(len); - dout.writeShort(priority); - dout.writeShort(weight); - dout.writeShort(port); - dout.write(server.getBytes("UTF8")); - dout.close(); - return bout.toByteArray(); - } - catch (IOException e) - { - throw new InternalError(); - } - } - - private int lexCompare(DNSRecord.Service that) - { - byte[] thisBytes = this.toByteArray(); - byte[] thatBytes = that.toByteArray(); - for (int i = 0, n = Math.min(thisBytes.length, thatBytes.length); - i < n; - i++) - { - if (thisBytes[i] > thatBytes[i]) - { - return 1; - } - else - { - if (thisBytes[i] < thatBytes[i]) - { - return -1; - } - } - } - return thisBytes.length - thatBytes.length; - } - - @Override - boolean sameValue(DNSRecord other) - { - Service s = (Service) other; - return (priority == s.priority) && - (weight == s.weight) && - (port == s.port) && - server.equals(s.server); - } - - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null && - (port != info.port || - !server.equalsIgnoreCase(dns.getLocalHost().getName()))) - { - logger.finer("handleQuery() Conflicting probe detected"); - - // Tie breaker test - if (info.getState().isProbing() && - lexCompare(new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - dns.getLocalHost().getName())) >= 0) - { - // We lost the tie break - String oldName = info.getQualifiedName().toLowerCase(); - info.setName(dns.incrementName(info.getName())); - dns.services.remove(oldName); - dns.services.put(info.getQualifiedName().toLowerCase(), info); - logger.finer( - "handleQuery() Lost tie break: new unique name chosen:" + info.getName()); - - } - info.revertState(); - return true; - - } - return false; - } - - @Override - boolean handleResponse(JmDNS dns) - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null && - (port != info.port || !server.equalsIgnoreCase(dns.getLocalHost().getName()))) - { - logger.finer("handleResponse() Denial detected"); - - if (info.getState().isProbing()) - { - String oldName = info.getQualifiedName().toLowerCase(); - info.setName(dns.incrementName(info.getName())); - dns.services.remove(oldName); - dns.services.put(info.getQualifiedName().toLowerCase(), info); - logger.finer( - "handleResponse() New unique name chose:" + info.getName()); - - } - info.revertState(); - return true; - } - return false; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null) - { - if (this.port == info.port != server.equals(dns.getLocalHost().getName())) - { - return dns.addAnswer(in, addr, port, out, - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - dns.getLocalHost().getName())); - } - } - return out; - } - - @Override - public String toString() - { - return toString(server + ":" + port); - } - } - - public String toString(String other) - { - return toString("record", ttl + "/" + - getRemainingTTL(System.currentTimeMillis()) -// + "," + other - ); - } -} - diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java deleted file mode 100644 index 5016050..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; -import java.util.logging.*; - -/** - * DNSState defines the possible states for services registered with JmDNS. - * - * @author Werner Randelshofer, Rick Blair - * @version 1.0 May 23, 2004 Created. - */ -public class DNSState - implements Comparable<DNSState> -{ - private static Logger logger = - Logger.getLogger(DNSState.class.toString()); - - private final String name; - - /** - * Ordinal of next state to be created. - */ - private static int nextOrdinal = 0; - /** - * Assign an ordinal to this state. - */ - private final int ordinal = nextOrdinal++; - /** - * Logical sequence of states. - * The sequence is consistent with the ordinal of a state. - * This is used for advancing through states. - */ - private final static ArrayList<DNSState> sequence - = new ArrayList<DNSState>(); - - private DNSState(String name) - { - this.name = name; - sequence.add(this); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - public final String toString() - { - return name; - } - - public static final DNSState PROBING_1 = new DNSState("probing 1"); - public static final DNSState PROBING_2 = new DNSState("probing 2"); - public static final DNSState PROBING_3 = new DNSState("probing 3"); - public static final DNSState ANNOUNCING_1 = new DNSState("announcing 1"); - public static final DNSState ANNOUNCING_2 = new DNSState("announcing 2"); - public static final DNSState ANNOUNCED = new DNSState("announced"); - public static final DNSState CANCELED = new DNSState("canceled"); - - /** - * Returns the next advanced state. - * In general, this advances one step in the following sequence: PROBING_1, - * PROBING_2, PROBING_3, ANNOUNCING_1, ANNOUNCING_2, ANNOUNCED. - * Does not advance for ANNOUNCED and CANCELED state. - * @return Returns the next advanced state. - */ - public final DNSState advance() - { - return (isProbing() || isAnnouncing()) ? - sequence.get(ordinal + 1) : - this; - } - - /** - * Returns to the next reverted state. - * All states except CANCELED revert to PROBING_1. - * Status CANCELED does not revert. - * @return Returns to the next reverted state. - */ - public final DNSState revert() - { - return (this == CANCELED) ? this : PROBING_1; - } - - /** - * Returns true, if this is a probing state. - * @return Returns true, if this is a probing state. - */ - public boolean isProbing() - { - return compareTo(PROBING_1) >= 0 && compareTo(PROBING_3) <= 0; - } - - /** - * Returns true, if this is an announcing state. - * @return Returns true, if this is an announcing state. - */ - public boolean isAnnouncing() - { - return compareTo(ANNOUNCING_1) >= 0 && compareTo(ANNOUNCING_2) <= 0; - } - - /** - * Returns true, if this is an announced state. - * @return Returns true, if this is an announced state. - */ - public boolean isAnnounced() - { - return compareTo(ANNOUNCED) == 0; - } - - /** - * Compares two states. - * The states compare as follows: - * PROBING_1 < PROBING_2 < PROBING_3 < ANNOUNCING_1 < - * ANNOUNCING_2 < RESPONDING < ANNOUNCED < CANCELED. - */ - public int compareTo(DNSState state) - { - return ordinal - state.ordinal; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java deleted file mode 100644 index 644afc9..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.net.*; -import java.util.logging.*; - - -/** - * HostInfo information on the local host to be able to cope with change of addresses. - * - * @version %I%, %G% - * @author Pierre Frisch, Werner Randelshofer - */ -class HostInfo -{ - private static Logger logger = Logger.getLogger(HostInfo.class.toString()); - protected String name; - protected InetAddress address; - protected NetworkInterface interfaze; - /** - * This is used to create a unique name for the host name. - */ - private int hostNameCount; - - public HostInfo(InetAddress address, String name) - { - super(); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.address = address; - this.name = name; - if (address != null) - { - try - { - interfaze = NetworkInterface.getByInetAddress(address); - } - catch (Exception exception) - { - // FIXME Shouldn't we take an action here? - logger.log(Level.WARNING, - "LocalHostInfo() exception ", exception); - } - } - } - - public String getName() - { - return name; - } - - public InetAddress getAddress() - { - return address; - } - - public NetworkInterface getInterface() - { - return interfaze; - } - - synchronized String incrementHostName() - { - hostNameCount++; - int plocal = name.indexOf(".local."); - int punder = name.lastIndexOf("-"); - name = name.substring(0, (punder == -1 ? plocal : punder)) + "-" + - hostNameCount + ".local."; - return name; - } - - boolean shouldIgnorePacket(DatagramPacket packet) - { - boolean result = false; - if (getAddress() != null) - { - InetAddress from = packet.getAddress(); - if (from != null) - { - if (from.isLinkLocalAddress() && - (!getAddress().isLinkLocalAddress())) - { - // Ignore linklocal packets on regular interfaces, unless this is - // also a linklocal interface. This is to avoid duplicates. This is - // a terrible hack caused by the lack of an API to get the address - // of the interface on which the packet was received. - result = true; - } - if (from.isLoopbackAddress() && - (!getAddress().isLoopbackAddress())) - { - // Ignore loopback packets on a regular interface unless this is - // also a loopback interface. - result = true; - } - } - } - return result; - } - - DNSRecord.Address getDNSAddressRecord(DNSRecord.Address address) - { - return (DNSConstants.TYPE_AAAA == address.type ? - getDNS6AddressRecord() : - getDNS4AddressRecord()); - } - - DNSRecord.Address getDNS4AddressRecord() - { - if ((getAddress() != null) && - ((getAddress() instanceof Inet4Address) || - ((getAddress() instanceof Inet6Address) && - (((Inet6Address) getAddress()).isIPv4CompatibleAddress())))) - { - return new DNSRecord.Address(getName(), - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, getAddress()); - } - return null; - } - - DNSRecord.Address getDNS6AddressRecord() - { - if ((getAddress() != null) && (getAddress() instanceof Inet6Address)) - { - return new DNSRecord.Address( - getName(), - DNSConstants.TYPE_AAAA, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - getAddress()); - } - return null; - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("local host info["); - buf.append(getName() != null ? getName() : "no name"); - buf.append(", "); - buf.append(getInterface() != null ? - getInterface().getDisplayName() : - "???"); - buf.append(":"); - buf.append(getAddress() != null ? - getAddress().getHostAddress() : - "no address"); - buf.append("]"); - return buf.toString(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java deleted file mode 100644 index 96420ba..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java +++ /dev/null @@ -1,3048 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; - -import net.java.sip.communicator.util.*; - -// REMIND: multiple IP addresses - -/** - * mDNS implementation in Java. - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, - * Werner Randelshofer, Pierre Frisch, Scott Lewis - * @author Christian Vincenot - */ -public class JmDNS -{ - private static final Logger logger - = Logger.getLogger(JmDNS.class); - - /** - * The version of JmDNS. - */ - public static String VERSION = "2.0"; - - /** - * This is the multicast group, we are listening to for - * multicast DNS messages. - */ - private InetAddress group; - /** - * This is our multicast socket. - */ - private MulticastSocket socket; - - /** - * Used to fix live lock problem on unregester. - */ - - protected boolean closed = false; - - /** - * Holds instances of JmDNS.DNSListener. - * Must by a synchronized collection, because it is updated from - * concurrent threads. - */ - private List<DNSListener> listeners; - /** - * Holds instances of ServiceListener's. - * Keys are Strings holding a fully qualified service type. - * Values are LinkedList's of ServiceListener's. - */ - private Map<String, List<ServiceListener>> serviceListeners; - /** - * Holds instances of ServiceTypeListener's. - */ - private List<ServiceTypeListener> typeListeners; - - - /** - * Cache for DNSEntry's. - */ - private DNSCache cache; - - /** - * This hashtable holds the services that have been registered. - * Keys are instances of String which hold an all lower-case version of the - * fully qualified service name. - * Values are instances of ServiceInfo. - */ - Map<String, ServiceInfo> services; - - /** - * This hashtable holds the service types that have been registered or - * that have been received in an incoming datagram. - * Keys are instances of String which hold an all lower-case version of the - * fully qualified service type. - * Values hold the fully qualified service type. - */ - Map<String, String> serviceTypes; - - /** - * Handle on the local host - */ - HostInfo localHost; - - private Thread incomingListener = null; - - /** - * Throttle count. - * This is used to count the overall number of probes sent by JmDNS. - * When the last throttle increment happened . - */ - private int throttle; - /** - * Last throttle increment. - */ - private long lastThrottleIncrement; - - /** - * The timer is used to dispatch all outgoing messages of JmDNS. - * It is also used to dispatch maintenance tasks for the DNS cache. - */ - private Timer timer; - - /** - * The source for random values. - * This is used to introduce random delays in responses. This reduces the - * potential for collisions on the network. - */ - private final static Random random = new Random(); - - /** - * This lock is used to coordinate processing of incoming and outgoing - * messages. This is needed, because the Rendezvous Conformance Test - * does not forgive race conditions. - */ - private Object ioLock = new Object(); - - /** - * If an incoming package which needs an answer is truncated, we store it - * here. We add more incoming DNSRecords to it, until the JmDNS.Responder - * timer picks it up. - * Remind: This does not work well with multiple planned answers for packages - * that came in from different clients. - */ - private DNSIncoming plannedAnswer; - - // State machine - /** - * The state of JmDNS. - * <p/> - * For proper handling of concurrency, this variable must be - * changed only using methods advanceState(), revertState() and cancel(). - */ - private DNSState state = DNSState.PROBING_1; - - /** - * Timer task associated to the host name. - * This is used to prevent from having multiple tasks associated to the host - * name at the same time. - */ - TimerTask task; - - /** - * This hashtable is used to maintain a list of service types being collected - * by this JmDNS instance. - * The key of the hashtable is a service type name, the value is an instance - * of JmDNS.ServiceCollector. - * - * @see #list - */ - private HashMap<String, ServiceCollector> serviceCollectors = new HashMap<String, ServiceCollector>(); - - /** - * Create an instance of JmDNS. - * @throws java.io.IOException - */ - public JmDNS() - throws IOException - { - //String SLevel = System.getProperty("jmdns.debug"); - - if (logger.isDebugEnabled()) - logger.debug("JmDNS instance created"); - try - { - InetAddress addr = InetAddress.getLocalHost(); - // [PJYF Oct 14 2004] Why do we disallow the loopback address? - init(addr.isLoopbackAddress() ? null : addr, addr.getHostName()); - } - catch (IOException exc) - { - logger.error("Failed to get a reference to localhost", exc); - init(null, "computer"); - } - } - - /** - * Create an instance of JmDNS and bind it to a - * specific network interface given its IP-address. - * @param addr - * @throws java.io.IOException - */ - public JmDNS(InetAddress addr) - throws IOException - { - try - { - init(addr, addr.getHostName()); - } - catch (IOException e) - { - init(null, "computer"); - } - } - - /** - * Initialize everything. - * - * @param address The interface to which JmDNS binds to. - * @param name The host name of the interface. - */ - private void init(InetAddress address, String name) throws IOException - { - // A host name with "." is illegal. - // so strip off everything and append .local. - int idx = name.indexOf("."); - if (idx > 0) - { - name = name.substring(0, idx); - } - name += ".local."; - // localHost to IP address binding - localHost = new HostInfo(address, name); - - cache = new DNSCache(100); - - listeners = Collections.synchronizedList(new ArrayList<DNSListener>()); - serviceListeners = new HashMap<String, List<ServiceListener>>(); - typeListeners = new ArrayList<ServiceTypeListener>(); - - services = new Hashtable<String, ServiceInfo>(20); - serviceTypes = new Hashtable<String, String>(20); - - // REMIND: If I could pass in a name for the Timer thread, - // I would pass 'JmDNS.Timer'. - timer = new Timer(); - new RecordReaper().start(); - - incomingListener = new Thread( - new SocketListener(), "JmDNS.SocketListener"); - incomingListener.setDaemon(true); - // Bind to multicast socket - openMulticastSocket(localHost); - start(services.values()); - } - - private void start(Collection<ServiceInfo> serviceInfos) - { - state = DNSState.PROBING_1; - incomingListener.start(); - new Prober().start(); - for (ServiceInfo serviceInfo : serviceInfos) - { - try - { - registerService(new ServiceInfo(serviceInfo)); - } - catch (Exception exception) - { - logger.warn("start() Registration exception ", exception); - } - } - } - - private void openMulticastSocket(HostInfo hostInfo) throws IOException - { - if (group == null) - { - group = InetAddress.getByName(DNSConstants.MDNS_GROUP); - } - if (socket != null) - { - this.closeMulticastSocket(); - } - socket = new MulticastSocket(DNSConstants.MDNS_PORT); - if ((hostInfo != null) && (localHost.getInterface() != null)) - { - socket.setNetworkInterface(hostInfo.getInterface()); - } - socket.setTimeToLive(255); - socket.joinGroup(group); - } - - private void closeMulticastSocket() - { - if (logger.isDebugEnabled()) - logger.debug("closeMulticastSocket()"); - if (socket != null) - { - // close socket - try - { - socket.leaveGroup(group); - socket.close(); - if (incomingListener != null) - { - incomingListener.join(); - } - } - catch (Exception exception) - { - logger.warn("closeMulticastSocket() Close socket exception ", - exception); - } - socket = null; - } - } - - // State machine - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void advanceState() - { - state = state.advance(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void revertState() - { - state = state.revert(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void cancel() - { - state = DNSState.CANCELED; - notifyAll(); - } - - /** - * Returns the current state of this info. - */ - DNSState getState() - { - return state; - } - - - /** - * Return the DNSCache associated with the cache variable - */ - DNSCache getCache() - { - return cache; - } - - /** - * Return the HostName associated with this JmDNS instance. - * Note: May not be the same as what started. The host name is subject to - * negotiation. - * @return Return the HostName associated with this JmDNS instance. - */ - public String getHostName() - { - return localHost.getName(); - } - - public HostInfo getLocalHost() - { - return localHost; - } - - /** - * Return the address of the interface to which this instance of JmDNS is - * bound. - * @return Return the address of the interface to which this instance - * of JmDNS is bound. - * @throws java.io.IOException - */ - public InetAddress getInterface() - throws IOException - { - return socket.getInterface(); - } - - /** - * Get service information. If the information is not cached, the method - * will block until updated information is received. - * <p/> - * Usage note: Do not call this method from the AWT event dispatcher thread. - * You will make the user interface unresponsive. - * - * @param type fully qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @return null if the service information cannot be obtained - */ - public ServiceInfo getServiceInfo(String type, String name) - { - return getServiceInfo(type, name, 3 * 1000); - } - - /** - * Get service information. If the information is not cached, the method - * will block for the given timeout until updated information is received. - * <p/> - * Usage note: If you call this method from the AWT event dispatcher thread, - * use a small timeout, or you will make the user interface unresponsive. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @param timeout timeout in milliseconds - * @return null if the service information cannot be obtained - */ - public ServiceInfo getServiceInfo(String type, String name, int timeout) - { - ServiceInfo info = new ServiceInfo(type, name); - new ServiceInfoResolver(info).start(); - - try - { - long end = System.currentTimeMillis() + timeout; - long delay; - synchronized (info) - { - while (!info.hasData() && - (delay = end - System.currentTimeMillis()) > 0) - { - info.wait(delay); - } - } - } - catch (InterruptedException e) - { - // empty - } - - return (info.hasData()) ? info : null; - } - - /** - * Request service information. The information about the service is - * requested and the ServiceListener.resolveService method is called as soon - * as it is available. - * <p/> - * Usage note: Do not call this method from the AWT event dispatcher thread. - * You will make the user interface unresponsive. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - */ - public void requestServiceInfo(String type, String name) - { - requestServiceInfo(type, name, 3 * 1000); - } - - /** - * Request service information. The information about the service - * is requested and the ServiceListener.resolveService method is - * called as soon as it is available. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @param timeout timeout in milliseconds - */ - public void requestServiceInfo(String type, String name, int timeout) - { - registerServiceType(type); - ServiceInfo info = new ServiceInfo(type, name); - new ServiceInfoResolver(info).start(); - - try - { - long end = System.currentTimeMillis() + timeout; - long delay; - synchronized (info) - { - while (!info.hasData() && - (delay = end - System.currentTimeMillis()) > 0) - { - info.wait(delay); - } - } - } - catch (InterruptedException e) - { - // empty - } - } - - void handleServiceResolved(ServiceInfo info) - { - List<ServiceListener> list = serviceListeners.get(info.type.toLowerCase()); - if (list != null) - { - ServiceEvent event = - new ServiceEvent(this, info.type, info.getName(), info); - // Iterate on a copy in case listeners will modify it - List<ServiceListener> listCopy - = new ArrayList<ServiceListener> (list); - for (ServiceListener serviceListener : listCopy) - serviceListener.serviceResolved(event); - } - } - - /** - * Listen for service types. - * - * @param listener listener for service types - * @throws java.io.IOException - */ - public void addServiceTypeListener(ServiceTypeListener listener) - throws IOException - { - synchronized (this) - { - typeListeners.remove(listener); - typeListeners.add(listener); - } - - // report cached service types - for (String serviceType : serviceTypes.values()) - { - listener.serviceTypeAdded( - new ServiceEvent(this, serviceType, null, null)); - } - - new TypeResolver().start(); - } - - /** - * Remove listener for service types. - * - * @param listener listener for service types - */ - public void removeServiceTypeListener(ServiceTypeListener listener) - { - synchronized (this) - { - typeListeners.remove(listener); - } - } - - /** - * Listen for services of a given type. The type has to be a fully - * qualified type name such as <code>_http._tcp.local.</code>. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code>. - * @param listener listener for service updates - */ - public void addServiceListener(String type, ServiceListener listener) - { - String lotype = type.toLowerCase(); - removeServiceListener(lotype, listener); - List<ServiceListener> list = null; - synchronized (this) - { - list = serviceListeners.get(lotype); - if (list == null) - { - list = Collections.synchronizedList(new LinkedList<ServiceListener>()); - serviceListeners.put(lotype, list); - } - list.add(listener); - } - - // report cached service types - for (Iterator<DNSCache.CacheNode> i = cache.iterator(); i.hasNext();) - { - for (DNSCache.CacheNode n = i.next(); n != null; n = n.next()) - { - DNSRecord rec = (DNSRecord) n.getValue(); - if (rec.type == DNSConstants.TYPE_SRV) - { - if (rec.name.endsWith(type)) - { - listener.serviceAdded( - new ServiceEvent( - this, - type, - toUnqualifiedName(type, rec.name), - null)); - } - } - } - } - new ServiceResolver(type).start(); - } - - /** - * Remove listener for services of a given type. - * - * @param type of listener to be removed - * @param listener listener for service updates - */ - public void removeServiceListener(String type, ServiceListener listener) - { - type = type.toLowerCase(); - List<ServiceListener> list = serviceListeners.get(type); - if (list != null) - { - synchronized (this) - { - list.remove(listener); - if (list.size() == 0) - { - serviceListeners.remove(type); - } - } - } - } - - /** - * Register a service. The service is registered - * for access by other jmdns clients. - * The name of the service may be changed to make it unique. - * @param info of service - * @throws java.io.IOException - */ - public void registerService(ServiceInfo info) throws IOException - { - registerServiceType(info.type); - - // bind the service to this address - info.server = localHost.getName(); - info.addr = localHost.getAddress(); - - synchronized (this) - { - makeServiceNameUnique(info); - services.put(info.getQualifiedName().toLowerCase(), info); - } - - new /*Service*/Prober().start(); - try - { - synchronized (info) - { - while (info.getState().compareTo(DNSState.ANNOUNCED) < 0) - { - info.wait(); - } - } - } - catch (InterruptedException e) - { - logger.error(e.getMessage(), e); - } - if (logger.isDebugEnabled()) - logger.debug("registerService() JmDNS registered service as " + info); - } - - /** - * Unregister a service. The service should have been registered. - * @param info of service - */ - public void unregisterService(ServiceInfo info) - { - synchronized (this) - { - services.remove(info.getQualifiedName().toLowerCase()); - } - info.cancel(); - - // Note: We use this lock object to synchronize on it. - // Synchronizing on another object (e.g. the ServiceInfo) does - // not make sense, because the sole purpose of the lock is to - // wait until the canceler has finished. If we synchronized on - // the ServiceInfo or on the Canceler, we would block all - // accesses to synchronized methods on that object. This is not - // what we want! - Object lock = new Object(); - new Canceler(info, lock).start(); - - // Remind: We get a deadlock here, if the Canceler does not run! - try - { - synchronized (lock) - { - lock.wait(); - } - } - catch (InterruptedException e) - { - // empty - } - } - - /** - * Unregister all services. - */ - public void unregisterAllServices() - { - if (logger.isDebugEnabled()) - logger.debug("unregisterAllServices()"); - if (services.size() == 0) - { - return; - } - - Collection<ServiceInfo> list; - synchronized (this) - { - list = new LinkedList<ServiceInfo>(services.values()); - services.clear(); - } - for (Iterator<ServiceInfo> iterator = list.iterator(); iterator.hasNext();) - { - iterator.next().cancel(); - } - - - Object lock = new Object(); - new Canceler(list, lock).start(); - // Remind: We get a livelock here, if the Canceler does not run! - try - { - synchronized (lock) - { - if (!closed) - { - lock.wait(); - } - } - } - catch (InterruptedException e) - { - // empty - } - } - - /** - * Register a service type. If this service type was not already known, - * all service listeners will be notified of the new service type. - * Service types are automatically registered as they are discovered. - * @param type of service - */ - public void registerServiceType(String type) - { - String name = type.toLowerCase(); - if (serviceTypes.get(name) == null) - { - if ((type.indexOf("._mdns._udp.") < 0) && - !type.endsWith(".in-addr.arpa.")) - { - Collection<ServiceTypeListener> list; - synchronized (this) - { - serviceTypes.put(name, type); - list = new LinkedList<ServiceTypeListener>(typeListeners); - } - for (ServiceTypeListener listener : list) - listener - .serviceTypeAdded( - new ServiceEvent(this, type, null, null)); - } - } - } - - /** - * Generate a possibly unique name for a service using the information we - * have in the cache. - * - * @return returns true, if the name of the service info had to be changed. - */ - private boolean makeServiceNameUnique(ServiceInfo info) - { - String originalQualifiedName = info.getQualifiedName(); - long now = System.currentTimeMillis(); - - boolean collision; - do - { - collision = false; - - // Check for collision in cache - for (DNSCache.CacheNode j = cache.find( - info.getQualifiedName().toLowerCase()); - j != null; - j = j.next()) - { - DNSRecord a = (DNSRecord) j.getValue(); - if ((a.type == DNSConstants.TYPE_SRV) && !a.isExpired(now)) - { - DNSRecord.Service s = (DNSRecord.Service) a; - if (s.port != info.port || !s.server.equals(localHost.getName())) - { - if (logger.isDebugEnabled()) - logger.debug("makeServiceNameUnique() " + - "JmDNS.makeServiceNameUnique srv collision:" + - a + " s.server=" + s.server + " " + - localHost.getName() + " equals:" + - (s.server.equals(localHost.getName()))); - info.setName(incrementName(info.getName())); - collision = true; - break; - } - } - } - - // Check for collision with other service infos published by JmDNS - Object selfService = - services.get(info.getQualifiedName().toLowerCase()); - if (selfService != null && selfService != info) - { - info.setName(incrementName(info.getName())); - collision = true; - } - } - while (collision); - - return !(originalQualifiedName.equals(info.getQualifiedName())); - } - - String incrementName(String name) - { - try - { - int l = name.lastIndexOf('('); - int r = name.lastIndexOf(')'); - if ((l >= 0) && (l < r)) - { - name = name.substring(0, l) + "(" + - (Integer.parseInt(name.substring(l + 1, r)) + 1) + ")"; - } - else - { - name += " (2)"; - } - } - catch (NumberFormatException e) - { - name += " (2)"; - } - return name; - } - - /** - * Add a listener for a question. The listener will receive updates - * of answers to the question as they arrive, or from the cache if they - * are already available. - * @param listener to be added - * @param question - which the listener is responsible for. - */ - public void addListener(DNSListener listener, DNSQuestion question) - { - long now = System.currentTimeMillis(); - - // add the new listener - synchronized (this) - { - listeners.add(listener); - } - - // report existing matched records - if (question != null) - { - for (DNSCache.CacheNode i = cache.find(question.name); - i != null; - i = i.next()) - { - DNSRecord c = (DNSRecord) i.getValue(); - if (question.answeredBy(c) && !c.isExpired(now)) - { - listener.updateRecord(this, now, c); - } - } - } - } - - /** - * Remove a listener from all outstanding questions. - * The listener will no longer receive any updates. - */ - void removeListener(DNSListener listener) - { - synchronized (this) - { - listeners.remove(listener); - } - } - - - // Remind: Method updateRecord should receive a better name. - /** - * Notify all listeners that a record was updated. - */ - void updateRecord(long now, DNSRecord rec) - { - // We do not want to block the entire DNS - // while we are updating the record for each listener (service info) - List<DNSListener> listenerList = null; - synchronized (this) - { - listenerList = new ArrayList<DNSListener>(listeners); - } - - //System.out.println("OUT OF MUTEX!!!!!"); - - for (DNSListener listener : listenerList) - listener.updateRecord(this, now, rec); - - if (rec.type == DNSConstants.TYPE_PTR || - rec.type == DNSConstants.TYPE_SRV) - { - List<ServiceListener> serviceListenerList = null; - synchronized (this) - { - serviceListenerList = serviceListeners.get(rec.name.toLowerCase()); - // Iterate on a copy in case listeners will modify it - if (serviceListenerList != null) - { - serviceListenerList = new ArrayList<ServiceListener>(serviceListenerList); - } - } - if (serviceListenerList != null) - { - boolean expired = rec.isExpired(now); - String type = rec.getName(); - String name = ((DNSRecord.Pointer) rec).getAlias(); - // DNSRecord old = (DNSRecord)services.get(name.toLowerCase()); - if (!expired) - { - // new record - ServiceEvent event = - new ServiceEvent( - this, - type, - toUnqualifiedName(type, name), - null); - for (Iterator<ServiceListener> iterator = serviceListenerList.iterator(); - iterator.hasNext();) - { - iterator.next().serviceAdded(event); - } - } - else - { - // expire record - ServiceEvent event = - new ServiceEvent( - this, - type, - toUnqualifiedName(type, name), - null); - for (Iterator<ServiceListener> iterator = serviceListenerList.iterator(); - iterator.hasNext();) - { - iterator.next().serviceRemoved(event); - } - } - } - } - } - - /** - * Handle an incoming response. Cache answers, and pass them on to - * the appropriate questions. - */ - private void handleResponse(DNSIncoming msg) - throws IOException - { - long now = System.currentTimeMillis(); - - boolean hostConflictDetected = false; - boolean serviceConflictDetected = false; - - if (logger.isTraceEnabled()) - logger.trace("JMDNS/handleResponse received " + - msg.answers.size()+ " messages"); - for (DNSRecord rec : msg.answers) - { - if (logger.isTraceEnabled()) - logger.trace("PRINT: "+ rec); - //cache.add(rec); - } - - for (DNSRecord rec : msg.answers) - { - boolean isInformative = false; - boolean expired = rec.isExpired(now); - - if (logger.isTraceEnabled()) - logger.trace("JMDNS received : " + rec + " expired: "+expired); - - // update the cache - DNSRecord c = (DNSRecord) cache.get(rec); - if (c != null) - { - if (logger.isTraceEnabled()) - logger.trace("JMDNS has found "+rec+" in cache"); - if (expired) - { - isInformative = true; - cache.remove(c); - } - else - { - /* Special case for SIP Communicator. - * We want to be informed if a cache entry is modified - */ -// if ((c.isUnique() -// && c.getType() == DNSConstants.TYPE_TXT -// && ((c.getClazz() & DNSConstants.CLASS_IN) != 0))) -// isInformative = true; -// c.resetTTL(rec); -// rec = c; - if (logger.isTraceEnabled()) - logger.trace( - new Boolean(c.isUnique()).toString() + - c.getType()+c.getClazz() + "/" + - DNSConstants.TYPE_TXT + " "+DNSConstants.CLASS_IN); - - if ((rec.isUnique() - && ((rec.getType() & DNSConstants.TYPE_TXT) != 0) - && ((rec.getClazz() & DNSConstants.CLASS_IN) != 0))) - { - if (logger.isTraceEnabled()) - logger.trace("UPDATING CACHE !! "); - isInformative = true; - cache.remove(c); - cache.add(rec); - } - else - { - c.resetTTL(rec); - rec = c; - } - } - } - else - { - if (!expired) - { - isInformative = true; - if (logger.isTraceEnabled()) - logger.trace("Adding "+rec+" to the cache"); - cache.add(rec); - } - } - switch (rec.type) - { - case DNSConstants.TYPE_PTR: - // handle _mdns._udp records - if (rec.getName().indexOf("._mdns._udp.") >= 0) - { - if (!expired && - rec.name.startsWith("_services._mdns._udp.")) - { - isInformative = true; - registerServiceType(((DNSRecord.Pointer)rec).alias); - } - continue; - } - registerServiceType(rec.name); - break; - } - - - if ((rec.getType() == DNSConstants.TYPE_A) || - (rec.getType() == DNSConstants.TYPE_AAAA)) - { - hostConflictDetected |= rec.handleResponse(this); - } - else - { - serviceConflictDetected |= rec.handleResponse(this); - } - - // notify the listeners - if (isInformative) - { - updateRecord(now, rec); - } - - - } - - if (hostConflictDetected || serviceConflictDetected) - { - new Prober().start(); - } - } - - /** - * Handle an incoming query. See if we can answer any part of it - * given our service infos. - */ - private void handleQuery(DNSIncoming in, InetAddress addr, int port) - throws IOException - { - // Track known answers - boolean hostConflictDetected = false; - boolean serviceConflictDetected = false; - long expirationTime = System.currentTimeMillis() + - DNSConstants.KNOWN_ANSWER_TTL; - for (DNSRecord answer : in.answers) - { - if ((answer.getType() == DNSConstants.TYPE_A) || - (answer.getType() == DNSConstants.TYPE_AAAA)) - { - hostConflictDetected |= - answer.handleQuery(this, expirationTime); - } - else - { - serviceConflictDetected |= - answer.handleQuery(this, expirationTime); - } - } - - if (plannedAnswer != null) - { - plannedAnswer.append(in); - } - else - { - if (in.isTruncated()) - { - plannedAnswer = in; - } - - new Responder(in, addr, port).start(); - } - - if (hostConflictDetected || serviceConflictDetected) - { - new Prober().start(); - } - } - - /** - * Add an answer to a question. Deal with the case when the - * outgoing packet overflows - */ - DNSOutgoing addAnswer(DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out, - DNSRecord rec) - throws IOException - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - try - { - out.addAnswer(in, rec); - } - catch (IOException e) - { - out.flags |= DNSConstants.FLAGS_TC; - out.id = in.id; - out.finish(); - send(out); - - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - out.addAnswer(in, rec); - } - return out; - } - - - /** - * Send an outgoing multicast DNS message. - */ - private void send(DNSOutgoing out) throws IOException - { - out.finish(); - if (!out.isEmpty()) - { - DatagramPacket packet = - new DatagramPacket( - out.data, out.off, group, DNSConstants.MDNS_PORT); - - try - { - DNSIncoming msg = new DNSIncoming(packet); - if (logger.isTraceEnabled()) - logger.trace("send() JmDNS out:" + msg.print(true)); - } - catch (IOException exc) - { - logger.error( - "send(DNSOutgoing) - JmDNS can not parse what it sends!!!", - exc); - } - socket.send(packet); - } - } - - /** - * Listen for multicast packets. - */ - class SocketListener implements Runnable - { - public void run() - { - try - { - byte buf[] = new byte[DNSConstants.MAX_MSG_ABSOLUTE]; - DatagramPacket packet = new DatagramPacket(buf, buf.length); - while (state != DNSState.CANCELED) - { - packet.setLength(buf.length); - socket.receive(packet); - if (state == DNSState.CANCELED) - { - break; - } - try - { - if (localHost.shouldIgnorePacket(packet)) - { - continue; - } - - DNSIncoming msg = new DNSIncoming(packet); - if (logger.isTraceEnabled()) - logger.trace("SocketListener.run() JmDNS in:" + - msg.print(true)); - - synchronized (ioLock) - { - if (msg.isQuery()) - { - if (packet.getPort() != DNSConstants.MDNS_PORT) - { - handleQuery(msg, - packet.getAddress(), - packet.getPort()); - } - handleQuery(msg, group, DNSConstants.MDNS_PORT); - } - else - { - handleResponse(msg); - } - } - } - catch (IOException e) - { - logger.warn( "run() exception ", e); - } - } - } - catch (IOException e) - { - if (state != DNSState.CANCELED) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - } - - - /** - * Periodicaly removes expired entries from the cache. - */ - private class RecordReaper extends TimerTask - { - public void start() - { - timer.schedule( this, - DNSConstants.RECORD_REAPER_INTERVAL, - DNSConstants.RECORD_REAPER_INTERVAL); - } - - @Override - public void run() - { - synchronized (JmDNS.this) - { - if (state == DNSState.CANCELED) - { - return; - } - if (logger.isTraceEnabled()) - logger.trace("run() JmDNS reaping cache"); - - // Remove expired answers from the cache - // ------------------------------------- - // To prevent race conditions, we defensively copy all cache - // entries into a list. - List<DNSEntry> list = new ArrayList<DNSEntry>(); - synchronized (cache) - { - for (Iterator<DNSCache.CacheNode> i = cache.iterator(); - i.hasNext();) - { - for (DNSCache.CacheNode n = i.next(); - n != null; - n = n.next()) - { - list.add(n.getValue()); - } - } - } - // Now, we remove them. - long now = System.currentTimeMillis(); - for (Iterator<DNSEntry> i = list.iterator(); i.hasNext();) - { - DNSRecord c = (DNSRecord)i.next(); - if (c.isExpired(now)) - { - updateRecord(now, c); - cache.remove(c); - } - } - } - } - } - - - /** - * The Prober sends three consecutive probes for all service infos - * that needs probing as well as for the host name. - * The state of each service info of the host name is advanced, - * when a probe has been sent for it. - * When the prober has run three times, it launches an Announcer. - * <p/> - * If a conflict during probes occurs, the affected service - * infos (and affected host name) are taken away from the prober. - * This eventually causes the prober tho cancel itself. - */ - private class Prober extends TimerTask - { - /** - * The state of the prober. - */ - DNSState taskState = DNSState.PROBING_1; - - public Prober() - { - // Associate the host name to this, if it needs probing - if (state == DNSState.PROBING_1) - { - task = this; - } - // Associate services to this, if they need probing - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> iterator = services.values().iterator(); - iterator.hasNext();) - { - ServiceInfo info = iterator.next(); - if (info.getState() == DNSState.PROBING_1) - { - info.task = this; - } - } - } - } - - - public void start() - { - long now = System.currentTimeMillis(); - if (now - lastThrottleIncrement < - DNSConstants.PROBE_THROTTLE_COUNT_INTERVAL) - { - throttle++; - } - else - { - throttle = 1; - } - lastThrottleIncrement = now; - - if (state == DNSState.ANNOUNCED && - throttle < DNSConstants.PROBE_THROTTLE_COUNT) - { - timer.schedule(this, - random.nextInt(1 + DNSConstants.PROBE_WAIT_INTERVAL), - DNSConstants.PROBE_WAIT_INTERVAL); - } - else - { - timer.schedule(this, - DNSConstants.PROBE_CONFLICT_INTERVAL, - DNSConstants.PROBE_CONFLICT_INTERVAL); - } - } - - @Override - public boolean cancel() - { - // Remove association from host name to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - synchronized (ioLock) - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState && task == this) - { - if (out == null) - { - out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - } - out.addQuestion( - new DNSQuestion( - localHost.getName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAuthorativeAnswer(answer); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAuthorativeAnswer(answer); - } - advanceState(); - } - // send probes for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new LinkedList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - - synchronized (info) - { - if (info.getState() == taskState && - info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS probing " + - info.getQualifiedName() + " state " + - info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - out.addAuthorativeAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS probing #" + taskState); - send(out); - } - else - { - // If we have nothing to send, another timer taskState - // ahead of us has done the job for us. We can cancel. - cancel(); - return; - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isProbing()) - { - cancel(); - - new Announcer().start(); - } - } - } - - } - - /** - * The Announcer sends an accumulated query of all announces, and advances - * the state of all serviceInfos, for which it has sent an announce. - * The Announcer also sends announcements and advances the state of JmDNS - * itself. - * <p/> - * When the announcer has run two times, it finishes. - */ - private class Announcer extends TimerTask - { - /** - * The state of the announcer. - */ - DNSState taskState = DNSState.ANNOUNCING_1; - - public Announcer() - { - // Associate host to this, if it needs announcing - if (state == DNSState.ANNOUNCING_1) - { - task = this; - } - // Associate services to this, if they need announcing - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - ServiceInfo info = s.next(); - if (info.getState() == DNSState.ANNOUNCING_1) - { - info.task = this; - } - } - } - } - - public void start() - { - timer.schedule(this, - DNSConstants.ANNOUNCE_WAIT_INTERVAL, - DNSConstants.ANNOUNCE_WAIT_INTERVAL); - } - - @Override - public boolean cancel() - { - // Remove association from host to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState) - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - advanceState(); - } - // send announces for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new ArrayList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - synchronized (info) - { - if (info.getState() == taskState && info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announcing " + - info.getQualifiedName() + - " state " + info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | - DNSConstants.FLAGS_AA); - } - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.text), 0); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announcing #" + taskState); - send(out); - } - else - { - // If we have nothing to send, another timer taskState ahead - // of us has done the job for us. We can cancel. - cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isAnnouncing()) - { - cancel(); - - new Renewer().start(); - } - } - } - - /** - * The Renewer is there to send renewal announcment - * when the record expire for ours infos. - */ - private class Renewer extends TimerTask - { - /** - * The state of the announcer. - */ - DNSState taskState = DNSState.ANNOUNCED; - - public Renewer() - { - // Associate host to this, if it needs renewal - if (state == DNSState.ANNOUNCED) - { - task = this; - } - // Associate services to this, if they need renewal - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - ServiceInfo info = s.next(); - if (info.getState() == DNSState.ANNOUNCED) - { - info.task = this; - } - } - } - } - - public void start() - { - timer.schedule(this, - DNSConstants.ANNOUNCED_RENEWAL_TTL_INTERVAL, - DNSConstants.ANNOUNCED_RENEWAL_TTL_INTERVAL); - } - - @Override - public boolean cancel() - { - // Remove association from host to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState) - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - advanceState(); - } - // send announces for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new ArrayList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - synchronized (info) - { - if (info.getState() == taskState && info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announced " + - info.getQualifiedName() + " state " + info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | - DNSConstants.FLAGS_AA); - } - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.text), 0); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announced"); - send(out); - } - else - { - // If we have nothing to send, another timer taskState ahead - // of us has done the job for us. We can cancel. - cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isAnnounced()) - { - cancel(); - - } - } - } - - /** - * The Responder sends a single answer for the specified service infos - * and for the host name. - */ - private class Responder extends TimerTask - { - private DNSIncoming in; - private InetAddress addr; - private int port; - - public Responder(DNSIncoming in, InetAddress addr, int port) - { - this.in = in; - this.addr = addr; - this.port = port; - } - - public void start() - { - // According to draft-cheshire-dnsext-multicastdns.txt - // chapter "8 Responding": - // We respond immediately if we know for sure, that we are - // the only one who can respond to the query. - // In all other cases, we respond within 20-120 ms. - // - // According to draft-cheshire-dnsext-multicastdns.txt - // chapter "7.2 Multi-Packet Known Answer Suppression": - // We respond after 20-120 ms if the query is truncated. - - boolean iAmTheOnlyOne = true; - for (DNSEntry entry : in.questions) - { - if (entry instanceof DNSQuestion) - { - DNSQuestion q = (DNSQuestion) entry; - if (logger.isTraceEnabled()) - logger.trace("start() question=" + q); - iAmTheOnlyOne &= (q.type == DNSConstants.TYPE_SRV - || q.type == DNSConstants.TYPE_TXT - || q.type == DNSConstants.TYPE_A - || q.type == DNSConstants.TYPE_AAAA - || localHost.getName().equalsIgnoreCase(q.name) - || services.containsKey(q.name.toLowerCase())); - if (!iAmTheOnlyOne) - { - break; - } - } - } - int delay = (iAmTheOnlyOne && !in.isTruncated()) ? - 0 : - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + - random.nextInt( - DNSConstants.RESPONSE_MAX_WAIT_INTERVAL - - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1) - - in.elapseSinceArrival(); - if (delay < 0) - { - delay = 0; - } - if (logger.isTraceEnabled()) - logger.trace("start() Responder chosen delay=" + delay); - timer.schedule(this, delay); - } - - @Override - public void run() - { - synchronized (ioLock) - { - if (plannedAnswer == in) - { - plannedAnswer = null; - } - - // We use these sets to prevent duplicate records - // FIXME - This should be moved into DNSOutgoing - HashSet<DNSQuestion> questions = new HashSet<DNSQuestion>(); - HashSet<DNSRecord> answers = new HashSet<DNSRecord>(); - - - if (state == DNSState.ANNOUNCED) - { - try - { - boolean isUnicast = (port != DNSConstants.MDNS_PORT); - - - // Answer questions - for (Iterator<DNSEntry> iterator = in.questions.iterator(); - iterator.hasNext();) - { - DNSEntry entry = iterator.next(); - if (entry instanceof DNSQuestion) - { - DNSQuestion q = (DNSQuestion) entry; - - // for unicast responses the question - // must be included - if (isUnicast) - { - //out.addQuestion(q); - questions.add(q); - } - - int type = q.type; - if (type == DNSConstants.TYPE_ANY || - type == DNSConstants.TYPE_SRV) - { // I ama not sure of why there is a special - // case here [PJYF Oct 15 2004] - if (localHost.getName(). - equalsIgnoreCase(q.getName())) - { - // type = DNSConstants.TYPE_A; - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - type = DNSConstants.TYPE_IGNORE; - } - else - { - if (serviceTypes.containsKey( - q.getName().toLowerCase())) - { - type = DNSConstants.TYPE_PTR; - } - } - } - - switch (type) - { - case DNSConstants.TYPE_A: - { - // Answer a query for a domain name - //out = addAnswer( in, addr, port, out, host ); - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - break; - } - case DNSConstants.TYPE_AAAA: - { - // Answer a query for a domain name - DNSRecord answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - break; - } - case DNSConstants.TYPE_PTR: - { - // Answer a query for services of a given type - - // find matching services - for (Iterator<ServiceInfo> serviceIterator = - services.values().iterator(); - serviceIterator.hasNext();) - { - ServiceInfo info = serviceIterator.next(); - if (info.getState() == DNSState.ANNOUNCED) - { - if (q.name.equalsIgnoreCase(info.type)) - { - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answers.add( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName())); - answers.add( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - answers.add( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text)); - } - } - } - if (q.name.equalsIgnoreCase("_services._mdns._udp.local.")) - { - for (Iterator<String> serviceTypeIterator = serviceTypes.values().iterator(); - serviceTypeIterator.hasNext();) - { - answers.add( - new DNSRecord.Pointer( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - serviceTypeIterator.next())); - } - } - break; - } - case DNSConstants.TYPE_SRV: - case DNSConstants.TYPE_ANY: - case DNSConstants.TYPE_TXT: - { - ServiceInfo info = services.get(q.name.toLowerCase()); - if (info != null && - info.getState() == DNSState.ANNOUNCED) - { - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answers.add( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName())); - answers.add( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - answers.add( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text)); - } - break; - } - default : - { - //System.out.println("JmDNSResponder.unhandled query:"+q); - break; - } - } - } - } - - - // remove known answers, if the ttl is at least half of - // the correct value. (See Draft Cheshire chapter 7.1.). - for (DNSRecord knownAnswer : in.answers) - { - if (knownAnswer.ttl > DNSConstants.DNS_TTL / 2 && - answers.remove(knownAnswer)) - { - if (logger.isDebugEnabled()) - logger.debug( - "JmDNS Responder Known Answer Removed"); - } - } - - - // responde if we have answers - if (answers.size() != 0) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS responding"); - DNSOutgoing out = null; - if (isUnicast) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE - | DNSConstants.FLAGS_AA, - false); - } - - for (Iterator<DNSQuestion> i = questions.iterator(); - i.hasNext();) - { - out.addQuestion(i.next()); - } - for (Iterator<DNSRecord> i = answers.iterator(); - i.hasNext();) - { - out = addAnswer(in, addr, port, out, i.next()); - } - send(out); - } - this.cancel(); - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - close(); - } - } - } - } - } - - /** - * Helper class to resolve service types. - * <p/> - * The TypeResolver queries three times consecutively for service types, and then - * removes itself from the timer. - * <p/> - * The TypeResolver will run only if JmDNS is in state ANNOUNCED. - */ - private class TypeResolver extends TimerTask - { - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - /** - * Counts the number of queries that were sent. - */ - int count = 0; - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS querying type"); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN)); - for (String serviceType : serviceTypes.values()) - { - out.addAnswer( - new DNSRecord.Pointer( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - serviceType), 0); - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The ServiceResolver queries three times consecutively for services of - * a given type, and then removes itself from the timer. - * <p/> - * The ServiceResolver will run only if JmDNS is in state ANNOUNCED. - * REMIND: Prevent having multiple service resolvers for the same type in the - * timer queue. - */ - private class ServiceResolver extends TimerTask - { - /** - * Counts the number of queries being sent. - */ - int count = 0; - private String type; - - public ServiceResolver(String type) - { - this.type = type; - } - - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS querying service"); - long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN)); - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - final ServiceInfo info = s.next(); - try - { - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), now); - } - catch (IOException ee) - { - break; - } - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The ServiceInfoResolver queries up to three times consecutively for - * a service info, and then removes itself from the timer. - * <p/> - * The ServiceInfoResolver will run only if JmDNS is in state ANNOUNCED. - * REMIND: Prevent having multiple service resolvers for the same info in the - * timer queue. - */ - private class ServiceInfoResolver extends TimerTask - { - /** - * Counts the number of queries being sent. - */ - int count = 0; - private ServiceInfo info; - - public ServiceInfoResolver(ServiceInfo info) - { - this.info = info; - info.dns = JmDNS.this; - addListener(info, - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3 && !info.hasData()) - { - long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN)); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN)); - if (info.server != null) - { - out.addQuestion( - new DNSQuestion( - info.server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN)); - } - out.addAnswer((DNSRecord) cache.get( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN), now); - out.addAnswer((DNSRecord) cache.get( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN), now); - if (info.server != null) - { - out.addAnswer((DNSRecord) cache.get( - info.server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN), now); - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - removeListener(info); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - removeListener(info); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The Canceler sends two announces with TTL=0 for the specified services. - */ - /* TODO: Clarify whether 2 or 3 announces should be sent. The header says 2, - * run() uses the (misleading) ++count < 3 (while all other tasks use count++ < 3) - * and the comment in the else block in run() says: "After three successful..." - */ - public class Canceler extends TimerTask - { - /** - * Counts the number of announces being sent. - */ - int count = 0; - /** - * The services that need cancelling. - * Note: We have to use a local variable here, because the services - * that are canceled, are removed immediately from variable JmDNS.services. - */ - private ServiceInfo[] infos; - /** - * We call notifyAll() on the lock object, when we have canceled the - * service infos. - * This is used by method JmDNS.unregisterService() and - * JmDNS.unregisterAllServices, to ensure that the JmDNS - * socket stays open until the Canceler has canceled all services. - * <p/> - * Note: We need this lock, because ServiceInfos do the transition from - * state ANNOUNCED to state CANCELED before we get here. We could get - * rid of this lock, if we added a state named CANCELLING to DNSState. - */ - private Object lock; - int ttl = 0; - - public Canceler(ServiceInfo info, Object lock) - { - this.infos = new ServiceInfo[]{info}; - this.lock = lock; - addListener(info, - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - - public Canceler(ServiceInfo[] infos, Object lock) - { - this.infos = infos; - this.lock = lock; - } - - public Canceler(Collection<ServiceInfo> infos, Object lock) - { - this.infos = infos.toArray(new ServiceInfo[infos.size()]); - this.lock = lock; - } - - public void start() - { - timer.schedule(this, 0, DNSConstants.ANNOUNCE_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (++count < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS canceling service"); - // announce the service - //long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - for (int i = 0; i < infos.length; i++) - { - ServiceInfo info = infos[i]; - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - ttl, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - ttl, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - ttl, - info.text), 0); - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - } - send(out); - } - else - { - // After three successful announcements, we are finished. - synchronized (lock) - { - closed=true; - lock.notifyAll(); - } - this.cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * Recover jmdns when there is an error. - */ - protected void recover() - { - if (logger.isDebugEnabled()) - logger.debug("recover()"); - // We have an IO error so lets try to recover if anything happens lets close it. - // This should cover the case of the IP address changing under our feet - if (DNSState.CANCELED != state) - { - synchronized (this) - { // Synchronize only if we are not already in process to prevent dead locks - // - if (logger.isDebugEnabled()) - logger.debug("recover() Cleanning up"); - // Stop JmDNS - state = DNSState.CANCELED; // This protects against recursive calls - - // We need to keep a copy for reregistration - Collection<ServiceInfo> oldServiceInfos = new ArrayList<ServiceInfo>(services.values()); - - // Cancel all services - unregisterAllServices(); - disposeServiceCollectors(); - // - // close multicast socket - closeMulticastSocket(); - // - cache.clear(); - if (logger.isDebugEnabled()) - logger.debug("recover() All is clean"); - // - // All is clear now start the services - // - try - { - openMulticastSocket(localHost); - start(oldServiceInfos); - } - catch (Exception exception) - { - logger.warn( - "recover() Start services exception ", exception); - } - logger.warn( "recover() We are back!"); - } - } - } - - /** - * Close down jmdns. Release all resources and unregister all services. - */ - public void close() - { - if (state != DNSState.CANCELED) - { - synchronized (this) - { // Synchronize only if we are not already in process to prevent dead locks - // Stop JmDNS - state = DNSState.CANCELED; // This protects against recursive calls - - unregisterAllServices(); - disposeServiceCollectors(); - - // close socket - closeMulticastSocket(); - - // Stop the timer - timer.cancel(); - } - } - } - - /** - * List cache entries, for debugging only. - */ - void print() - { - if (logger.isInfoEnabled()) - logger.info("---- cache ----\n"); - cache.print(); - if (logger.isInfoEnabled()) - logger.info("\n"); - } - - /** - * List Services and serviceTypes. - * Debugging Only - */ - - public void printServices() - { - if (logger.isInfoEnabled()) - logger.info(toString()); - } - - @Override - public String toString() - { - StringBuffer aLog = new StringBuffer(); - aLog.append("\t---- Services -----"); - if (services != null) - { - for (Map.Entry<String, ServiceInfo> entry : services.entrySet()) - { - aLog.append("\n\t\tService: " + entry.getKey() + ": " - + entry.getValue()); - } - } - aLog.append("\n"); - aLog.append("\t---- Types ----"); - if (serviceTypes != null) - { - for (Map.Entry<String, String> entry : serviceTypes.entrySet()) - { - aLog.append("\n\t\tType: " + entry.getKey() + ": " - + entry.getValue()); - } - } - aLog.append("\n"); - aLog.append(cache.toString()); - aLog.append("\n"); - aLog.append("\t---- Service Collectors ----"); - if (serviceCollectors != null) - { - synchronized (serviceCollectors) - { - for (Map.Entry<String, ServiceCollector> entry - : serviceCollectors.entrySet()) - { - aLog.append("\n\t\tService Collector: " + entry.getKey() - + ": " + entry.getValue()); - } - serviceCollectors.clear(); - } - } - return aLog.toString(); - } - - /** - * Returns a list of service infos of the specified type. - * - * @param type Service type name, such as <code>_http._tcp.local.</code>. - * @return An array of service instance names. - */ - public ServiceInfo[] list(String type) - { - // Implementation note: The first time a list for a given type is - // requested, a ServiceCollector is created which collects service - // infos. This greatly speeds up the performance of subsequent calls - // to this method. The caveats are, that 1) the first call to this method - // for a given type is slow, and 2) we spawn a ServiceCollector - // instance for each service type which increases network traffic a - // little. - - ServiceCollector collector; - - boolean newCollectorCreated; - synchronized (serviceCollectors) - { - collector = serviceCollectors.get(type); - if (collector == null) - { - collector = new ServiceCollector(type); - serviceCollectors.put(type, collector); - addServiceListener(type, collector); - newCollectorCreated = true; - } - else - { - newCollectorCreated = false; - } - } - - // After creating a new ServiceCollector, we collect service infos for - // 200 milliseconds. This should be enough time, to get some service - // infos from the network. - if (newCollectorCreated) - { - try - { - Thread.sleep(200); - } - catch (InterruptedException e) - { - } - } - - return collector.list(); - } - - /** - * This method disposes all ServiceCollector instances which have been - * created by calls to method <code>list(type)</code>. - * - * @see #list - */ - private void disposeServiceCollectors() - { - if (logger.isDebugEnabled()) - logger.debug("disposeServiceCollectors()"); - synchronized (serviceCollectors) - { - for (Iterator<ServiceCollector> i = serviceCollectors.values().iterator(); i.hasNext();) - { - ServiceCollector collector = i.next(); - removeServiceListener(collector.type, collector); - } - serviceCollectors.clear(); - } - } - - /** - * Instances of ServiceCollector are used internally to speed up the - * performance of method <code>list(type)</code>. - * - * @see #list - */ - private static class ServiceCollector implements ServiceListener - { - - /** - * A set of collected service instance names. - */ - private Map<String, ServiceInfo> infos = Collections.synchronizedMap(new HashMap<String, ServiceInfo>()); - - public String type; - - public ServiceCollector(String type) - { - this.type = type; - } - - /** - * A service has been added. - */ - public void serviceAdded(ServiceEvent event) - { - synchronized (infos) - { - event.getDNS().requestServiceInfo( - event.getType(), event.getName(), 0); - } - } - - /** - * A service has been removed. - */ - public void serviceRemoved(ServiceEvent event) - { - synchronized (infos) - { - infos.remove(event.getName()); - } - } - - /** - * A service hase been resolved. Its details are now available in the - * ServiceInfo record. - */ - public void serviceResolved(ServiceEvent event) - { - synchronized (infos) - { - infos.put(event.getName(), event.getInfo()); - } - } - - /** - * Returns an array of all service infos which have been collected by this - * ServiceCollector. - * @return - */ - public ServiceInfo[] list() - { - synchronized (infos) - { - return infos.values(). - toArray(new ServiceInfo[infos.size()]); - } - } - - @Override - public String toString() - { - StringBuffer aLog = new StringBuffer(); - synchronized (infos) - { - for (Map.Entry<String, ServiceInfo> entry : infos.entrySet()) - { - aLog.append("\n\t\tService: " + entry.getKey() + ": " - + entry.getValue()); - } - } - return aLog.toString(); - } - }; - - private static String toUnqualifiedName(String type, String qualifiedName) - { - if (qualifiedName.endsWith(type)) - { - return qualifiedName.substring(0, - qualifiedName.length() - type.length() - 1); - } - else - { - return qualifiedName; - } - } - - /** - * SC-Bonjour Implementation : Method used to update the corresponding DNS - * entry in the cache of JmDNS with the new information in this ServiceInfo. - * A call to getLocalService must first be issued to get the - * ServiceInfo object to be modified. - * THIS METHOD MUST BE USED INSTEAD OF ANY DIRECT ACCESS TO JMDNS' CACHE!! - * This is used in the implementation of Zeroconf in SIP Communicator - * to be able to change fields declared by the local contact (status, etc). - * @param info Updated service data to be used to replace the old - * stuff contained in JmDNS' cache - * @param old info bytes - */ - public void updateInfos(ServiceInfo info, byte[] old) - { - - DNSOutgoing out, out2; - synchronized (JmDNS.this) - { - //list = new ArrayList(services.values()); - services.put(info.getQualifiedName().toLowerCase(), info); - } - - synchronized (info) - { - if (logger.isDebugEnabled()) - logger.debug("updateInfos() JmDNS updating " + - info.getQualifiedName() + " state " + - info.getState()); - - out = new DNSOutgoing( - /*DNSConstants.FLAGS_QR_RESPONSE*/ - DNSConstants.FLAGS_RA | DNSConstants.FLAGS_AA); - out2 = new DNSOutgoing( - /*DNSConstants.FLAGS_QR_RESPONSE*/ - DNSConstants.FLAGS_RA | DNSConstants.FLAGS_AA); - - - try - { - //out.addAnswer(new DNSRecord.Pointer(info.type, DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.getQualifiedName()), 0); - //out.addAnswer(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_A, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.priority, info.weight, info.port, localHost.getName()), 0); - //out.addAnswer(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.priority, info.weight, info.port, localHost.getName()), 0); -// out.addAnswer( -// new DNSRecord.Text( -// info.getQualifiedName(), -// DNSConstants.TYPE_TXT, -// DNSConstants.CLASS_IN , -// DNSConstants.DNS_TTL, -// info.text), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN , - 0, - old), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text), 0); - - out2.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text), 0); - - if (logger.isDebugEnabled()) - logger.debug("updateInfos() JmDNS updated infos for "+info); - - send(out); - Thread.sleep(1000); - send(out2); - Thread.sleep(2000); - send(out2); - } - catch( Exception e) - { - logger.warn( "", e); - } - } - } - - - /** - * SC-Bonjour Implementation: Method to retrieve the DNS Entry corresponding to a service - * that has been declared and return it as a ServiceInfo structure. - * It is used in the implementation of Bonjour in SIP Communicator to retrieve the information - * concerning the service declared by the local contact. THIS METHOD MUST BE USED INSTEAD OF ANY - * LOCAL COPY SAVED BEFORE SERVICE REGISTRATION!! - * @return information corresponding to the specified service - * @param FQN String representing the Fully Qualified name of the service we want info about - */ - public ServiceInfo getLocalService(String FQN) - { - return services.get(FQN); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java deleted file mode 100644 index ff922d1..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; -import java.util.logging.*; - -/** - * ServiceEvent. - * - * @author Werner Randelshofer, Rick Blair - * @version %I%, %G% - */ -public class ServiceEvent - extends EventObject -{ - private static Logger logger = - Logger.getLogger(ServiceEvent.class.toString()); - /** - * The type name of the service. - */ - private String type; - /** - * The instance name of the service. Or null, if the event was - * fired to a service type listener. - */ - private String name; - /** - * The service info record, or null if the service could be be resolved. - * This is also null, if the event was fired to a service type listener. - */ - private ServiceInfo info; - - /** - * Creates a new instance. - * - * @param source the JmDNS instance which originated the event. - * @param type the type name of the service. - * @param name the instance name of the service. - * @param info the service info record, or null if the - * service could be be resolved. - */ - public ServiceEvent(JmDNS source, String type, String name, ServiceInfo info) - { - super(source); - this.type = type; - this.name = name; - this.info = info; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Returns the JmDNS instance which originated the event. - * @return Returns the JmDNS instance which originated the event. - */ - public JmDNS getDNS() - { - return (JmDNS) getSource(); - } - - /** - * Returns the fully qualified type of the service. - * @return Returns the fully qualified type of the service. - */ - public String getType() - { - return type; - } - - /** - * Returns the instance name of the service. - * Always returns null, if the event is sent to a service type listener. - * @return Returns the instance name of the service. - */ - public String getName() - { - return name; - } - - /** - * Returns the service info record, or null if the service could not be - * resolved. - * Always returns null, if the event is sent to a service type listener. - * @return Returns the service info record. - */ - public ServiceInfo getInfo() - { - return info; - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("<" + getClass().getName() + "> "); - buf.append(super.toString()); - buf.append(" name "); - buf.append(getName()); - buf.append(" type "); - buf.append(getType()); - buf.append(" info "); - buf.append(getInfo()); - return buf.toString(); - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java deleted file mode 100644 index b7f7f2d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.*; - -/** - * JmDNS service information. - * - * @version %I%, %G% - * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer - * @author Christian Vincenot - */ -public class ServiceInfo implements DNSListener -{ - private static Logger logger = - Logger.getLogger(ServiceInfo.class.toString()); - public final static byte[] NO_VALUE = new byte[0]; - JmDNS dns; - - // State machine - /** - * The state of this service info. - * This is used only for services announced by JmDNS. - * <p/> - * For proper handling of concurrency, this variable must be - * changed only using methods advanceState(), revertState() and cancel(). - */ - private DNSState state = DNSState.PROBING_1; - - /** - * Task associated to this service info. - * Possible tasks are JmDNS.Prober, JmDNS.Announcer, JmDNS.Responder, - * JmDNS.Canceler. - */ - TimerTask task; - - String type; - private String name; - String server; - int port; - int weight; - int priority; - byte text[]; - private Map<String, Object> props; - InetAddress addr; - - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param text string describing the service - */ - public ServiceInfo(String type, String name, int port, String text) - { - this(type, name, port, 0, 0, text); - } - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param text string describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, String text) - { - this(type, name, port, weight, priority, (byte[]) null); - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(text.length()); - writeUTF(out, text); - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - - /** - * Construct a service description for registrating with JmDNS. The properties hashtable must - * map property names to either Strings or byte arrays describing the property values. - * - * @param type fully qualified service type name, such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param props properties describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, Map<String, Object> props) - { - this(type, name, port, weight, priority, new byte[0]); - if (props != null) - { - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(256); - for (Map.Entry<String, Object> prop : props.entrySet()) - { - String key = prop.getKey(); - Object val = prop.getValue(); - ByteArrayOutputStream out2 = new ByteArrayOutputStream(100); - writeUTF(out2, key); - if (val instanceof String) - { - out2.write('='); - writeUTF(out2, (String) val); - } - else - { - if (val instanceof byte[]) - { - out2.write('='); - byte[] bval = (byte[]) val; - out2.write(bval, 0, bval.length); - } - else - { - if (val != NO_VALUE) - { - throw new IllegalArgumentException( - "invalid property value: " + val); - } - } - } - byte data[] = out2.toByteArray(); - out.write(data.length); - out.write(data, 0, data.length); - } - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - } - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param text bytes describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, byte text[]) - { - this.type = type; - this.name = name; - this.port = port; - this.weight = weight; - this.priority = priority; - this.text = text; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Construct a service record during service discovery. - */ - ServiceInfo(String type, String name) - { - if (!type.endsWith(".")) - { - throw new IllegalArgumentException( - "type must be fully qualified DNS name ending in '.': " + type); - } - - this.type = type; - this.name = name; - } - - /** - * During recovery we need to duplicate service info to reregister them - */ - ServiceInfo(ServiceInfo info) - { - if (info != null) - { - this.type = info.type; - this.name = info.name; - this.port = info.port; - this.weight = info.weight; - this.priority = info.priority; - this.text = info.text; - } - } - - /** - * Fully qualified service type name, - * such as <code>_http._tcp.local.</code> . - * @return Returns fully qualified service type name. - */ - public String getType() - { - return type; - } - - /** - * Unqualified service instance name, - * such as <code>foobar</code> . - * @return Returns unqualified service instance name. - */ - public String getName() - { - return name; - } - - /** - * Sets the service instance name. - * - * @param name unqualified service instance name, - * such as <code>foobar</code> - */ - void setName(String name) - { - this.name = name; - } - - /** - * Fully qualified service name, - * such as <code>foobar._http._tcp.local.</code> . - * @return Returns fully qualified service name. - */ - public String getQualifiedName() - { - return name + "." + type; - } - - /** - * Get the name of the server. - * @return Returns name of the server. - */ - public String getServer() - { - return server; - } - - /** - * Get the host address of the service (ie X.X.X.X). - * @return Returns host address of the service. - */ - public String getHostAddress() - { - return (addr != null ? addr.getHostAddress() : ""); - } - - public InetAddress getAddress() - { - return addr; - } - - /** - * Get the InetAddress of the service. - * @return Returns the InetAddress of the service. - */ - public InetAddress getInetAddress() - { - return addr; - } - - /** - * Get the port for the service. - * @return Returns port for the service. - */ - public int getPort() - { - return port; - } - - /** - * Get the priority of the service. - * @return Returns the priority of the service. - */ - public int getPriority() - { - return priority; - } - - /** - * Get the weight of the service. - * @return Returns the weight of the service. - */ - public int getWeight() - { - return weight; - } - - /** - * Get the text for the serivce as raw bytes. - * @return Returns the text for the serivce as raw bytes. - */ - public byte[] getTextBytes() - { - return text; - } - - /** - * Get the text for the service. This will interpret the text bytes - * as a UTF8 encoded string. Will return null if the bytes are not - * a valid UTF8 encoded string. - * @return Returns the text for the service. - */ - public String getTextString() - { - if ((text == null) || - (text.length == 0) || - ((text.length == 1) && (text[0] == 0))) - { - return null; - } - return readUTF(text, 0, text.length); - } - - /** - * Get the URL for this service. An http URL is created by - * combining the address, port, and path properties. - * @return Returns the URL for this service. - */ - public String getURL() - { - return getURL("http"); - } - - /** - * Get the URL for this service. An URL is created by - * combining the protocol, address, port, and path properties. - * @param protocol - * @return Returns URL for this service. - */ - public String getURL(String protocol) - { - String url = protocol + "://" + getHostAddress() + ":" + getPort(); - String path = getPropertyString("path"); - if (path != null) - { - if (path.indexOf("://") >= 0) - { - url = path; - } - else - { - url += path.startsWith("/") ? path : "/" + path; - } - } - return url; - } - - /** - * Get a property of the service. This involves decoding the - * text bytes into a property list. Returns null if the property - * is not found or the text data could not be decoded correctly. - * @param name - * @return Returns property of the service as bytes. - */ - public synchronized byte[] getPropertyBytes(String name) - { - return (byte[]) getProperties().get(name); - } - - /** - * Get a property of the service. This involves decoding the - * text bytes into a property list. Returns null if the property - * is not found, the text data could not be decoded correctly, or - * the resulting bytes are not a valid UTF8 string. - * @param name - * @return Returns property of the service as string. - */ - public synchronized String getPropertyString(String name) - { - byte data[] = (byte[]) getProperties().get(name); - - if (data == null) - { - return null; - } - if (data == NO_VALUE) - { - return "true"; - } - String res = readUTF(data, 0, data.length); - - return res; - } - - /** - * Iterator<String> of the property names. - * @return Iterator<String> of the property names. - */ - public Iterator<String> getPropertyNames() - { - Map<String, Object> properties = getProperties(); - Iterable<String> propertyNames - = (properties != null) ? properties.keySet() : new Vector<String>(); - return propertyNames.iterator(); - } - - /** - * Write a UTF string with a length to a stream. - */ - void writeUTF(OutputStream out, String str) throws IOException - { - for (int i = 0, len = str.length(); i < len; i++) - { - int c = str.charAt(i); - if ((c >= 0x0001) && (c <= 0x007F)) - { - out.write(c); - } - else - { - if (c > 0x07FF) - { - out.write(0xE0 | ((c >> 12) & 0x0F)); - out.write(0x80 | ((c >> 6) & 0x3F)); - out.write(0x80 | ((c >> 0) & 0x3F)); - } - else - { - out.write(0xC0 | ((c >> 6) & 0x1F)); - out.write(0x80 | ((c >> 0) & 0x3F)); - } - } - } - } - - /** - * Read data bytes as a UTF stream. - */ - String readUTF(byte data[], int off, int len) - { - StringBuffer buf = new StringBuffer(); - for (int end = off + len; off < end;) - { - int ch = data[off++] & 0xFF; - switch (ch >> 4) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - break; - case 12: - case 13: - if (off >= len) - { - return null; - } - // 110x xxxx 10xx xxxx - ch = ((ch & 0x1F) << 6) | (data[off++] & 0x3F); - break; - case 14: - if (off + 2 >= len) - { - return null; - } - // 1110 xxxx 10xx xxxx 10xx xxxx - ch = ((ch & 0x0f) << 12) | - ((data[off++] & 0x3F) << 6) | - (data[off++] & 0x3F); - break; - default: - if (off + 1 >= len) - { - return null; - } - // 10xx xxxx, 1111 xxxx - ch = ((ch & 0x3F) << 4) | (data[off++] & 0x0f); - break; - } - buf.append((char) ch); - } - return buf.toString(); - } - - synchronized Map<String, Object> getProperties() - { - if ((props == null) && (text != null)) - { - Map<String, Object> props = new Hashtable<String, Object>(); - int off = 0; - while (off < text.length) - { - // length of the next key value pair - int len = text[off++] & 0xFF; - if ((len == 0) || (off + len > text.length)) - { - props.clear(); - break; - } - // look for the '=' - int i = 0; - for (; (i < len) && (text[off + i] != '='); i++) - { - ; - } - - // get the property name - String name = readUTF(text, off, i); - if (name == null) - { - props.clear(); - break; - } - if (i == len) - { - props.put(name, NO_VALUE); - } - else - { - byte value[] = new byte[len - ++i]; - System.arraycopy(text, off + i, value, 0, len - i); - props.put(name, value); - off += len; - } - } - this.props = props; - } - return props; - } - - - /** - * JmDNS callback to update a DNS record. - * @param rec - */ - public void updateRecord(JmDNS jmdns, long now, DNSRecord rec) - { - if ((rec != null) && !rec.isExpired(now)) - { - switch (rec.type) - { - case DNSConstants.TYPE_A: // IPv4 - case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested - if (rec.name.equals(server)) - { - addr = ((DNSRecord.Address) rec).getAddress(); - - } - break; - case DNSConstants.TYPE_SRV: - if (rec.name.equals(getQualifiedName())) - { - DNSRecord.Service srv = (DNSRecord.Service) rec; - server = srv.server; - port = srv.port; - weight = srv.weight; - priority = srv.priority; - addr = null; - // changed to use getCache() instead - jeffs - // updateRecord(jmdns, now, (DNSRecord)jmdns.cache.get(server, TYPE_A, CLASS_IN)); - updateRecord(jmdns, - now, - (DNSRecord) jmdns.getCache().get( - server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN)); - } - break; - case DNSConstants.TYPE_TXT: - if (rec.name.equals(getQualifiedName())) - { - DNSRecord.Text txt = (DNSRecord.Text) rec; - text = txt.text; - } - break; - } - // Future Design Pattern - // This is done, to notify the wait loop in method - // JmDNS.getServiceInfo(type, name, timeout); - if (hasData() && dns != null) - { - dns.handleServiceResolved(this); - dns = null; - } - synchronized (this) - { - notifyAll(); - } - } - } - - /** - * Returns true if the service info is filled with data. - */ - boolean hasData() - { - return server != null && addr != null && text != null; - } - - - // State machine - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void advanceState() - { - state = state.advance(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void revertState() - { - state = state.revert(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void cancel() - { - state = DNSState.CANCELED; - notifyAll(); - } - - /** - * Returns the current state of this info. - */ - DNSState getState() - { - return state; - } - - - @Override - public int hashCode() - { - return getQualifiedName().hashCode(); - } - - @Override - public boolean equals(Object obj) - { - return (obj instanceof ServiceInfo) && - getQualifiedName().equals(((ServiceInfo) obj).getQualifiedName()); - } - - public String getNiceTextString() - { - StringBuffer buf = new StringBuffer(); - for (int i = 0, len = text.length; i < len; i++) - { - if (i >= 20) - { - buf.append("..."); - break; - } - int ch = text[i] & 0xFF; - if ((ch < ' ') || (ch > 127)) - { - buf.append("\\0"); - buf.append(Integer.toString(ch, 8)); - } - else - { - buf.append((char) ch); - } - } - return buf.toString(); - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("service["); - buf.append(getQualifiedName()); - buf.append(','); - buf.append(getAddress()); - buf.append(':'); - buf.append(port); - buf.append(','); - buf.append(getNiceTextString()); - buf.append(']'); - return buf.toString(); - } - - /** - * SC-Bonjour Implementation: Method used to set the properties of an existing ServiceInfo. - * This is used in the implementation of Bonjour in SIP Communicator to be able to replace - * old properties of the service we've declared to announce the local user with new properties - * (for example in case of a status change). - * @param props Hashtable containing all the new properties to set - */ - public void setProps(Map<String, Object> props) - { - if (props != null) - { - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(256); - for (Map.Entry<String, Object> prop : props.entrySet()) - { - String key = prop.getKey(); - Object val = prop.getValue(); - - ByteArrayOutputStream out2 = new ByteArrayOutputStream(100); - writeUTF(out2, key); - if (val instanceof String) - { - out2.write('='); - writeUTF(out2, (String) val); - } - else - { - if (val instanceof byte[]) - { - out2.write('='); - byte[] bval = (byte[]) val; - out2.write(bval, 0, bval.length); - } - else - { - if (val != NO_VALUE) - { - throw new IllegalArgumentException( - "invalid property value: " + val); - } - } - } - byte data[] = out2.toByteArray(); - out.write(data.length); - out.write(data, 0, data.length); - } - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java deleted file mode 100644 index 1c34adf..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -/** - * Listener for service updates. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer - */ -public interface ServiceListener extends EventListener -{ - /** - * A service has been added. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - - void serviceAdded(ServiceEvent event); - - /** - * A service has been removed. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - void serviceRemoved(ServiceEvent event); - - /** - * A service has been resolved. Its details are now available in the - * ServiceInfo record. - * - * @param event The ServiceEvent providing the name, the fully qualified - * type of the service, and the service info record, - * or null if the service could not be resolved. - */ - - void serviceResolved(ServiceEvent event); -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java deleted file mode 100644 index 84e5c59..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -/** - * Listener for service types. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer - */ -public interface ServiceTypeListener extends EventListener -{ - /** - * A new service type was discovered. - * - * @param event The service event providing the fully qualified type of - * the service. - */ - void serviceTypeAdded(ServiceEvent event); -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/zeroconf.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/zeroconf/zeroconf.provider.manifest.mf deleted file mode 100644 index 24daba0..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/zeroconf.provider.manifest.mf +++ /dev/null @@ -1,12 +0,0 @@ -Bundle-Activator: net.java.sip.communicator.impl.protocol.zeroconf.ZeroconfActivator -Bundle-Name: Zeroconf Protocol Provider -Bundle-Description: A bundle providing support for the Zeroconf protocol. -Bundle-Vendor: jitsi.org -Bundle-Version: 0.0.1 -Bundle-SymbolicName: net.java.sip.communicator.protocol.zeroconf -Import-Package: org.osgi.framework, - org.jitsi.service.configuration, - org.jitsi.service.resources, net.java.sip.communicator.service.resources, - net.java.sip.communicator.util, - net.java.sip.communicator.service.protocol, - net.java.sip.communicator.service.protocol.event |