/** * WhiteboardObjectTextJabberImpl constructor. * * @param xml the XML string object to parse. */ public WhiteboardObjectTextJabberImpl(String xml) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); InputStream in = new ByteArrayInputStream(xml.getBytes()); Document doc = builder.parse(in); Element e = doc.getDocumentElement(); String elementName = e.getNodeName(); if (elementName.equals("text")) { // we have a text String id = e.getAttribute("id"); double x = Double.parseDouble(e.getAttribute("x")); double y = Double.parseDouble(e.getAttribute("y")); String fill = e.getAttribute("fill"); String fontFamily = e.getAttribute("font-family"); int fontSize = Integer.parseInt(e.getAttribute("font-size")); String text = e.getTextContent(); this.setID(id); this.setWhiteboardPoint(new WhiteboardPoint(x, y)); this.setFontName(fontFamily); this.setFontSize(fontSize); this.setText(text); this.setColor(Color.decode(fill).getRGB()); } } catch (ParserConfigurationException ex) { if (logger.isDebugEnabled()) logger.debug("Problem WhiteboardObject : " + xml); } catch (IOException ex) { if (logger.isDebugEnabled()) logger.debug("Problem WhiteboardObject : " + xml); } catch (Exception ex) { if (logger.isDebugEnabled()) logger.debug("Problem WhiteboardObject : " + xml); } }
/** * The dependent service is available and the bundle will start. * * @param dependentService the UIService this activator is waiting. */ @Override public void start(Object dependentService) { if (logger.isDebugEnabled()) logger.debug("Update checker [STARTED]"); ConfigurationService cfg = getConfiguration(); if (OSUtils.IS_WINDOWS) { updateService = new Update(); bundleContext.registerService(UpdateService.class.getName(), updateService, null); // Register the "Check for Updates" menu item if // the "Check for Updates" property isn't disabled. if (!cfg.getBoolean(CHECK_FOR_UPDATES_MENU_DISABLED_PROP, false)) { // Register the "Check for Updates" menu item. CheckForUpdatesMenuItemComponent checkForUpdatesMenuItemComponent = new CheckForUpdatesMenuItemComponent(Container.CONTAINER_HELP_MENU); Hashtable<String, String> toolsMenuFilter = new Hashtable<String, String>(); toolsMenuFilter.put(Container.CONTAINER_ID, Container.CONTAINER_HELP_MENU.getID()); bundleContext.registerService( PluginComponent.class.getName(), checkForUpdatesMenuItemComponent, toolsMenuFilter); } // Check for software update upon startup if enabled. if (cfg.getBoolean(UPDATE_ENABLED, true)) updateService.checkForUpdates(false); } if (cfg.getBoolean(CHECK_FOR_UPDATES_DAILY_ENABLED_PROP, false)) { logger.info("Scheduled update checking enabled"); // Schedule a "check for updates" task that will run once a day int hoursToWait = calcHoursToWait(); Runnable updateRunnable = new Runnable() { public void run() { logger.debug("Performing scheduled update check"); getUpdateService().checkForUpdates(false); } }; mUpdateExecutor = Executors.newSingleThreadScheduledExecutor(); mUpdateExecutor.scheduleAtFixedRate( updateRunnable, hoursToWait, 24 * 60 * 60, TimeUnit.SECONDS); } if (logger.isDebugEnabled()) logger.debug("Update checker [REGISTERED]"); }
/** * Subscribes this provider as interested in receiving notifications for new mail messages from * Google mail services such as Gmail or Google Apps. */ private void subscribeForGmailNotifications() { // first check support for the notification service String accountIDService = jabberProvider.getAccountID().getService(); boolean notificationsAreSupported = jabberProvider.isFeatureSupported(accountIDService, NewMailNotificationIQ.NAMESPACE); if (!notificationsAreSupported) { if (logger.isDebugEnabled()) logger.debug( accountIDService + " does not seem to provide a Gmail notification " + " service so we won't be trying to subscribe for it"); return; } if (logger.isDebugEnabled()) logger.debug( accountIDService + " seems to provide a Gmail notification " + " service so we will try to subscribe for it"); ProviderManager providerManager = ProviderManager.getInstance(); providerManager.addIQProvider( MailboxIQ.ELEMENT_NAME, MailboxIQ.NAMESPACE, new MailboxIQProvider()); providerManager.addIQProvider( NewMailNotificationIQ.ELEMENT_NAME, NewMailNotificationIQ.NAMESPACE, new NewMailNotificationProvider()); Connection connection = jabberProvider.getConnection(); connection.addPacketListener(new MailboxIQListener(), new PacketTypeFilter(MailboxIQ.class)); connection.addPacketListener( new NewMailNotificationListener(), new PacketTypeFilter(NewMailNotificationIQ.class)); if (opSetPersPresence.getCurrentStatusMessage().equals(JabberStatusEnum.OFFLINE)) return; // create a query with -1 values for newer-than-tid and // newer-than-time attributes MailboxQueryIQ mailboxQuery = new MailboxQueryIQ(); if (logger.isTraceEnabled()) logger.trace( "sending mailNotification for acc: " + jabberProvider.getAccountID().getAccountUniqueID()); jabberProvider.getConnection().sendPacket(mailboxQuery); }
/** * Tries to obtain a mapped/public address for the specified port (possibly by executing a STUN * query). * * @param dst the destination that we'd like to use this address with. * @param port the port whose mapping we are interested in. * @return a public address corresponding to the specified port or null if all attempts to * retrieve such an address have failed. * @throws IOException if an error occurs while stun4j is using sockets. * @throws BindException if the port is already in use. */ public InetSocketAddress getPublicAddressFor(InetAddress dst, int port) throws IOException, BindException { if (!useStun || (dst instanceof Inet6Address)) { logger.debug( "Stun is disabled for destination " + dst + ", skipping mapped address recovery (useStun=" + useStun + ", IPv6@=" + (dst instanceof Inet6Address) + ")."); // we'll still try to bind though so that we could notify the caller // if the port has been taken already. DatagramSocket bindTestSocket = new DatagramSocket(port); bindTestSocket.close(); // if we're here then the port was free. return new InetSocketAddress(getLocalHost(dst), port); } StunAddress mappedAddress = queryStunServer(port); InetSocketAddress result = null; if (mappedAddress != null) result = mappedAddress.getSocketAddress(); else { // Apparently STUN failed. Let's try to temporarily disble it // and use algorithms in getLocalHost(). ... We should probably // eveng think about completely disabling stun, and not only // temporarily. // Bug report - John J. Barton - IBM InetAddress localHost = getLocalHost(dst); result = new InetSocketAddress(localHost, port); } if (logger.isDebugEnabled()) logger.debug("Returning mapping for port:" + port + " as follows: " + result); return result; }
/** * Stop the bundle. Nothing to stop for now. * * @param bundleContext <tt>BundleContext</tt> provided by OSGi framework * @throws Exception if something goes wrong during stop */ public void stop(BundleContext bundleContext) throws Exception { if (logger.isDebugEnabled()) logger.debug("Update checker [STOPPED]"); if (mUpdateExecutor != null) { mUpdateExecutor.shutdown(); mUpdateExecutor = null; } }
/** * Handles {@link EventFactory#FOCUS_JOINED_ROOM_TOPIC} and {@link * EventFactory#CONFERENCE_ROOM_TOPIC}. * * <p>{@inheritDoc} */ @Override public void handleEvent(Event event) { String topic = event.getTopic(); if (!topic.equals(EventFactory.FOCUS_JOINED_ROOM_TOPIC) && !topic.equals(EventFactory.CONFERENCE_ROOM_TOPIC)) { logger.error("Unexpected event topic: " + topic); return; } String roomJid = (String) event.getProperty(EventFactory.ROOM_JID_KEY); JitsiMeetConference conference = focusManager.getConference(roomJid); if (conference == null) { logger.error("Conference is null"); return; } ChatRoom chatRoom = conference.getChatRoom(); if (chatRoom == null) { logger.error("Chat room is null"); return; } JitsiMeetServices meetServices = focusManager.getJitsiMeetServices(); ComponentVersionsExtension versionsExtension = new ComponentVersionsExtension(); // XMPP Version xmppServerVersion = meetServices.getXMPPServerVersion(); if (xmppServerVersion != null) { versionsExtension.addComponentVersion( ComponentVersionsExtension.COMPONENT_XMPP_SERVER, xmppServerVersion.getNameVersionOsString()); } // Conference focus org.jitsi.service.version.Version jicofoVersion = versionService.getCurrentVersion(); versionsExtension.addComponentVersion( ComponentVersionsExtension.COMPONENT_FOCUS, jicofoVersion.getApplicationName() + "(" + jicofoVersion.toString() + "," + System.getProperty("os.name") + ")"); // Videobridge // It is not be reported for FOCUS_JOINED_ROOM_TOPIC String bridgeJid = (String) event.getProperty(EventFactory.BRIDGE_JID_KEY); Version jvbVersion = bridgeJid == null ? null : meetServices.getBridgeVersion(bridgeJid); if (jvbVersion != null) { versionsExtension.addComponentVersion( ComponentVersionsExtension.COMPONENT_VIDEOBRIDGE, jvbVersion.getNameVersionOsString()); } meetTools.sendPresenceExtension(chatRoom, versionsExtension); if (logger.isDebugEnabled()) logger.debug("Sending versions: " + versionsExtension.toXML()); }
/** * Return true/false if logging is enabled at a given level. * * @param logLevel the level that we'd like to check loggability for. * @return always <tt>true</tt> regardless of <tt>logLevel</tt>'s value. */ public boolean isLoggingEnabled(int logLevel) { // always enable trace messages so we can receive packets // and log them to packet logging service if (logLevel == TRACE_DEBUG) return logger.isDebugEnabled(); if (logLevel == TRACE_MESSAGES) // same as TRACE_INFO return true; if (logLevel == TRACE_NONE) return false; return true; }
/** * Ends the call with for this <tt>CallPeer</tt>. Depending on the state of the peer the method * would send a CANCEL, BYE, or BUSY_HERE message and set the new state to DISCONNECTED. * * @param failed indicates if the hangup is following to a call failure or simply a disconnect * @param reasonText the text, if any, to be set on the <tt>ReasonPacketExtension</tt> as the * value of its * @param reasonOtherExtension the <tt>PacketExtension</tt>, if any, to be set on the * <tt>ReasonPacketExtension</tt> as the value of its <tt>otherExtension</tt> property */ public void hangup(boolean failed, String reasonText, PacketExtension reasonOtherExtension) { // do nothing if the call is already ended if (CallPeerState.DISCONNECTED.equals(getState()) || CallPeerState.FAILED.equals(getState())) { if (logger.isDebugEnabled()) logger.debug("Ignoring a request to hangup a call peer " + "that is already DISCONNECTED"); return; } CallPeerState prevPeerState = getState(); getMediaHandler().getTransportManager().close(); if (failed) setState(CallPeerState.FAILED, reasonText); else setState(CallPeerState.DISCONNECTED, reasonText); SessionIQ responseIQ = null; if (prevPeerState.equals(CallPeerState.CONNECTED) || CallPeerState.isOnHold(prevPeerState)) { responseIQ = GTalkPacketFactory.createBye(getProtocolProvider().getOurJID(), peerJID, getSID()); responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID()); } else if (CallPeerState.CONNECTING.equals(prevPeerState) || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState) || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState)) { responseIQ = GTalkPacketFactory.createCancel(getProtocolProvider().getOurJID(), peerJID, getSID()); responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID()); } else if (prevPeerState.equals(CallPeerState.INCOMING_CALL)) { responseIQ = GTalkPacketFactory.createBusy(getProtocolProvider().getOurJID(), peerJID, getSID()); responseIQ.setInitiator(isInitiator() ? getAddress() : getProtocolProvider().getOurJID()); } else if (prevPeerState.equals(CallPeerState.BUSY) || prevPeerState.equals(CallPeerState.FAILED)) { // For FAILED and BUSY we only need to update CALL_STATUS // as everything else has been done already. } else { logger.info("Could not determine call peer state!"); } if (responseIQ != null) { if (reasonOtherExtension != null) { ReasonPacketExtension reason = (ReasonPacketExtension) responseIQ.getExtension( ReasonPacketExtension.ELEMENT_NAME, ReasonPacketExtension.NAMESPACE); if (reason == null) { if (reasonOtherExtension instanceof ReasonPacketExtension) { responseIQ.setReason((ReasonPacketExtension) reasonOtherExtension); } } else reason.setOtherExtension(reasonOtherExtension); } getProtocolProvider().getConnection().sendPacket(responseIQ); } }
/** * Initializes this network address manager service implementation and starts all * processes/threads associated with this address manager, such as a stun firewall/nat detector, * keep alive threads, binding lifetime discovery threads and etc. The method may also be used * after a call to stop() as a reinitialization technique. */ public void start() { // init stun String stunAddressStr = null; int port = -1; stunAddressStr = NetaddrActivator.getConfigurationService().getString(PROP_STUN_SERVER_ADDRESS); String portStr = NetaddrActivator.getConfigurationService().getString(PROP_STUN_SERVER_PORT); this.localHostFinderSocket = initRandomPortSocket(); if (stunAddressStr == null || portStr == null) { useStun = false; // we use the default stun server address only for chosing a public // route and not for stun queries. stunServerAddress = new StunAddress(DEFAULT_STUN_SERVER_ADDRESS, DEFAULT_STUN_SERVER_PORT); logger.info( "Stun server address(" + stunAddressStr + ")/port(" + portStr + ") not set (or invalid). Disabling STUN."); } else { try { port = Integer.valueOf(portStr).intValue(); } catch (NumberFormatException ex) { logger.error(portStr + " is not a valid port number. " + "Defaulting to 3478", ex); port = 3478; } stunServerAddress = new StunAddress(stunAddressStr, port); detector = new SimpleAddressDetector(stunServerAddress); if (logger.isDebugEnabled()) { logger.debug( "Created a STUN Address detector for the following " + "STUN server: " + stunAddressStr + ":" + port); } detector.start(); logger.debug("STUN server detector started;"); // make sure that someone doesn't set invalid stun address and port NetaddrActivator.getConfigurationService() .addVetoableChangeListener(PROP_STUN_SERVER_ADDRESS, this); NetaddrActivator.getConfigurationService() .addVetoableChangeListener(PROP_STUN_SERVER_PORT, this); // now start a thread query to the stun server and only set the // useStun flag to true if it succeeds. launchStunServerTest(); } }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processIOException(IOExceptionEvent event) { try { if (logger.isTraceEnabled()) logger.trace(event); // impossible to dispatch, log here if (logger.isDebugEnabled()) logger.debug("@todo implement processIOException()"); } catch (Throwable exc) { // any exception thrown within our code should be caught here // so that we could log it rather than interrupt stack activity with // it. this.logApplicationException(DialogTerminatedEvent.class, exc); } }
/** * The method queries a Stun server for a binding for the specified port. * * @param port the port to resolve (the stun message gets sent trhough that port) * @return StunAddress the address returned by the stun server or null if an error occurred or no * address was returned * @throws IOException if an error occurs while stun4j is using sockets. * @throws BindException if the port is already in use. */ private StunAddress queryStunServer(int port) throws IOException, BindException { StunAddress mappedAddress = null; if (detector != null && useStun) { mappedAddress = detector.getMappingFor(port); if (logger.isDebugEnabled()) logger.debug( "For port:" + port + "a Stun server returned the " + "following mapping [" + mappedAddress); } return mappedAddress; }
/** * Creates and sends a SUBSCRIBE request to a specific subscription <tt>Address</tt>/Request URI * if it matches a <tt>Subscription</tt> with an id tag of its Event header of a specific value in * the list of subscriptions managed by this instance with an Expires header value of zero in * order to terminate receiving event notifications and removes the specified * <tt>Subscription</tt> from the list of subscriptions managed by this instance. The removed * <tt>Subscription</tt> may receive notifications to process the <tt>Request</tt>s and/or * <tt>Response</tt>s which constitute the signaling session associated with it. If the attempt to * create the SUBSCRIBE request fails, the associated <tt>Subscription</tt> is not removed from * the list of subscriptions managed by this instance. If the specified <tt>Address</tt> does not * identify an existing <tt>Subscription</tt> in the list of subscriptions managed by this * instance, an assertion may optionally be performed or no reaction can be taken. * * @param toAddress a subscription <tt>Address</tt>/Request URI which identifies a * <tt>Subscription</tt> to be removed from the list of subscriptions managed by this instance * @param eventId the id tag placed in the Event header of the <tt>Subscription</tt> to be matched * if there is one or <tt>null</tt> if the <tt>Subscription</tt> should have no id tag in its * Event header * @param assertSubscribed <tt>true</tt> to assert if the specified subscription * <tt>Address</tt>/Request URI does not identify an existing <tt>Subscription</tt> in the * list of subscriptions managed by this instance; <tt>false</tt> to not assert if the * mentioned condition is met * @throws IllegalArgumentException if <tt>assertSubscribed</tt> is <tt>true</tt> and * <tt>toAddress</tt> and <tt>eventId</tt> do not identify an existing <tt>Subscription</tt> * in the list of subscriptions managed by this instance * @throws OperationFailedException if we fail constructing or sending the unSUBSCRIBE request. */ public void unsubscribe(Address toAddress, String eventId, boolean assertSubscribed) throws IllegalArgumentException, OperationFailedException { Subscription subscription = getSubscription(toAddress, eventId); if (subscription == null) if (assertSubscribed) throw new IllegalArgumentException("trying to unregister a not registered contact"); else return; Dialog dialog = subscription.getDialog(); // we stop the subscription if we're subscribed to this contact if (dialog != null) { String callId = dialog.getCallId().getCallId(); ClientTransaction subscribeTransaction; try { subscribeTransaction = createSubscription(subscription, dialog, 0); } catch (OperationFailedException e) { if (logger.isDebugEnabled()) logger.debug("failed to create the unsubscription", e); throw e; } // we are not anymore subscribed to this contact // this ensure that the response of this request will be // handled as an unsubscription response removeSubscription(callId, subscription); try { dialog.sendRequest(subscribeTransaction); } catch (SipException e) { if (logger.isDebugEnabled()) logger.debug("Can't send the request", e); throw new OperationFailedException( "Failed to send the subscription message", OperationFailedException.NETWORK_FAILURE, e); } } }
/** * Sets the icon for the given file. * * @param file the file to set an icon for * @return the byte array containing the thumbnail */ private byte[] getFileThumbnail(File file) { byte[] bytes = null; if (FileUtils.isImage(file.getName())) { try { ImageIcon image = new ImageIcon(file.toURI().toURL()); int width = image.getIconWidth(); int height = image.getIconHeight(); if (width > THUMBNAIL_WIDTH) width = THUMBNAIL_WIDTH; if (height > THUMBNAIL_HEIGHT) height = THUMBNAIL_HEIGHT; bytes = ImageUtils.getScaledInstanceInBytes(image.getImage(), width, height); } catch (MalformedURLException e) { if (logger.isDebugEnabled()) logger.debug("Could not locate image.", e); } } return bytes; }
/** * Gets the max allowed size value in bytes from Configuration service and sets the value to * <tt>imgMaxSize</tt> if succeed. If the configuration property isn't available or the value * can't be parsed correctly the value of <tt>imgMaxSize</tt> isn't changed. */ private void setMaxImgSizeFromConf() { ConfigurationService configService = DirectImageActivator.getConfigService(); if (configService != null) { String confImgSizeStr = (String) configService.getProperty(MAX_IMG_SIZE); try { if (confImgSizeStr != null) { imgMaxSize = Long.parseLong(confImgSizeStr); } else { configService.setProperty(MAX_IMG_SIZE, imgMaxSize); } } catch (NumberFormatException e) { if (logger.isDebugEnabled()) logger.debug( "Failed to parse max image size: " + confImgSizeStr + ". Going for default."); } } }
/** * 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 java.lang.IllegalStateException if the underlying stack is not registered and * initialized. * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an instance of ContactImpl. */ public void sendInstantMessage(Contact to, Message message) throws IllegalStateException, IllegalArgumentException { if (!(to instanceof ContactSipImpl)) throw new IllegalArgumentException("The specified contact is not a Sip contact." + to); assertConnected(); // offline message if (to.getPresenceStatus().equals(sipStatusEnum.getStatus(SipStatusEnum.OFFLINE)) && !offlineMessageSupported) { if (logger.isDebugEnabled()) logger.debug("trying to send a message to an offline contact"); fireMessageDeliveryFailed( message, to, MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED); return; } // create the message Request mes; try { mes = createMessageRequest(to, message); } catch (OperationFailedException ex) { logger.error("Failed to create the message.", ex); fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.INTERNAL_ERROR); return; } try { sendMessageRequest(mes, to, message); } catch (TransactionUnavailableException ex) { logger.error( "Failed to create messageTransaction.\n" + "This is most probably a network connection error.", ex); fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.NETWORK_FAILURE); return; } catch (SipException ex) { logger.error("Failed to send the message.", ex); fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.INTERNAL_ERROR); return; } }
/** * Sends a file transfer request to the given <tt>toContact</tt> by specifying the local and * remote file path and the <tt>fromContact</tt>, sending the file. * * @param toContact the contact that should receive the file * @param file the file to send * @return the transfer object * @throws IllegalStateException if the protocol provider is not registered or connected * @throws IllegalArgumentException if some of the arguments doesn't fit the protocol requirements */ public FileTransfer sendFile(Contact toContact, File file) throws IllegalStateException, IllegalArgumentException { assertConnected(); if (file.length() > getMaximumFileLength()) throw new IllegalArgumentException("File length exceeds the allowed one for this protocol"); // Get the aim connection AimConnection aimConnection = icqProvider.getAimConnection(); // Create an outgoing file transfer instance OutgoingFileTransfer outgoingFileTransfer = aimConnection .getIcbmService() .getRvConnectionManager() .createOutgoingFileTransfer(new Screenname(toContact.getAddress())); String id = String.valueOf(outgoingFileTransfer.getRvSessionInfo().getRvSession().getRvSessionId()); FileTransferImpl outFileTransfer = new FileTransferImpl(outgoingFileTransfer, id, toContact, file, FileTransfer.OUT); // Adding the file to the outgoing file transfer try { outgoingFileTransfer.setSingleFile(new File(file.getPath())); } catch (IOException e) { if (logger.isDebugEnabled()) logger.debug("Error sending file", e); return null; } // Notify all interested listeners that a file transfer has been // created. FileTransferCreatedEvent event = new FileTransferCreatedEvent(outFileTransfer, new Date()); fireFileTransferCreated(event); // Sending the file outgoingFileTransfer.sendRequest(new InvitationMessage("")); outFileTransfer.fireStatusChangeEvent(FileTransferStatusChangeEvent.PREPARING); return outFileTransfer; }
/** * Notify all listeners of the corresponding account detail change event. * * @param source the protocol provider service source * @param eventID the int ID of the event to dispatch * @param oldValue the value that the changed property had before the change occurred. * @param newValue the value that the changed property currently has (after the change has * occurred). */ public void fireServerStoredDetailsChangeEvent( ProtocolProviderService source, int eventID, Object oldValue, Object newValue) { ServerStoredDetailsChangeEvent evt = new ServerStoredDetailsChangeEvent(source, eventID, oldValue, newValue); Collection<ServerStoredDetailsChangeListener> listeners; synchronized (serverStoredDetailsListeners) { listeners = new ArrayList<ServerStoredDetailsChangeListener>(serverStoredDetailsListeners); } if (logger.isDebugEnabled()) logger.debug( "Dispatching a Contact Property Change Event to" + listeners.size() + " listeners. Evt=" + evt); for (ServerStoredDetailsChangeListener listener : listeners) listener.serverStoredDetailsChanged(evt); }
/** * Handles buttons action events. * * @param evt the <tt>ActionEvent</tt> that notified us */ public void actionPerformed(ActionEvent evt) { JButton sourceButton = (JButton) evt.getSource(); if (sourceButton.equals(openFileButton)) { this.openFile(downloadFile); } else if (sourceButton.equals(openFolderButton)) { try { File downloadDir = GuiActivator.getFileAccessService().getDefaultDownloadDirectory(); GuiActivator.getDesktopService().open(downloadDir); } catch (IllegalArgumentException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_DOES_NOT_EXIST")); } catch (NullPointerException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_DOES_NOT_EXIST")); } catch (UnsupportedOperationException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FILE_OPEN_NOT_SUPPORTED")); } catch (SecurityException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_NO_PERMISSION")); } catch (IOException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_NO_APPLICATION")); } catch (Exception e) { if (logger.isDebugEnabled()) logger.debug("Unable to open file.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_FAILED")); } } else if (sourceButton.equals(cancelButton)) { if (fileTransfer != null) fileTransfer.cancel(); } }
/** * Logs exceptions that have occurred in the application while processing events originating from * the stack. * * @param eventClass the class of the jain-sip event that we were handling when the exception was * thrown. * @param exc the exception that we need to log. */ private void logApplicationException(Class<DialogTerminatedEvent> eventClass, Throwable exc) { String message = "An error occurred while processing event of type: " + eventClass.getName(); logger.error(message, exc); if (logger.isDebugEnabled()) logger.debug(message, exc); }
/** * Logs the specified trace with a debuf level. * * @param message the trace to log. */ public void logTrace(String message) { if (logger.isDebugEnabled()) logger.debug(message); }
/** * Handles presence stanzas * * @param presence */ private void handlePresence(Presence presence) { // unavailable is sent when user leaves the room if (!presence.isAvailable()) { return; } String from = presence.getFrom(); JitsiMeetConference conference = getConferenceForMucJid(from); if (conference == null) { if (logger.isDebugEnabled()) { logger.debug("Room not found for JID: " + from); } return; } ChatRoomMemberRole role = conference.getRoleForMucJid(from); if (role != null && role.compareTo(ChatRoomMemberRole.MODERATOR) < 0) { StartMutedPacketExtension ext = (StartMutedPacketExtension) presence.getExtension( StartMutedPacketExtension.ELEMENT_NAME, StartMutedPacketExtension.NAMESPACE); if (ext != null) { boolean[] startMuted = {ext.getAudioMuted(), ext.getVideoMuted()}; conference.setStartMuted(startMuted); } } Participant participant = conference.findParticipantForRoomJid(from); ColibriConference colibriConference = conference.getColibriConference(); if (participant != null && colibriConference != null) { // Check if this conference is valid String conferenceId = colibriConference.getConferenceId(); if (StringUtils.isNullOrEmpty(conferenceId)) { logger.error("Unable to send DisplayNameChanged event" + " - no conference id"); return; } // Check for changes to the display name String oldDisplayName = participant.getDisplayName(); String newDisplayName = null; for (PacketExtension pe : presence.getExtensions()) { if (pe instanceof Nick) { newDisplayName = ((Nick) pe).getName(); break; } } if (!Objects.equals(oldDisplayName, newDisplayName)) { participant.setDisplayName(newDisplayName); EventAdmin eventAdmin = FocusBundleActivator.getEventAdmin(); if (eventAdmin != null) { // Prevent NPE when adding to event hashtable if (newDisplayName == null) { newDisplayName = ""; } eventAdmin.sendEvent( EventFactory.endpointDisplayNameChanged( conferenceId, participant.getEndpointId(), newDisplayName)); } } } }
/** Thread entry point. */ @Override public void run() { int status; long progress; String statusReason = ""; while (true) { try { Thread.sleep(10); status = parseJabberStatus(jabberTransfer.getStatus()); progress = fileTransfer.getTransferedBytes(); if (status == FileTransferStatusChangeEvent.FAILED || status == FileTransferStatusChangeEvent.COMPLETED || status == FileTransferStatusChangeEvent.CANCELED || status == FileTransferStatusChangeEvent.REFUSED) { if (fileTransfer instanceof OutgoingFileTransferJabberImpl) { ((OutgoingFileTransferJabberImpl) fileTransfer).removeThumbnailRequestListener(); } // sometimes a filetransfer can be preparing // and than completed : // transfered in one iteration of current thread // so it won't go through intermediate state - inProgress // make sure this won't happen if (status == FileTransferStatusChangeEvent.COMPLETED && fileTransfer.getStatus() == FileTransferStatusChangeEvent.PREPARING) { fileTransfer.fireStatusChangeEvent( FileTransferStatusChangeEvent.IN_PROGRESS, "Status changed"); fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress); } break; } fileTransfer.fireStatusChangeEvent(status, "Status changed"); fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress); } catch (InterruptedException e) { if (logger.isDebugEnabled()) logger.debug("Unable to sleep thread.", e); } } if (jabberTransfer.getError() != null) { logger.error( "An error occured while transfering file: " + jabberTransfer.getError().getMessage()); } if (jabberTransfer.getException() != null) { logger.error( "An exception occured while transfering file: ", jabberTransfer.getException()); if (jabberTransfer.getException() instanceof XMPPException) { XMPPError error = ((XMPPException) jabberTransfer.getException()).getXMPPError(); if (error != null) if (error.getCode() == 406 || error.getCode() == 403) status = FileTransferStatusChangeEvent.REFUSED; } statusReason = jabberTransfer.getException().getMessage(); } if (initialFileSize > 0 && status == FileTransferStatusChangeEvent.COMPLETED && fileTransfer.getTransferedBytes() < initialFileSize) { status = FileTransferStatusChangeEvent.CANCELED; } fileTransfer.fireStatusChangeEvent(status, statusReason); fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress); }
/** * Adds all contacts contained in the given <tt>MetaContactGroup</tt> matching the current filter * and not contained in the contact list. * * @param metaGroup the <tt>MetaContactGroup</tt>, which matching contacts to add * @param query the <tt>MetaContactQuery</tt> that notifies interested listeners of the results of * this matching * @param resultCount the initial result count we would insert directly to the contact list * without firing events */ private void addMatching(MetaContactGroup metaGroup, MetaContactQuery query, int resultCount) { Iterator<MetaContact> childContacts = metaGroup.getChildContacts(); while (childContacts.hasNext() && !query.isCanceled()) { MetaContact metaContact = childContacts.next(); if (isMatching(metaContact)) { resultCount++; if (resultCount <= INITIAL_CONTACT_COUNT) { UIGroup uiGroup = null; if (!MetaContactListSource.isRootGroup(metaGroup)) { synchronized (metaGroup) { uiGroup = MetaContactListSource.getUIGroup(metaGroup); if (uiGroup == null) uiGroup = MetaContactListSource.createUIGroup(metaGroup); } } if (logger.isDebugEnabled()) logger.debug("Presence filter contact added: " + metaContact.getDisplayName()); UIContactImpl newUIContact; synchronized (metaContact) { newUIContact = MetaContactListSource.getUIContact(metaContact); if (newUIContact == null) { newUIContact = MetaContactListSource.createUIContact(metaContact); } } GuiActivator.getContactList().addContact(newUIContact, uiGroup, true, true); query.setInitialResultCount(resultCount); } else { query.fireQueryEvent(metaContact); } } } // If in the meantime the filtering has been stopped we return here. if (query.isCanceled()) return; Iterator<MetaContactGroup> subgroups = metaGroup.getSubgroups(); while (subgroups.hasNext() && !query.isCanceled()) { MetaContactGroup subgroup = subgroups.next(); if (isMatching(subgroup)) { UIGroup uiGroup; synchronized (subgroup) { uiGroup = MetaContactListSource.getUIGroup(subgroup); if (uiGroup == null) uiGroup = MetaContactListSource.createUIGroup(subgroup); } GuiActivator.getContactList().addGroup(uiGroup, true); addMatching(subgroup, query, resultCount); } } }
/** * Creates an html description of the specified mailbox. * * @param mailboxIQ the mailboxIQ that we are to describe. * @return an html description of <tt>mailboxIQ</tt> */ private String createMailboxDescription(MailboxIQ mailboxIQ) { int threadCount = mailboxIQ.getThreadCount(); String resourceHeaderKey = threadCount > 1 ? "service.gui.NEW_GMAIL_MANY_HEADER" : "service.gui.NEW_GMAIL_HEADER"; String resourceFooterKey = threadCount > 1 ? "service.gui.NEW_GMAIL_MANY_FOOTER" : "service.gui.NEW_GMAIL_FOOTER"; // FIXME Escape HTML! String newMailHeader = JabberActivator.getResources() .getI18NString( resourceHeaderKey, new String[] { jabberProvider.getAccountID().getService(), // {0} - service name mailboxIQ.getUrl(), // {1} - inbox URI Integer.toString(threadCount) // {2} - thread count }); StringBuilder message = new StringBuilder(newMailHeader); // we now start an html table for the threads. message.append("<table width=100% cellpadding=2 cellspacing=0 "); message.append("border=0 bgcolor=#e8eef7>"); Iterator<MailThreadInfo> threads = mailboxIQ.threads(); String maxThreadsStr = (String) JabberActivator.getConfigurationService() .getProperty(PNAME_MAX_GMAIL_THREADS_PER_NOTIFICATION); int maxThreads = 5; try { if (maxThreadsStr != null) maxThreads = Integer.parseInt(maxThreadsStr); } catch (NumberFormatException e) { if (logger.isDebugEnabled()) logger.debug("Failed to parse max threads count: " + maxThreads + ". Going for default."); } // print a maximum of MAX_THREADS for (int i = 0; i < maxThreads && threads.hasNext(); i++) { message.append(threads.next().createHtmlDescription()); } message.append("</table><br/>"); if (threadCount > maxThreads) { String messageFooter = JabberActivator.getResources() .getI18NString( resourceFooterKey, new String[] { mailboxIQ.getUrl(), // {0} - inbox URI Integer.toString(threadCount - maxThreads) // {1} - thread count }); message.append(messageFooter); } return message.toString(); }
/** * Starts the execution of the neomedia bundle in the specified context. * * @param bundleContext the context in which the neomedia bundle is to start executing * @throws Exception if an error occurs while starting the execution of the neomedia bundle in the * specified context */ public void start(BundleContext bundleContext) throws Exception { if (logger.isDebugEnabled()) logger.debug("Started."); NeomediaActivator.bundleContext = bundleContext; // MediaService mediaServiceImpl = (MediaServiceImpl) LibJitsi.getMediaService(); bundleContext.registerService(MediaService.class.getName(), mediaServiceImpl, null); if (logger.isDebugEnabled()) logger.debug("Media Service ... [REGISTERED]"); // mediaConfiguration = new MediaConfigurationImpl(); // bundleContext.registerService( // MediaConfigurationService.class.getStatus(), // getMediaConfiguration(), // null); if (logger.isDebugEnabled()) logger.debug("Media Configuration ... [REGISTERED]"); ConfigurationService cfg = NeomediaActivator.getConfigurationService(); Dictionary<String, String> mediaProps = new Hashtable<String, String>(); mediaProps.put(ConfigurationForm.FORM_TYPE, ConfigurationForm.GENERAL_TYPE); // If the audio configuration form is disabled don't register it. // if ((cfg == null) || !cfg.getBoolean(AUDIO_CONFIG_DISABLED_PROP, false)) // { // audioConfigurationForm // = new LazyConfigurationForm( // AudioConfigurationPanel.class.getStatus(), // getClass().getClassLoader(), // "plugin.mediaconfig.AUDIO_ICON", // "impl.neomedia.configform.AUDIO", // 3); // // bundleContext.registerService( // ConfigurationForm.class.getStatus(), // audioConfigurationForm, // mediaProps); // // if (deviceConfigurationPropertyChangeListener == null) // { // // Initializes and registers the changed device configuration // // event ot the notification service. // getNotificationService(); // // deviceConfigurationPropertyChangeListener // = new AudioDeviceConfigurationListener(); // mediaServiceImpl // .getDeviceConfiguration() // .addPropertyChangeListener( // deviceConfigurationPropertyChangeListener); // } // } // If the video configuration form is disabled don't register it. // if ((cfg == null) || !cfg.getBoolean(VIDEO_CONFIG_DISABLED_PROP, false)) // { // bundleContext.registerService( // ConfigurationForm.class.getStatus(), // new LazyConfigurationForm( // VideoConfigurationPanel.class.getStatus(), // getClass().getClassLoader(), // "plugin.mediaconfig.VIDEO_ICON", // "impl.neomedia.configform.VIDEO", // 4), // mediaProps); // } // H.264 // If the H.264 configuration form is disabled don't register it. // if ((cfg == null) || !cfg.getBoolean(H264_CONFIG_DISABLED_PROP, false)) // { // Dictionary<String, String> h264Props // = new Hashtable<String, String>(); // // h264Props.put( // ConfigurationForm.FORM_TYPE, // ConfigurationForm.ADVANCED_TYPE); // bundleContext.registerService( // ConfigurationForm.class.getStatus(), // new LazyConfigurationForm( // ConfigurationPanel.class.getStatus(), // getClass().getClassLoader(), // "plugin.mediaconfig.VIDEO_ICON", // "impl.neomedia.configform.H264", // -1, // true), // h264Props); // } // ZRTP // If the ZRTP configuration form is disabled don't register it. // if ((cfg == null) || !cfg.getBoolean(ZRTP_CONFIG_DISABLED_PROP, false)) // { // Dictionary<String, String> securityProps // = new Hashtable<String, String>(); // // securityProps.put( ConfigurationForm.FORM_TYPE, // ConfigurationForm.SECURITY_TYPE); // bundleContext.registerService( // ConfigurationForm.class.getStatus(), // new LazyConfigurationForm( // SecurityConfigForm.class.getStatus(), // getClass().getClassLoader(), // "impl.media.security.zrtp.CONF_ICON", // "impl.media.security.zrtp.TITLE", // 0), // securityProps); // } // we use the nist-sdp stack to make parse sdp and we need to set the // following property to make sure that it would accept java generated // IPv6 addresses that contain address scope zones. System.setProperty("gov.nist.core.STRIP_ADDR_SCOPES", "true"); // AudioNotifierService AudioNotifierService audioNotifierService = LibJitsi.getAudioNotifierService(); audioNotifierService.setMute( (cfg == null) || !cfg.getBoolean("net.java.sip.communicator" + ".impl.sound.isSoundEnabled", true)); bundleContext.registerService(AudioNotifierService.class.getName(), audioNotifierService, null); if (logger.isInfoEnabled()) logger.info("Audio Notifier Service ...[REGISTERED]"); // Call Recording // If the call recording configuration form is disabled don't continue. // if ((cfg == null) // || !cfg.getBoolean(CALL_RECORDING_CONFIG_DISABLED_PROP, false)) // { // Dictionary<String, String> callRecordingProps // = new Hashtable<String, String>(); // // callRecordingProps.put( // ConfigurationForm.FORM_TYPE, // ConfigurationForm.ADVANCED_TYPE); // bundleContext.registerService( // ConfigurationForm.class.getStatus(), // new LazyConfigurationForm( // CallRecordingConfigForm.class.getStatus(), // getClass().getClassLoader(), // null, // "plugin.callrecordingconfig.CALL_RECORDING_CONFIG", // 1100, // true), // callRecordingProps); // } }
/** * Implements {@link MethodProcessor#processResponse(ResponseEvent)}. Handles only responses to * SUBSCRIBE requests because they are the only requests concerning event package subscribers (and * the only requests sent by them, for that matter) and if the processing of a given response * requires event package-specific handling, delivers the response to the matching * <tt>Subscription</tt> instance. Examples of such event package-specific handling include * letting the respective <tt>Subscription</tt> handle the success or failure in the establishment * of a subscription. * * @param responseEvent a <tt>ResponseEvent</tt> specifying the SIP <tt>Response</tt> to be * processed * @return <tt>true</tt> if the SIP <tt>Response</tt> specified by <tt>responseEvent</tt> was * processed; otherwise, <tt>false</tt> */ @Override public boolean processResponse(ResponseEvent responseEvent) { Response response = responseEvent.getResponse(); CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME); if (cseqHeader == null) { logger.error("An incoming response did not contain a CSeq header"); return false; } if (!Request.SUBSCRIBE.equals(cseqHeader.getMethod())) return false; ClientTransaction clientTransaction = responseEvent.getClientTransaction(); Request request = clientTransaction.getRequest(); /* * Don't handle responses to requests not coming from this event * package. */ if (request != null) { EventHeader eventHeader = (EventHeader) request.getHeader(EventHeader.NAME); if ((eventHeader == null) || !eventPackage.equalsIgnoreCase(eventHeader.getEventType())) return false; } // Find the subscription. CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME); String callId = callIdHeader.getCallId(); Subscription subscription = getSubscription(callId); // if it's the response to an unsubscribe message, we just ignore it // whatever the response is however if we need to handle a // challenge, we do it ExpiresHeader expHeader = response.getExpires(); int statusCode = response.getStatusCode(); SipProvider sourceProvider = (SipProvider) responseEvent.getSource(); if (((expHeader != null) && (expHeader.getExpires() == 0)) || (subscription == null)) // this handle the unsubscription // case where we removed the contact // from subscribedContacts { boolean processed = false; if ((statusCode == Response.UNAUTHORIZED) || (statusCode == Response.PROXY_AUTHENTICATION_REQUIRED)) { try { processAuthenticationChallenge(clientTransaction, response, sourceProvider); processed = true; } catch (OperationFailedException e) { logger.error("can't handle the challenge", e); } } else if ((statusCode != Response.OK) && (statusCode != Response.ACCEPTED)) processed = true; // any other cases (200/202) will imply a NOTIFY, so we will // handle the end of a subscription there return processed; } if ((statusCode >= Response.OK) && (statusCode < Response.MULTIPLE_CHOICES)) { // OK (200/202) if ((statusCode == Response.OK) || (statusCode == Response.ACCEPTED)) { if (expHeader == null) { // not conform to rfc3265 logger.error("no Expires header in this response"); return false; } SubscriptionRefreshTask refreshTask = new SubscriptionRefreshTask(subscription); subscription.setTimerTask(refreshTask); int refreshDelay = expHeader.getExpires(); // try to keep a margin if the refresh delay allows it if (refreshDelay >= (2 * refreshMargin)) refreshDelay -= refreshMargin; timer.schedule(refreshTask, refreshDelay * 1000); // do it to remember the dialog in case of a polling // subscription (which means no call to finalizeSubscription) subscription.setDialog(clientTransaction.getDialog()); subscription.processSuccessResponse(responseEvent, statusCode); } } else if ((statusCode >= Response.MULTIPLE_CHOICES) && (statusCode < Response.BAD_REQUEST)) { if (logger.isInfoEnabled()) logger.info( "Response to subscribe to " + subscription.getAddress() + ": " + response.getReasonPhrase()); } else if (statusCode >= Response.BAD_REQUEST) { // if the response is a 423 response, just re-send the request // with a valid expires value if (statusCode == Response.INTERVAL_TOO_BRIEF) { MinExpiresHeader min = (MinExpiresHeader) response.getHeader(MinExpiresHeader.NAME); if (min == null) { logger.error("no minimal expires value in this 423 " + "response"); return false; } ExpiresHeader exp = request.getExpires(); try { exp.setExpires(min.getExpires()); } catch (InvalidArgumentException e) { logger.error("can't set the new expires value", e); return false; } ClientTransaction transac = null; try { transac = protocolProvider.getDefaultJainSipProvider().getNewClientTransaction(request); } catch (TransactionUnavailableException e) { logger.error("can't create the client transaction", e); return false; } try { transac.sendRequest(); } catch (SipException e) { logger.error("can't send the new request", e); return false; } return true; // UNAUTHORIZED (401/407) } else if ((statusCode == Response.UNAUTHORIZED) || (statusCode == Response.PROXY_AUTHENTICATION_REQUIRED)) { try { processAuthenticationChallenge(clientTransaction, response, sourceProvider); } catch (OperationFailedException e) { logger.error("can't handle the challenge", e); removeSubscription(callId, subscription); subscription.processFailureResponse(responseEvent, statusCode); } // 408 480 486 600 603 : non definitive reject // others: definitive reject (or not implemented) } else { if (logger.isDebugEnabled()) logger.debug("error received from the network:\n" + response); removeSubscription(callId, subscription); subscription.processFailureResponse(responseEvent, statusCode); } } return true; }
/** * Log a message into the log file. * * @param message message to log into the log file. */ public void logDebug(String message) { if (logger.isDebugEnabled()) logger.debug("Debug output from the JAIN-SIP stack: " + message); }
/** * Find the <tt>ProtocolProviderServiceSipImpl</tt> (one of our "candidate recipient" listeners) * which this <tt>request</tt> should be dispatched to. The strategy is to look first at the * request URI, and then at the To field to find a matching candidate for dispatching. Note that * this method takes a <tt>Request</tt> as param, and not a <tt>ServerTransaction</tt>, because * sometimes <tt>RequestEvent</tt>s have no associated <tt>ServerTransaction</tt>. * * @param request the <tt>Request</tt> to find a recipient for. * @return a suitable <tt>ProtocolProviderServiceSipImpl</tt>. */ private ProtocolProviderServiceSipImpl findTargetFor(Request request) { if (request == null) { logger.error("request shouldn't be null."); return null; } List<ProtocolProviderServiceSipImpl> currentListenersCopy = new ArrayList<ProtocolProviderServiceSipImpl>(this.getSipListeners()); // Let's first narrow down candidate choice by comparing // addresses and ports (no point in delivering to a provider with a // non matching IP address since they will reject it anyway). filterByAddress(currentListenersCopy, request); if (currentListenersCopy.size() == 0) { logger.error("no listeners"); return null; } URI requestURI = request.getRequestURI(); if (requestURI.isSipURI()) { String requestUser = ((SipURI) requestURI).getUser(); List<ProtocolProviderServiceSipImpl> candidates = new ArrayList<ProtocolProviderServiceSipImpl>(); // check if the Request-URI username is // one of ours usernames for (ProtocolProviderServiceSipImpl listener : currentListenersCopy) { String ourUserID = listener.getAccountID().getUserID(); // logger.trace(ourUserID + " *** " + requestUser); if (ourUserID.equals(requestUser)) { if (logger.isTraceEnabled()) logger.trace("suitable candidate found: " + listener.getAccountID()); candidates.add(listener); } } // the perfect match // every other case is approximation if (candidates.size() == 1) { ProtocolProviderServiceSipImpl perfectMatch = candidates.get(0); if (logger.isTraceEnabled()) logger.trace("Will dispatch to \"" + perfectMatch.getAccountID() + "\""); return perfectMatch; } // more than one account match if (candidates.size() > 1) { // check if a custom param exists in the contact // address (set for registrar accounts) for (ProtocolProviderServiceSipImpl candidate : candidates) { String hostValue = ((SipURI) requestURI).getParameter(SipStackSharing.CONTACT_ADDRESS_CUSTOM_PARAM_NAME); if (hostValue == null) continue; if (hostValue.equals(candidate.getContactAddressCustomParamValue())) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "\" the custom param was set"); return candidate; } } // Past this point, our guess is not reliable. We try to find // the "least worst" match based on parameters like the To field // check if the To header field host part // matches any of our SIP hosts for (ProtocolProviderServiceSipImpl candidate : candidates) { URI fromURI = ((FromHeader) request.getHeader(FromHeader.NAME)).getAddress().getURI(); if (fromURI.isSipURI() == false) continue; SipURI ourURI = (SipURI) candidate.getOurSipAddress((SipURI) fromURI).getURI(); String ourHost = ourURI.getHost(); URI toURI = ((ToHeader) request.getHeader(ToHeader.NAME)).getAddress().getURI(); if (toURI.isSipURI() == false) continue; String toHost = ((SipURI) toURI).getHost(); // logger.trace(toHost + "***" + ourHost); if (toHost.equals(ourHost)) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "host in the To: is the same as in our AOR"); return candidate; } } // fallback on the first candidate ProtocolProviderServiceSipImpl target = candidates.iterator().next(); logger.info( "Will randomly dispatch to \"" + target.getAccountID() + "\" because there is ambiguity on the username from" + " the Request-URI"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } // fallback on any account ProtocolProviderServiceSipImpl target = currentListenersCopy.iterator().next(); if (logger.isDebugEnabled()) logger.debug( "Will randomly dispatch to \"" + target.getAccountID() + "\" because the username in the Request-URI " + "is unknown or empty"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } else { logger.error("Request-URI is not a SIP URI, dropping"); } return null; }
/** * Attach JAIN-SIP <tt>SipProvider</tt> and <tt>ListeningPoint</tt> to the stack either for clear * communications or TLS. Clear UDP and TCP <tt>ListeningPoint</tt>s are not handled separately as * the former is a fallback for the latter (depending on the size of the data transmitted). Both * <tt>ListeningPoint</tt>s must be bound to the same address and port in order for the related * <tt>SipProvider</tt> to be created. If a UDP or TCP <tt>ListeningPoint</tt> cannot bind, retry * for both on another port. * * @param preferredPort which port to try first to bind. * @param retries how many times should we try to find a free port to bind * @param secure whether to create the TLS SipProvider. or the clear UDP/TCP one. * @throws TransportNotSupportedException in case we try to create a provider for a transport not * currently supported by jain-sip * @throws InvalidArgumentException if we try binding to an illegal port (which we won't) * @throws ObjectInUseException if another <tt>SipProvider</tt> is already associated with this * <tt>ListeningPoint</tt>. * @throws TransportAlreadySupportedException if there is already a ListeningPoint associated to * this <tt>SipProvider</tt> with the same transport of the <tt>ListeningPoint</tt>. * @throws TooManyListenersException if we try to add a new <tt>SipListener</tt> with a * <tt>SipProvider</tt> when one was already registered. */ private void createProvider(int preferredPort, int retries, boolean secure) throws TransportNotSupportedException, InvalidArgumentException, ObjectInUseException, TransportAlreadySupportedException, TooManyListenersException { String context = (secure ? "TLS: " : "clear UDP/TCP: "); if (retries < 0) { // very unlikely to happen with the default 50 retries logger.error(context + "couldn't find free ports to listen on."); return; } ListeningPoint tlsLP = null; ListeningPoint udpLP = null; ListeningPoint tcpLP = null; try { if (secure) { tlsLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.TLS); if (logger.isTraceEnabled()) logger.trace("TLS secure ListeningPoint has been created."); this.secureJainSipProvider = this.stack.createSipProvider(tlsLP); this.secureJainSipProvider.addSipListener(this); } else { udpLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.UDP); tcpLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.TCP); if (logger.isTraceEnabled()) logger.trace("UDP and TCP clear ListeningPoints have " + "been created."); this.clearJainSipProvider = this.stack.createSipProvider(udpLP); this.clearJainSipProvider.addListeningPoint(tcpLP); this.clearJainSipProvider.addSipListener(this); } if (logger.isTraceEnabled()) logger.trace(context + "SipProvider has been created."); } catch (InvalidArgumentException ex) { // makes sure we didn't leave an open listener // as both UDP and TCP listener have to bind to the same port if (tlsLP != null) this.stack.deleteListeningPoint(tlsLP); if (udpLP != null) this.stack.deleteListeningPoint(udpLP); if (tcpLP != null) this.stack.deleteListeningPoint(tcpLP); // FIXME: "Address already in use" is not working // as ex.getMessage() displays in the locale language in SC // (getMessage() is always supposed to be English though) // this should be a temporary workaround // if (ex.getMessage().indexOf("Address already in use") != -1) // another software is probably using the port if (ex.getCause() instanceof java.io.IOException) { if (logger.isDebugEnabled()) logger.debug("Port " + preferredPort + " seems in use for either TCP or UDP."); // tries again on a new random port int currentlyTriedPort = NetworkUtils.getRandomPortNumber(); if (logger.isDebugEnabled()) logger.debug("Retrying bind on port " + currentlyTriedPort); this.createProvider(currentlyTriedPort, retries - 1, secure); } else throw ex; } }
/** * Implements {@link MethodProcessor#processRequest(RequestEvent)}. Handles only NOTIFY requests * because they are the only requests concerning event package subscribers and if the processing * of a given request requires event package-specific handling, delivers the request to the * matching Subscription instance. Examples of such event package-specific handling include * handling the termination of an existing Subscription and processing the bodies of the NOTIFY * requests for active Subscriptions. * * @param requestEvent a <tt>RequestEvent</tt> specifying the SIP <tt>Request</tt> to be processed * @return <tt>true</tt> if the SIP <tt>Request</tt> specified by <tt>requestEvent</tt> was * processed; otherwise, <tt>false</tt> */ @Override public boolean processRequest(RequestEvent requestEvent) { Request request = requestEvent.getRequest(); EventHeader eventHeader = (EventHeader) request.getHeader(EventHeader.NAME); if ((eventHeader == null) || !eventPackage.equalsIgnoreCase(eventHeader.getEventType())) { /* * We are not concerned by this request, perhaps another listener * is. So don't send a 489 / Bad event answer here. */ return false; } if (!Request.NOTIFY.equals(request.getMethod())) return false; if (logger.isDebugEnabled()) logger.debug("notify received"); SubscriptionStateHeader sstateHeader = (SubscriptionStateHeader) request.getHeader(SubscriptionStateHeader.NAME); // notify must contain one (rfc3265) if (sstateHeader == null) { logger.error("no subscription state in this request"); return false; } String sstate = sstateHeader.getState(); ServerTransaction serverTransaction = getOrCreateServerTransaction(requestEvent); // first handle the case of a contact still pending // it's possible if the NOTIFY arrives before the OK CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME); String callId = callIdHeader.getCallId(); Subscription subscription = getSubscription(callId); // see if the notify correspond to an existing subscription if ((subscription == null) && !SubscriptionStateHeader.TERMINATED.equalsIgnoreCase(sstate)) { if (logger.isDebugEnabled()) logger.debug("subscription not found for callId " + callId); // send a 481 response (rfc3625) Response response; try { response = protocolProvider .getMessageFactory() .createResponse(Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST, request); } catch (ParseException e) { logger.error("failed to create the 481 response", e); return false; } try { serverTransaction.sendResponse(response); } catch (SipException e) { logger.error("failed to send the response", e); } catch (InvalidArgumentException e) { // should not happen logger.error("invalid argument provided while trying to send the response", e); } return true; } // if we don't understand the content ContentTypeHeader ctheader = (ContentTypeHeader) request.getHeader(ContentTypeHeader.NAME); if ((ctheader != null) && !ctheader.getContentSubType().equalsIgnoreCase(contentSubType)) { // send a 415 response (rfc3261) Response response; try { response = protocolProvider .getMessageFactory() .createResponse(Response.UNSUPPORTED_MEDIA_TYPE, request); } catch (ParseException e) { logger.error("failed to create the OK response", e); return false; } // we want PIDF AcceptHeader acceptHeader; try { acceptHeader = protocolProvider.getHeaderFactory().createAcceptHeader("application", contentSubType); } catch (ParseException e) { // should not happen logger.error("failed to create the accept header", e); return false; } response.setHeader(acceptHeader); try { serverTransaction.sendResponse(response); } catch (SipException e) { logger.error("failed to send the response", e); } catch (InvalidArgumentException e) { // should not happen logger.error("invalid argument provided while trying" + " to send the response", e); } } // if the presentity doesn't want of us anymore if (SubscriptionStateHeader.TERMINATED.equalsIgnoreCase(sstate)) { // if we requested this end of subscription, subscription == null if (subscription != null) { removeSubscription(callId, subscription); subscription.processTerminatedRequest(requestEvent, sstateHeader.getReasonCode()); } } // send an OK response Response response; try { response = protocolProvider.getMessageFactory().createResponse(Response.OK, request); } catch (ParseException e) { logger.error("failed to create the OK response", e); return false; } try { serverTransaction.sendResponse(response); } catch (SipException e) { logger.error("failed to send the response", e); } catch (InvalidArgumentException e) { // should not happen logger.error("invalid argument provided while trying to send the response", e); } // transform the presence document in new presence status if (subscription != null) subscription.processActiveRequest(requestEvent, request.getRawContent()); return true; }