aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl
diff options
context:
space:
mode:
authorWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2017-03-11 22:15:03 +0100
committerWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2017-03-11 22:15:03 +0100
commit85901329b0794b136b96bf745f4ab1572806fc89 (patch)
treef23da7e97cae727f39d825f0fef8348cffb238e4 /src/net/java/sip/communicator/impl
parent3db2e44f186c59429901b2c899e139ea60117a55 (diff)
parentcf5da997da8820b4050f5b87ee9440a0ede36d1f (diff)
downloadjitsi-master.zip
jitsi-master.tar.gz
jitsi-master.tar.bz2
Merge commit 'cf5da99'HEADmaster
Signed-off-by: Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
Diffstat (limited to 'src/net/java/sip/communicator/impl')
-rw-r--r--src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java56
-rw-r--r--src/net/java/sip/communicator/impl/configuration/ConfigurationActivator.java69
-rw-r--r--src/net/java/sip/communicator/impl/configuration/JdbcConfigService.java29
-rw-r--r--src/net/java/sip/communicator/impl/dns/ConfigurableDnssecResolver.java832
-rw-r--r--src/net/java/sip/communicator/impl/dns/DnsUtilActivator.java2
-rw-r--r--src/net/java/sip/communicator/impl/dns/SecureMessage.java183
-rw-r--r--src/net/java/sip/communicator/impl/dns/SecureResolveMode.java76
-rw-r--r--src/net/java/sip/communicator/impl/dns/UnboundApi.java239
-rw-r--r--src/net/java/sip/communicator/impl/dns/UnboundResolver.java410
-rw-r--r--src/net/java/sip/communicator/impl/dns/UnboundResult.java128
-rw-r--r--src/net/java/sip/communicator/impl/dns/dns.manifest.mf2
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java18
-rw-r--r--src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java13
-rw-r--r--src/net/java/sip/communicator/impl/gui/UIServiceImpl.java83
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/MainFrame.java52
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/UINotification.java8
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/UINotificationGroup.java5
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/UINotificationListener.java7
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/UINotificationManager.java19
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/account/AccountsConfigurationPanel.java552
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java1234
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java8
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java24
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java548
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java348
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java6
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java230
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java276
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java1136
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java2786
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java7
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/ChatSessionChangeListener.java60
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/conference/ChatContactListModel.java466
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatManager.java2776
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/filetransfer/ReceiveFileConversationComponent.java4
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java13
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chatroomslist/ChatRoomTableDialog.java13
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chatroomslist/ServerChatRoomsChoiceDialog.java190
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/AddContactDialog.java109
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactInfoDialog.java131
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java2
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java702
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java5
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java4
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java4
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/menus/MacOSXPreferencesRegistration.java56
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/menus/MacOSXQuitRegistration.java132
-rw-r--r--src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf2
-rw-r--r--src/net/java/sip/communicator/impl/gui/utils/PluginContainer.java632
-rw-r--r--src/net/java/sip/communicator/impl/hid/HIDServiceImpl.java442
-rw-r--r--src/net/java/sip/communicator/impl/ldap/LdapPersonFoundImpl.java6
-rw-r--r--src/net/java/sip/communicator/impl/ldap/LdapSSLSocketFactoryDelegate.java146
-rw-r--r--src/net/java/sip/communicator/impl/muc/BaseChatRoomSourceContact.java278
-rw-r--r--src/net/java/sip/communicator/impl/muc/ChatRoomQuery.java1080
-rw-r--r--src/net/java/sip/communicator/impl/muc/ChatRoomSourceContact.java222
-rw-r--r--src/net/java/sip/communicator/impl/muc/MUCCustomContactActionService.java1686
-rw-r--r--src/net/java/sip/communicator/impl/muc/MUCServiceImpl.java2230
-rw-r--r--src/net/java/sip/communicator/impl/muc/ServerChatRoomQuery.java658
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/AbstractDeviceConfigurationListener.java3
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/AudioDeviceConfigurationListener.java18
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java908
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/EncodingConfigurationTableModel.java540
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/MediaConfigurationImpl.java3
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/codec/video/h264/ConfigurationPanel.java3
-rw-r--r--src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf15
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/Desktop.java1
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/PopupMessageHandlerTrayIconImpl.java1
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/SystemTray.java215
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/TrayIcon.java412
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/jdic/StatusSubMenu.java15
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/jdic/SystrayServiceJdicImpl.java240
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/jdic/TrayMenuFactory.java27
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/osdependent.manifest.mf20
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/SystemTray.java156
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/TrayIcon.java (renamed from src/net/java/sip/communicator/impl/dns/UnboundException.java)52
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicator1.java189
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicatorTray.java78
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicatorTrayIcon.java695
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/Gobject.java90
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/Gtk.java68
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTMouseAdapter.java124
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTSystemTray.java78
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTTrayIcon.java131
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/windows/ImageConverter.java172
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/windows/TaskBarList3.java150
-rw-r--r--src/net/java/sip/communicator/impl/osdependent/windows/User32Ex.java47
-rw-r--r--src/net/java/sip/communicator/impl/packetlogging/PacketLoggingConfigurationImpl.java4
-rw-r--r--src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java64
-rw-r--r--src/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf3
-rw-r--r--src/net/java/sip/communicator/impl/phonenumbers/PhoneNumberI18nServiceImpl.java30
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/ContactDictImpl.java343
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/ContactGroupDictImpl.java588
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/DictAccountID.java70
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java146
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java88
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java46
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java412
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java988
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/ProtocolIconDictImpl.java133
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java200
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java364
-rw-r--r--src/net/java/sip/communicator/impl/protocol/dict/dict.provider.manifest.mf15
-rw-r--r--src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java25
-rw-r--r--src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf1
-rw-r--r--src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java3287
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java44
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java21
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java141
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java4
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java21
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java8
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java13
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java7
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java223
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java10
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java4
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java17
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java1166
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java560
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java10
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java229
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java1054
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java13
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/SmackV3InteroperabilityLayer.java91
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java1922
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java11
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java113
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java10
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java629
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java69
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java131
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java144
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ShutdownIQ.java134
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQ.java (renamed from src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/GracefulShutdownIQ.java)21
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQProvider.java94
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIq.java413
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIqProvider.java112
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriStatusPacketExt.java121
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/RecordingStatus.java126
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/XMPPErrorPE.java93
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java878
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java12
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java21
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java25
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java196
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/ComponentVersionsExtension.java135
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java15
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/VideoMutedExtension.java70
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java9
-rw-r--r--src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java25
-rw-r--r--src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java9
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java12
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java48
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java121
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java26
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java43
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java15
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java10
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java4
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java34
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java72
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java182
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java26
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java5
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java3
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java33
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java226
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java360
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java418
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java6
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java31
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf18
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ContactGroupSSHImpl.java580
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ContactSSH.java370
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ContactSSHImpl.java918
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ContactTimerSSHImpl.java116
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/FileTransferSSHImpl.java96
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/MessageSSHImpl.java63
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java312
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/OperationSetFileTransferSSHImpl.java161
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/OperationSetPersistentPresenceSSHImpl.java980
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ProtocolIconSSHImpl.java159
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSH.java49
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSHImpl.java179
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderServiceSSHImpl.java662
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/Resources.java53
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/SSHAccountID.java45
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/SSHActivator.java145
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/SSHContactInfo.java352
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/SSHFileTransferDaemon.java468
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/SSHReaderDaemon.java211
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/SSHStatusEnum.java138
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/SSHUserInfo.java164
-rw-r--r--src/net/java/sip/communicator/impl/protocol/ssh/ssh.provider.manifest.mf20
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java40
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomInvitationYahooImpl.java90
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomYahooImpl.java581
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java445
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java397
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/FileTransferImpl.java117
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/IncomingFileTransferRequestYahooImpl.java190
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java48
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetAdHocMultiUserChatYahooImpl.java714
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java649
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetFileTransferYahooImpl.java466
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java954
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java153
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolIconYahooImpl.java173
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java172
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java574
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java280
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java1274
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java97
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java42
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java145
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java76
-rw-r--r--src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf22
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/BonjourService.java706
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ClientThread.java499
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ContactGroupZeroconfImpl.java595
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ContactZeroconfImpl.java468
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/MessageZeroconfImpl.java234
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java192
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetPersistentPresenceZeroconfImpl.java852
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetTypingNotificationsZeroconfImpl.java83
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolIconZeroconfImpl.java189
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderFactoryZeroconfImpl.java120
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ProtocolProviderServiceZeroconfImpl.java302
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfAccountID.java96
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfActivator.java132
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/ZeroconfStatusEnum.java136
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java294
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java160
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java183
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java524
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java39
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java405
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java68
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java796
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java139
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java173
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java3048
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java124
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java785
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java57
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java37
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/zeroconf.provider.manifest.mf12
-rw-r--r--src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java2
-rw-r--r--src/net/java/sip/communicator/impl/resources/ResourceManagementActivator.java20
-rw-r--r--src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java20
-rw-r--r--src/net/java/sip/communicator/impl/sysactivity/DBusNetworkManager.java262
-rw-r--r--src/net/java/sip/communicator/impl/sysactivity/NetworkManagerListenerImpl.java324
-rw-r--r--src/net/java/sip/communicator/impl/sysactivity/SysActivityActivator.java198
-rw-r--r--src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java468
-rw-r--r--src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java1274
-rw-r--r--src/net/java/sip/communicator/impl/version/VersionImpl.java2
264 files changed, 23175 insertions, 48359 deletions
diff --git a/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java b/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java
index 7bf29d1..bb888ad 100644
--- a/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/certificate/CertificateServiceImpl.java
@@ -211,49 +211,6 @@ public class CertificateServiceImpl
System.getProperties().remove("javax.net.ssl.trustStorePassword");
}
- /**
- * Appends an index number to the alias of each entry in the KeyStore.
- *
- * The Windows TrustStore might contain multiple entries with the same
- * "Friendly Name", which is directly used as the "Alias" for the KeyStore.
- * As all operations of the KeyStore operate with these non-unique names,
- * PKIX path building could fail and in the end lead to certificate warnings
- * for perfectly valid certificates.
- *
- * @throws Exception when the aliases could not be renamed.
- */
- private static int keyStoreAppendIndex(KeyStore ks) throws Exception
- {
- Field keyStoreSpiField = ks.getClass().getDeclaredField("keyStoreSpi");
- keyStoreSpiField.setAccessible(true);
- KeyStoreSpi keyStoreSpi = (KeyStoreSpi) keyStoreSpiField.get(ks);
-
- if ("sun.security.mscapi.KeyStore$ROOT".equals(keyStoreSpi.getClass()
- .getName()))
- {
- Field entriesField =
- keyStoreSpi.getClass().getEnclosingClass()
- .getDeclaredField("entries");
- entriesField.setAccessible(true);
- Collection<?> entries =
- (Collection<?>) entriesField.get(keyStoreSpi);
-
- int i = 0;
- for (Object entry : entries)
- {
- Field aliasField = entry.getClass().getDeclaredField("alias");
- aliasField.setAccessible(true);
- String alias = (String) aliasField.get(entry);
- aliasField.set(entry,
- alias.concat("_").concat(Integer.toString(i++)));
- }
-
- return i;
- }
-
- return -1;
- }
-
// ------------------------------------------------------------------------
// Client authentication configuration
// ------------------------------------------------------------------------
@@ -678,10 +635,6 @@ public class CertificateServiceImpl
{
ks = KeyStore.getInstance(tsType);
ks.load(null, null);
- int numEntries = keyStoreAppendIndex(ks);
- logger.info(
- "Using Windows-ROOT. Aliases sucessfully renamed on "
- + numEntries + " root certificates.");
}
catch (Exception e)
{
@@ -784,8 +737,7 @@ public class CertificateServiceImpl
propNames.add(propName);
message =
- R.getI18NString("service.gui."
- + "CERT_DIALOG_DESCRIPTION_TXT_NOHOST",
+ R.getI18NString("service.gui.CERT_DIALOG_DESCRIPTION_TXT_NOHOST",
new String[] {
appName
}
@@ -809,8 +761,7 @@ public class CertificateServiceImpl
{
message =
R.getI18NString(
- "service.gui."
- + "CERT_DIALOG_DESCRIPTION_TXT",
+ "service.gui.CERT_DIALOG_DESCRIPTION_TXT",
new String[] {
appName,
identitiesToTest.toString()
@@ -821,8 +772,7 @@ public class CertificateServiceImpl
{
message =
R.getI18NString(
- "service.gui."
- + "CERT_DIALOG_PEER_DESCRIPTION_TXT",
+ "service.gui.CERT_DIALOG_PEER_DESCRIPTION_TXT",
new String[] {
appName,
identitiesToTest.toString()
diff --git a/src/net/java/sip/communicator/impl/configuration/ConfigurationActivator.java b/src/net/java/sip/communicator/impl/configuration/ConfigurationActivator.java
index 1553658..a77b2f5 100644
--- a/src/net/java/sip/communicator/impl/configuration/ConfigurationActivator.java
+++ b/src/net/java/sip/communicator/impl/configuration/ConfigurationActivator.java
@@ -28,6 +28,9 @@ import org.jitsi.util.*;
import org.osgi.framework.*;
import java.io.*;
+import java.nio.file.*;
+import java.nio.file.attribute.*;
+import java.util.*;
/**
*
@@ -64,39 +67,38 @@ public class ConfigurationActivator
if (fas != null)
{
- File useDatabaseConfig;
-
+ File usePropFileConfig;
try
{
- useDatabaseConfig
+ usePropFileConfig
= fas.getPrivatePersistentFile(
- ".usedatabaseconfig",
+ ".usepropfileconfig",
FileCategory.PROFILE);
}
catch (Exception ise)
{
-
// There is somewhat of a chicken-and-egg dependency between
// FileConfigurationServiceImpl and ConfigurationServiceImpl:
// FileConfigurationServiceImpl throws IllegalStateException if
// certain System properties are not set,
// ConfigurationServiceImpl will make sure that these properties
- //are set but it will do that later.
+ // are set but it will do that later.
// A SecurityException is thrown when the destination
// is not writable or we do not have access to that folder
- useDatabaseConfig = null;
+ usePropFileConfig = null;
}
- // BETA: if the marker file exists, use the database configuration
- if ((useDatabaseConfig != null) && useDatabaseConfig.exists())
+ if (usePropFileConfig != null && usePropFileConfig.exists())
{
- logger.info("Using database configuration store.");
- this.cs = new JdbcConfigService(fas);
+ logger.info("Using properties file configuration store.");
+ this.cs = LibJitsi.getConfigurationService();
}
}
if (this.cs == null)
- this.cs = LibJitsi.getConfigurationService();
+ {
+ this.cs = new JdbcConfigService(fas);
+ }
bundleContext.registerService(
ConfigurationService.class.getName(),
@@ -139,17 +141,30 @@ public class ConfigurationActivator
// let's check config file and config folder
File homeFolder
= new File(cs.getScHomeDirLocation(), cs.getScHomeDirName());
- CLibrary libc = (CLibrary) Native.loadLibrary("c", CLibrary.class);
-
- libc.chmod(homeFolder.getAbsolutePath(), 0700);
+ Set<PosixFilePermission> perms =
+ new HashSet<PosixFilePermission>()
+ {{
+ add(PosixFilePermission.OWNER_READ);
+ add(PosixFilePermission.OWNER_WRITE);
+ add(PosixFilePermission.OWNER_EXECUTE);
+ }};
+ Files.setPosixFilePermissions(
+ Paths.get(homeFolder.getAbsolutePath()), perms);
String fileName = cs.getConfigurationFilename();
-
if(fileName != null)
{
File cf = new File(homeFolder, fileName);
if(cf.exists())
- libc.chmod(cf.getAbsolutePath(), 0600);
+ {
+ perms = new HashSet<PosixFilePermission>()
+ {{
+ add(PosixFilePermission.OWNER_READ);
+ add(PosixFilePermission.OWNER_WRITE);
+ }};
+ Files.setPosixFilePermissions(
+ Paths.get(cf.getAbsolutePath()), perms);
+ }
}
}
catch(Throwable t)
@@ -164,24 +179,4 @@ public class ConfigurationActivator
throw (ThreadDeath) t;
}
}
-
- /**
- * The JNA interface to the <tt>c</tt> library and the <tt>chmod</tt>
- * function we use to fix permissions of user files and folders.
- */
- public interface CLibrary
- extends Library
- {
- /**
- * Changes file permissions.
- *
- * @param path the path to the file or folder the permissions of which
- * are to be changed.
- * @param mode the mode operand
- * @return <tt>0</tt> upon successful completion; otherwise,
- * <tt>-1</tt>. If <tt>-1</tt> is returned, no change to the file mode
- * occurs.
- */
- public int chmod(String path, int mode);
- }
}
diff --git a/src/net/java/sip/communicator/impl/configuration/JdbcConfigService.java b/src/net/java/sip/communicator/impl/configuration/JdbcConfigService.java
index 7ba362e..c9c9c3c 100644
--- a/src/net/java/sip/communicator/impl/configuration/JdbcConfigService.java
+++ b/src/net/java/sip/communicator/impl/configuration/JdbcConfigService.java
@@ -595,6 +595,35 @@ public final class JdbcConfigService
* (non-Javadoc)
*
* @see
+ * org.jitsi.service.configuration.ConfigurationService#getDouble(java.lang
+ * .String, double)
+ */
+ @Override
+ public double getDouble(String propertyName, double defaultValue)
+ {
+ Object value = this.getProperty(propertyName);
+ if (value == null || "".equals(value.toString()))
+ {
+ return defaultValue;
+ }
+
+ try
+ {
+ return Double.parseDouble(value.toString());
+ }
+ catch (NumberFormatException ex)
+ {
+ logger.error(String.format(
+ "'%s' for property %s not a double, returning default (%s)",
+ value, propertyName, defaultValue), ex);
+ return defaultValue;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
* org.jitsi.service.configuration.ConfigurationService#getLong(java.lang
* .String, long)
*/
diff --git a/src/net/java/sip/communicator/impl/dns/ConfigurableDnssecResolver.java b/src/net/java/sip/communicator/impl/dns/ConfigurableDnssecResolver.java
index af8d465..1b23af4 100644
--- a/src/net/java/sip/communicator/impl/dns/ConfigurableDnssecResolver.java
+++ b/src/net/java/sip/communicator/impl/dns/ConfigurableDnssecResolver.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,379 +15,457 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.dns;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.service.dns.*;
-import net.java.sip.communicator.service.notification.*;
-import net.java.sip.communicator.util.Logger;
-import net.java.sip.communicator.plugin.desktoputil.*;
-
-import org.jitsi.service.configuration.*;
-import org.jitsi.service.resources.*;
-import org.jitsi.util.*;
-import org.xbill.DNS.*;
-
-/**
- * Resolver that wraps a DNSSEC capable resolver and handles validation
- * failures according to the user's settings.
- *
- * @author Ingo Bauersachs
- */
-public class ConfigurableDnssecResolver
- extends UnboundResolver
-{
- private final static Logger logger
- = Logger.getLogger(ConfigurableDnssecResolver.class);
-
- /**
- * Name of the property that defines the default DNSSEC validation
- * behavior.
- */
- public final static String PNAME_DNSSEC_VALIDATION_MODE
- = "net.java.sip.communicator.util.dns.DNSSEC_VALIDATION_MODE";
-
- /**
- * Default value of {@link #PNAME_DNSSEC_VALIDATION_MODE}
- */
- public final static String PNAME_BASE_DNSSEC_PIN
- = "net.java.sip.communicator.util.dns.pin";
-
- final static String EVENT_TYPE = "DNSSEC_NOTIFICATION";
-
- private ConfigurationService config
- = DnsUtilActivator.getConfigurationService();
- private ResourceManagementService R
- = DnsUtilActivator.getResources();
- private Map<String, Date> lastNotifications
- = new HashMap<String, Date>();
-
- /**
- * Creates a new instance of this class. Tries to use the system's
- * default forwarders.
- */
- public ConfigurableDnssecResolver()
- {
- super();
- reset();
- Lookup.setDefaultResolver(this);
-
- DnsUtilActivator.getNotificationService().
- registerDefaultNotificationForEvent(
- ConfigurableDnssecResolver.EVENT_TYPE,
- NotificationAction.ACTION_POPUP_MESSAGE,
- null, null);
- }
-
- /**
- * Inspects a DNS answer message and handles validation results according to
- * the user's preferences.
- *
- * @throws DnssecRuntimeException when the validation failed and the user
- * did not choose to ignore it.
- */
- @Override
- protected void validateMessage(SecureMessage msg)
- throws DnssecRuntimeException
- {
- //---------------------------------------------------------------------
- // || 1 | 2 | 3 | 4 | 5
- //---------------------------------------------------------------------
- // Sec. | Bog. || Ign. | Sec.Only | Sec.Or.Unsig | Warn.Bog | WarnAll
- //---------------------------------------------------------------------
- //a) 1 | 0 || ok | ok | ok | ok | ok
- //b) 0 | 1 || ok | nok | nok | ask | ask
- //c) 0 | 0 || ok | nok | ok | ok | ask
- //---------------------------------------------------------------------
-
- String fqdn = msg.getQuestion().getName().toString();
- String type = Type.string(msg.getQuestion().getType());
- String propName = createPropNameUnsigned(fqdn, type);
- SecureResolveMode defaultAction = Enum.valueOf(SecureResolveMode.class,
- config.getString(
- PNAME_DNSSEC_VALIDATION_MODE,
- SecureResolveMode.WarnIfBogus.name()
- )
- );
- SecureResolveMode pinned = Enum.valueOf(SecureResolveMode.class,
- config.getString(
- propName,
- defaultAction.name()
- )
- );
-
- //create default entry
- if(pinned == defaultAction)
- config.setProperty(propName, pinned.name());
-
- //check domain policy
-
- //[abc]1, a[2-5]
- if(pinned == SecureResolveMode.IgnoreDnssec || msg.isSecure())
- return;
-
- if(
- //b2, c2
- (pinned == SecureResolveMode.SecureOnly && !msg.isSecure())
- ||
- //b3
- (pinned == SecureResolveMode.SecureOrUnsigned && msg.isBogus())
- )
- {
- String text = getExceptionMessage(msg);
- Date last = lastNotifications.get(text);
- if(last == null
- //wait at least 5 minutes before showing the same info again
- || last.before(new Date(new Date().getTime() - 1000*60*5)))
- {
- DnsUtilActivator.getNotificationService().fireNotification(
- EVENT_TYPE,
- R.getI18NString("util.dns.INSECURE_ANSWER_TITLE"),
- text,
- null);
- lastNotifications.put(text, new Date());
- }
- throw new DnssecRuntimeException(text);
- }
-
- //c3
- if(pinned == SecureResolveMode.SecureOrUnsigned && !msg.isBogus())
- return;
-
- //c4
- if(pinned == SecureResolveMode.WarnIfBogus && !msg.isBogus())
- return;
-
- //b4, b5, c5
- String reason = msg.isBogus()
- ? R.getI18NString("util.dns.DNSSEC_ADVANCED_REASON_BOGUS",
- new String[]{fqdn, msg.getBogusReason()})
- : R.getI18NString("util.dns.DNSSEC_ADVANCED_REASON_UNSIGNED",
- new String[]{type, fqdn});
- DnssecDialog dlg = new DnssecDialog(fqdn, reason);
- dlg.setVisible(true);
- DnssecDialogResult result = dlg.getResult();
- switch(result)
- {
- case Accept:
- break;
- case Deny:
- throw new DnssecRuntimeException(getExceptionMessage(msg));
- case AlwaysAccept:
- if(msg.isBogus())
- config.setProperty(propName,
- SecureResolveMode.IgnoreDnssec.name());
- else
- config.setProperty(propName,
- SecureResolveMode.WarnIfBogus.name());
- break;
- case AlwaysDeny:
- config.setProperty(propName, SecureResolveMode.SecureOnly);
- throw new DnssecRuntimeException(getExceptionMessage(msg));
- }
- }
-
- /**
- * Defines the return code from the DNSSEC verification dialog.
- */
- private enum DnssecDialogResult
- {
- /** The DNS result shall be accepted. */
- Accept,
- /** The result shall be rejected. */
- Deny,
- /** The result shall be accepted permanently. */
- AlwaysAccept,
- /**
- * The result shall be rejected automatically unless it is valid
- * according to DNSSEC.
- */
- AlwaysDeny
- }
-
- /**
- * Dialog to ask and warn the user if he wants to continue to accept an
- * invalid dnssec result.
- */
- private class DnssecDialog extends SIPCommDialog implements ActionListener
- {
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- //UI controls
- private JPanel pnlAdvanced;
- private JPanel pnlStandard;
- private final String domain;
- private final String reason;
- private JButton cmdAck;
- private JButton cmdShowDetails;
-
- //state
- private DnssecDialogResult result = DnssecDialogResult.Deny;
-
- /**
- * Creates a new instance of this class.
- * @param domain The FQDN of the domain that failed.
- * @param reason String describing why the validation failed.
- */
- public DnssecDialog(String domain, String reason)
- {
- super(false);
- setModal(true);
- this.domain = domain;
- this.reason = reason;
- initComponents();
- }
-
- /**
- * Creates the UI controls
- */
- private void initComponents()
- {
- setLayout(new BorderLayout(15, 15));
- setTitle(R.getI18NString("util.dns.INSECURE_ANSWER_TITLE"));
-
- // warning text
- JLabel imgWarning =
- new JLabel(R.getImage("service.gui.icons.WARNING_ICON"));
- imgWarning.setBorder(BorderFactory
- .createEmptyBorder(10, 10, 10, 10));
- add(imgWarning, BorderLayout.WEST);
- JLabel lblWarning = new JLabel(
- R.getI18NString("util.dns.DNSSEC_WARNING", new String[]{
- R.getSettingsString("service.gui.APPLICATION_NAME"),
- domain
- })
- );
- add(lblWarning, BorderLayout.CENTER);
-
- //standard panel (deny option)
- cmdAck = new JButton(R.getI18NString("service.gui.OK"));
- cmdAck.addActionListener(this);
-
- cmdShowDetails = new JButton(
- R.getI18NString("util.dns.DNSSEC_ADVANCED_OPTIONS"));
- cmdShowDetails.addActionListener(this);
-
- pnlStandard = new TransparentPanel(new BorderLayout());
- pnlStandard.setBorder(BorderFactory
- .createEmptyBorder(10, 10, 10, 10));
- pnlStandard.add(cmdShowDetails, BorderLayout.WEST);
- pnlStandard.add(cmdAck, BorderLayout.EAST);
- add(pnlStandard, BorderLayout.SOUTH);
-
- //advanced panel
- pnlAdvanced = new TransparentPanel(new BorderLayout());
- JPanel pnlAdvancedButtons = new TransparentPanel(
- new FlowLayout(FlowLayout.RIGHT));
- pnlAdvancedButtons.setBorder(BorderFactory
- .createEmptyBorder(10, 10, 10, 10));
- pnlAdvanced.add(pnlAdvancedButtons, BorderLayout.EAST);
- for(DnssecDialogResult r : DnssecDialogResult.values())
- {
- JButton cmd = new JButton(R.getI18NString(
- "net.java.sip.communicator.util.dns."
- + "ConfigurableDnssecResolver$DnssecDialogResult."
- + r.name()));
- cmd.setActionCommand(r.name());
- cmd.addActionListener(this);
- pnlAdvancedButtons.add(cmd);
- }
- JLabel lblReason = new JLabel(reason);
- lblReason.setBorder(BorderFactory
- .createEmptyBorder(10, 10, 10, 10));
- pnlAdvanced.add(lblReason, BorderLayout.NORTH);
- }
-
- /**
- * Handles the events coming from the buttons.
- */
- public void actionPerformed(ActionEvent e)
- {
- if(e.getSource() == cmdAck)
- {
- result = DnssecDialogResult.Deny;
- dispose();
- }
- else if(e.getSource() == cmdShowDetails)
- {
- getContentPane().remove(pnlStandard);
- add(pnlAdvanced, BorderLayout.SOUTH);
- pack();
- }
- else
- {
- result = Enum.valueOf(DnssecDialogResult.class,
- e.getActionCommand());
- dispose();
- }
- }
-
- /**
- * Gets the option that user has chosen.
- * @return the option that user has chosen.
- */
- public DnssecDialogResult getResult()
- {
- return result;
- }
- }
-
- private String getExceptionMessage(SecureMessage msg)
- {
- return msg.getBogusReason() == null
- ? R.getI18NString(
- "util.dns.INSECURE_ANSWER_MESSAGE_NO_REASON",
- new String[]{msg.getQuestion().getName().toString()}
- )
- : R.getI18NString(
- "util.dns.INSECURE_ANSWER_MESSAGE_REASON",
- new String[]{msg.getQuestion().getName().toString(),
- //TODO parse bogus reason text and translate
- msg.getBogusReason()}
- );
- }
-
- private String createPropNameUnsigned(String fqdn, String type)
- {
- return PNAME_BASE_DNSSEC_PIN + "." + fqdn.replace(".", "__");
- }
-
- /**
- * Reloads the configuration of forwarders and trust anchors.
- */
- @Override
- public void reset()
- {
- String forwarders = DnsUtilActivator.getConfigurationService()
- .getString(DnsUtilActivator.PNAME_DNSSEC_NAMESERVERS);
- if(!StringUtils.isNullOrEmpty(forwarders, true))
- {
- if(logger.isTraceEnabled())
- {
- logger.trace("Setting DNSSEC forwarders to: "
- + Arrays.toString(forwarders.split(",")));
- }
- super.setForwarders(forwarders.split(","));
- }
-
- for(int i = 1;;i++)
- {
- String anchor = DnsUtilActivator.getResources().getSettingsString(
- "net.java.sip.communicator.util.dns.DS_ROOT." + i);
- if(anchor == null)
- break;
- clearTrustAnchors();
- addTrustAnchor(anchor);
- if(logger.isTraceEnabled())
- logger.trace("Loaded trust anchor " + anchor);
- }
- }
-}
+package net.java.sip.communicator.impl.dns;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.service.dns.*;
+import net.java.sip.communicator.service.notification.*;
+import net.java.sip.communicator.util.Logger;
+import net.java.sip.communicator.plugin.desktoputil.*;
+
+import org.jitsi.dnssec.validator.ValidatingResolver;
+import org.jitsi.service.configuration.*;
+import org.jitsi.service.resources.*;
+import org.jitsi.util.*;
+import org.xbill.DNS.*;
+
+/**
+ * Resolver that wraps a DNSSEC capable resolver and handles validation
+ * failures according to the user's settings.
+ *
+ * @author Ingo Bauersachs
+ */
+public class ConfigurableDnssecResolver
+ extends ValidatingResolver
+ implements CustomResolver
+{
+ private final static Logger logger
+ = Logger.getLogger(ConfigurableDnssecResolver.class);
+
+ /**
+ * Name of the property that defines the default DNSSEC validation
+ * behavior.
+ */
+ public final static String PNAME_DNSSEC_VALIDATION_MODE
+ = "net.java.sip.communicator.util.dns.DNSSEC_VALIDATION_MODE";
+
+ /**
+ * Default value of {@link #PNAME_DNSSEC_VALIDATION_MODE}
+ */
+ public final static String PNAME_BASE_DNSSEC_PIN
+ = "net.java.sip.communicator.util.dns.pin";
+
+ final static String EVENT_TYPE = "DNSSEC_NOTIFICATION";
+
+ private ConfigurationService config
+ = DnsUtilActivator.getConfigurationService();
+ private ResourceManagementService R
+ = DnsUtilActivator.getResources();
+ private Map<String, Date> lastNotifications
+ = new HashMap<String, Date>();
+
+ private ExtendedResolver headResolver;
+
+ /**
+ * Creates a new instance of this class. Tries to use the system's
+ * default forwarders.
+ */
+ public ConfigurableDnssecResolver(ExtendedResolver headResolver)
+ {
+ super(headResolver);
+
+ List<String> propNames
+ = config.getPropertyNamesByPrefix("org.jitsi.dnssec", false);
+ Properties config = new Properties();
+ for (String propName : propNames)
+ {
+ String value = config.getProperty(propName);
+ if (!StringUtils.isNullOrEmpty(value))
+ {
+ config.put(propName, value);
+ }
+ }
+
+ try
+ {
+ super.init(config);
+ }
+ catch (IOException e)
+ {
+ logger.error("Extended dnssec properties contained an error", e);
+ }
+
+ this.headResolver = headResolver;
+ reset();
+ Lookup.setDefaultResolver(this);
+
+ DnsUtilActivator.getNotificationService().
+ registerDefaultNotificationForEvent(
+ ConfigurableDnssecResolver.EVENT_TYPE,
+ NotificationAction.ACTION_POPUP_MESSAGE,
+ null, null);
+ }
+
+ /**
+ * Inspects a DNS answer message and handles validation results according to
+ * the user's preferences.
+ *
+ * @throws DnssecRuntimeException when the validation failed and the user
+ * did not choose to ignore it.
+ */
+ @Override
+ public Message send(Message query)
+ throws DnssecRuntimeException, IOException
+ {
+ //---------------------------------------------------------------------
+ // || 1 | 2 | 3 | 4 | 5
+ //---------------------------------------------------------------------
+ // Sec. | Bog. || Ign. | Sec.Only | Sec.Or.Unsig | Warn.Bog | WarnAll
+ //---------------------------------------------------------------------
+ //a) 1 | 0 || ok | ok | ok | ok | ok
+ //b) 0 | 1 || ok | nok | nok | ask | ask
+ //c) 0 | 0 || ok | nok | ok | ok | ask
+ //---------------------------------------------------------------------
+
+ SecureMessage msg = new SecureMessage(super.send(query));
+ String fqdn = msg.getQuestion().getName().toString();
+ String type = Type.string(msg.getQuestion().getType());
+ String propName = createPropNameUnsigned(fqdn, type);
+ SecureResolveMode defaultAction = Enum.valueOf(SecureResolveMode.class,
+ config.getString(
+ PNAME_DNSSEC_VALIDATION_MODE,
+ SecureResolveMode.WarnIfBogus.name()
+ )
+ );
+ SecureResolveMode pinned = Enum.valueOf(SecureResolveMode.class,
+ config.getString(
+ propName,
+ defaultAction.name()
+ )
+ );
+
+ //create default entry
+ if(pinned == defaultAction)
+ config.setProperty(propName, pinned.name());
+
+ //check domain policy
+
+ //[abc]1, a[2-5]
+ if(pinned == SecureResolveMode.IgnoreDnssec || msg.isSecure())
+ return msg;
+
+ if(
+ //b2, c2
+ (pinned == SecureResolveMode.SecureOnly && !msg.isSecure())
+ ||
+ //b3
+ (pinned == SecureResolveMode.SecureOrUnsigned && msg.isBogus())
+ )
+ {
+ String text = getExceptionMessage(msg);
+ Date last = lastNotifications.get(text);
+ if(last == null
+ //wait at least 5 minutes before showing the same info again
+ || last.before(new Date(new Date().getTime() - 1000*60*5)))
+ {
+ DnsUtilActivator.getNotificationService().fireNotification(
+ EVENT_TYPE,
+ R.getI18NString("util.dns.INSECURE_ANSWER_TITLE"),
+ text,
+ null);
+ lastNotifications.put(text, new Date());
+ }
+
+ throw new DnssecRuntimeException(text);
+ }
+
+ //c3
+ if(pinned == SecureResolveMode.SecureOrUnsigned && !msg.isBogus())
+ return msg;
+
+ //c4
+ if(pinned == SecureResolveMode.WarnIfBogus && !msg.isBogus())
+ return msg;
+
+ //b4, b5, c5
+ String reason = msg.isBogus()
+ ? R.getI18NString("util.dns.DNSSEC_ADVANCED_REASON_BOGUS",
+ new String[]{fqdn, msg.getBogusReason()})
+ : R.getI18NString("util.dns.DNSSEC_ADVANCED_REASON_UNSIGNED",
+ new String[]{type, fqdn});
+ DnssecDialog dlg = new DnssecDialog(fqdn, reason);
+ dlg.setVisible(true);
+ DnssecDialogResult result = dlg.getResult();
+ switch(result)
+ {
+ case Accept:
+ break;
+ case Deny:
+ throw new DnssecRuntimeException(getExceptionMessage(msg));
+ case AlwaysAccept:
+ if(msg.isBogus())
+ config.setProperty(propName,
+ SecureResolveMode.IgnoreDnssec.name());
+ else
+ config.setProperty(propName,
+ SecureResolveMode.WarnIfBogus.name());
+ break;
+ case AlwaysDeny:
+ config.setProperty(propName, SecureResolveMode.SecureOnly);
+ throw new DnssecRuntimeException(getExceptionMessage(msg));
+ }
+
+ return msg;
+ }
+
+ /**
+ * Defines the return code from the DNSSEC verification dialog.
+ */
+ private enum DnssecDialogResult
+ {
+ /** The DNS result shall be accepted. */
+ Accept,
+ /** The result shall be rejected. */
+ Deny,
+ /** The result shall be accepted permanently. */
+ AlwaysAccept,
+ /**
+ * The result shall be rejected automatically unless it is valid
+ * according to DNSSEC.
+ */
+ AlwaysDeny
+ }
+
+ /**
+ * Dialog to ask and warn the user if he wants to continue to accept an
+ * invalid dnssec result.
+ */
+ private class DnssecDialog extends SIPCommDialog implements ActionListener
+ {
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ //UI controls
+ private JPanel pnlAdvanced;
+ private JPanel pnlStandard;
+ private final String domain;
+ private final String reason;
+ private JButton cmdAck;
+ private JButton cmdShowDetails;
+
+ //state
+ private DnssecDialogResult result = DnssecDialogResult.Deny;
+
+ /**
+ * Creates a new instance of this class.
+ * @param domain The FQDN of the domain that failed.
+ * @param reason String describing why the validation failed.
+ */
+ public DnssecDialog(String domain, String reason)
+ {
+ super(false);
+ setModal(true);
+ this.domain = domain;
+ this.reason = reason;
+ initComponents();
+ }
+
+ /**
+ * Creates the UI controls
+ */
+ private void initComponents()
+ {
+ setLayout(new BorderLayout(15, 15));
+ setTitle(R.getI18NString("util.dns.INSECURE_ANSWER_TITLE"));
+
+ // warning text
+ JLabel imgWarning =
+ new JLabel(R.getImage("service.gui.icons.WARNING_ICON"));
+ imgWarning.setBorder(BorderFactory
+ .createEmptyBorder(10, 10, 10, 10));
+ add(imgWarning, BorderLayout.WEST);
+ JLabel lblWarning = new JLabel(
+ R.getI18NString("util.dns.DNSSEC_WARNING", new String[]{
+ R.getSettingsString("service.gui.APPLICATION_NAME"),
+ domain
+ })
+ );
+ add(lblWarning, BorderLayout.CENTER);
+
+ //standard panel (deny option)
+ cmdAck = new JButton(R.getI18NString("service.gui.OK"));
+ cmdAck.addActionListener(this);
+
+ cmdShowDetails = new JButton(
+ R.getI18NString("util.dns.DNSSEC_ADVANCED_OPTIONS"));
+ cmdShowDetails.addActionListener(this);
+
+ pnlStandard = new TransparentPanel(new BorderLayout());
+ pnlStandard.setBorder(BorderFactory
+ .createEmptyBorder(10, 10, 10, 10));
+ pnlStandard.add(cmdShowDetails, BorderLayout.WEST);
+ pnlStandard.add(cmdAck, BorderLayout.EAST);
+ add(pnlStandard, BorderLayout.SOUTH);
+
+ //advanced panel
+ pnlAdvanced = new TransparentPanel(new BorderLayout());
+ JPanel pnlAdvancedButtons = new TransparentPanel(
+ new FlowLayout(FlowLayout.RIGHT));
+ pnlAdvancedButtons.setBorder(BorderFactory
+ .createEmptyBorder(10, 10, 10, 10));
+ pnlAdvanced.add(pnlAdvancedButtons, BorderLayout.EAST);
+ for(DnssecDialogResult r : DnssecDialogResult.values())
+ {
+ JButton cmd = new JButton(R.getI18NString(
+ "net.java.sip.communicator.util.dns."
+ + "ConfigurableDnssecResolver$DnssecDialogResult."
+ + r.name()));
+ cmd.setActionCommand(r.name());
+ cmd.addActionListener(this);
+ pnlAdvancedButtons.add(cmd);
+ }
+ JLabel lblReason = new JLabel(reason);
+ lblReason.setBorder(BorderFactory
+ .createEmptyBorder(10, 10, 10, 10));
+ pnlAdvanced.add(lblReason, BorderLayout.NORTH);
+ }
+
+ /**
+ * Handles the events coming from the buttons.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if(e.getSource() == cmdAck)
+ {
+ result = DnssecDialogResult.Deny;
+ dispose();
+ }
+ else if(e.getSource() == cmdShowDetails)
+ {
+ getContentPane().remove(pnlStandard);
+ add(pnlAdvanced, BorderLayout.SOUTH);
+ pack();
+ }
+ else
+ {
+ result = Enum.valueOf(DnssecDialogResult.class,
+ e.getActionCommand());
+ dispose();
+ }
+ }
+
+ /**
+ * Gets the option that user has chosen.
+ * @return the option that user has chosen.
+ */
+ public DnssecDialogResult getResult()
+ {
+ return result;
+ }
+ }
+
+ private String getExceptionMessage(SecureMessage msg)
+ {
+ return msg.getBogusReason() == null
+ ? R.getI18NString(
+ "util.dns.INSECURE_ANSWER_MESSAGE_NO_REASON",
+ new String[]{msg.getQuestion().getName().toString()}
+ )
+ : R.getI18NString(
+ "util.dns.INSECURE_ANSWER_MESSAGE_REASON",
+ new String[]{msg.getQuestion().getName().toString(),
+ //TODO parse bogus reason text and translate
+ msg.getBogusReason()}
+ );
+ }
+
+ private String createPropNameUnsigned(String fqdn, String type)
+ {
+ return PNAME_BASE_DNSSEC_PIN + "." + fqdn.replace(".", "__");
+ }
+
+ /**
+ * Reloads the configuration of forwarders and trust anchors.
+ */
+ @Override
+ public void reset()
+ {
+ String forwarders = DnsUtilActivator.getConfigurationService()
+ .getString(DnsUtilActivator.PNAME_DNSSEC_NAMESERVERS);
+ if(!StringUtils.isNullOrEmpty(forwarders, true))
+ {
+ if(logger.isTraceEnabled())
+ {
+ logger.trace("Setting DNSSEC forwarders to: " + forwarders);
+ }
+
+ synchronized (Lookup.class)
+ {
+ Lookup.refreshDefault();
+ String[] fwds = forwarders.split(",");
+ Resolver[] rs = headResolver.getResolvers();
+ for (Resolver r : rs)
+ {
+ headResolver.deleteResolver(r);
+ }
+
+ for (String fwd : fwds)
+ {
+ try
+ {
+ SimpleResolver sr = new SimpleResolver(fwd);
+
+ // these properties are normally set by the
+ // ValidatingResolver in the constructor
+ sr.setEDNS(0, 0, ExtendedFlags.DO, null);
+ sr.setIgnoreTruncation(false);
+ headResolver.addResolver(sr);
+ }
+ catch (UnknownHostException e)
+ {
+ logger.error("Invalid forwarder, ignoring", e);
+ }
+ }
+
+ Lookup.setDefaultResolver(this);
+ }
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for(int i = 1;;i++)
+ {
+ String anchor = DnsUtilActivator.getResources().getSettingsString(
+ "net.java.sip.communicator.util.dns.DS_ROOT." + i);
+ if(anchor == null)
+ {
+ break;
+ }
+
+ sb.append(anchor);
+ sb.append('\n');
+ }
+
+ try
+ {
+ super.loadTrustAnchors(new ByteArrayInputStream(
+ sb.toString().getBytes("ASCII")));
+ }
+ catch (IOException e)
+ {
+ logger.error("Could not load the trust anchors", e);
+ }
+
+ if(logger.isTraceEnabled())
+ logger.trace("Loaded trust anchors " + sb.toString());
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/dns/DnsUtilActivator.java b/src/net/java/sip/communicator/impl/dns/DnsUtilActivator.java
index e8b2e7d..4fc828c 100644
--- a/src/net/java/sip/communicator/impl/dns/DnsUtilActivator.java
+++ b/src/net/java/sip/communicator/impl/dns/DnsUtilActivator.java
@@ -150,7 +150,7 @@ public class DnsUtilActivator
{
bundleContext.registerService(
CustomResolver.class.getName(),
- new ConfigurableDnssecResolver(),
+ new ConfigurableDnssecResolver(new ExtendedResolver()),
null);
logger.info("DnssecResolver ... [REGISTERED]");
}
diff --git a/src/net/java/sip/communicator/impl/dns/SecureMessage.java b/src/net/java/sip/communicator/impl/dns/SecureMessage.java
index 7fe9b68..15f77b0 100644
--- a/src/net/java/sip/communicator/impl/dns/SecureMessage.java
+++ b/src/net/java/sip/communicator/impl/dns/SecureMessage.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,90 +15,97 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.dns;
-
-import java.io.*;
-
-import org.xbill.DNS.*;
-
-/**
- * DNS Message that adds DNSSEC validation information.
- *
- * @author Ingo Bauersachs
- */
-public class SecureMessage
- extends Message
-{
- private boolean secure;
- private boolean bogus;
- private String bogusReason;
-
- /**
- * Creates a new instance of this class based on data received from an
- * Unbound resolve.
- *
- * @param msg The answer of the Unbound resolver.
- * @throws IOException
- */
- public SecureMessage(UnboundResult msg) throws IOException
- {
- super(msg.answerPacket);
- secure = msg.secure;
- bogus = msg.bogus;
- bogusReason = msg.whyBogus;
- }
-
- /**
- * Indicates whether the answer is secure.
- * @return True, if the result is validated securely.
- */
- public boolean isSecure()
- {
- return secure;
- }
-
- /**
- * Indicates if there was a validation failure.
- *
- * @return If the result was not secure (secure == false), and this result
- * is due to a security failure, bogus is true.
- */
- public boolean isBogus()
- {
- return bogus;
- }
-
- /**
- * If the result is bogus this contains a string that describes the failure.
- *
- * @return string that describes the failure.
- */
- public String getBogusReason()
- {
- return bogusReason;
- }
-
- /**
- * Converts the Message to a String. The fields secure, bogus and whyBogus
- * are append as a comment.
- */
- @Override
- public String toString()
- {
- StringBuilder s = new StringBuilder(super.toString());
- s.append('\n');
- s.append(";; Secure: ");
- s.append(secure);
- s.append('\n');
- s.append(";; Bogus: ");
- s.append(bogus);
- s.append('\n');
- if(bogus)
- {
- s.append(";; Reason: ");
- s.append(bogusReason);
- s.append('\n');
- }
- return s.toString();
- }
-}
+package net.java.sip.communicator.impl.dns;
+
+import java.io.*;
+
+import org.jitsi.dnssec.validator.*;
+import org.xbill.DNS.*;
+
+/**
+ * DNS Message that adds DNSSEC validation information.
+ *
+ * @author Ingo Bauersachs
+ */
+public class SecureMessage
+ extends Message
+{
+ private boolean secure;
+ private boolean bogus;
+ private String bogusReason;
+
+ /**
+ * Creates a new instance of this class based on data received from a
+ * dnssecjava resolve.
+ *
+ * @param msg The answer of the dnssecjava resolver.
+ * @throws IOException
+ */
+ public SecureMessage(Message msg) throws IOException
+ {
+ super(msg.toWire());
+ secure = msg.getHeader().getFlag(Flags.AD);
+ bogus = !secure && msg.getRcode() == Rcode.SERVFAIL;
+ for (RRset set : msg.getSectionRRsets(Section.ADDITIONAL)) {
+ if (set.getName().equals(Name.root) && set.getType() == Type.TXT
+ && set.getDClass() == ValidatingResolver.VALIDATION_REASON_QCLASS)
+ {
+ bogusReason = ((TXTRecord)set.first()).getStrings().get(0).toString();
+ }
+ }
+ }
+
+ /**
+ * Indicates whether the answer is secure.
+ * @return True, if the result is validated securely.
+ */
+ public boolean isSecure()
+ {
+ return secure;
+ }
+
+ /**
+ * Indicates if there was a validation failure.
+ *
+ * @return If the result was not secure (secure == false), and this result
+ * is due to a security failure, bogus is true.
+ */
+ public boolean isBogus()
+ {
+ return bogus;
+ }
+
+ /**
+ * If the result is bogus this contains a string that describes the failure.
+ *
+ * @return string that describes the failure.
+ */
+ public String getBogusReason()
+ {
+ return bogusReason;
+ }
+
+ /**
+ * Converts the Message to a String. The fields secure, bogus and whyBogus
+ * are append as a comment.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder s = new StringBuilder(super.toString());
+ s.append('\n');
+ s.append(";; Secure: ");
+ s.append(secure);
+ s.append('\n');
+ s.append(";; Bogus: ");
+ s.append(bogus);
+ s.append('\n');
+ if(bogus)
+ {
+ s.append(";; Reason: ");
+ s.append(bogusReason);
+ s.append('\n');
+ }
+ return s.toString();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/dns/SecureResolveMode.java b/src/net/java/sip/communicator/impl/dns/SecureResolveMode.java
index 8c99836..b74f1c6 100644
--- a/src/net/java/sip/communicator/impl/dns/SecureResolveMode.java
+++ b/src/net/java/sip/communicator/impl/dns/SecureResolveMode.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,40 +15,40 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.dns;
-
-/**
- * Defines how DNSSEC validation errors should be handled.
- *
- * @author Ingo Bauersachs
- */
-public enum SecureResolveMode
-{
- /**
- * Any DNSSEC data is completely ignored.
- */
- IgnoreDnssec,
-
- /**
- * The result of a query is only returned if it validated successfully.
- */
- SecureOnly,
-
- /**
- * The result of a query is returned if it validated successfully or when
- * the zone is unsigned.
- */
- SecureOrUnsigned,
-
- /**
- * If the result of a query is bogus (manipulated, incorrect), the user is
- * to be asked how to proceed.
- */
- WarnIfBogus,
-
- /**
- * If the result of a query is bogus (manipulated, incorrect) or if the zone
- * is unsigned, the user is to be asked how to proceed.
- */
- WarnIfBogusOrUnsigned
-}
+package net.java.sip.communicator.impl.dns;
+
+/**
+ * Defines how DNSSEC validation errors should be handled.
+ *
+ * @author Ingo Bauersachs
+ */
+public enum SecureResolveMode
+{
+ /**
+ * Any DNSSEC data is completely ignored.
+ */
+ IgnoreDnssec,
+
+ /**
+ * The result of a query is only returned if it validated successfully.
+ */
+ SecureOnly,
+
+ /**
+ * The result of a query is returned if it validated successfully or when
+ * the zone is unsigned.
+ */
+ SecureOrUnsigned,
+
+ /**
+ * If the result of a query is bogus (manipulated, incorrect), the user is
+ * to be asked how to proceed.
+ */
+ WarnIfBogus,
+
+ /**
+ * If the result of a query is bogus (manipulated, incorrect) or if the zone
+ * is unsigned, the user is to be asked how to proceed.
+ */
+ WarnIfBogusOrUnsigned
+}
diff --git a/src/net/java/sip/communicator/impl/dns/UnboundApi.java b/src/net/java/sip/communicator/impl/dns/UnboundApi.java
deleted file mode 100644
index 6f35a8b..0000000
--- a/src/net/java/sip/communicator/impl/dns/UnboundApi.java
+++ /dev/null
@@ -1,239 +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.dns;
-
-/**
- * Wrapper for the JUnbound JNI wrapper.
- * <p>
- * The JavaDoc of these methods is directly copied from libunbound, licensed as
- * follows:
- * <p>
- * Copyright (c) 2007, NLnet Labs. All rights reserved.
- *
- * This software is open source.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of the NLNET LABS nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * @author Ingo Bauersachs
- */
-public class UnboundApi
-{
- private static boolean isAvailable;
- private static final Object syncRoot = new Object();
-
- static
- {
- tryLoadUnbound();
- }
-
- /**
- * Attempts to load the Unbound native library. When successful,
- * {@link #isAvailable()} returns true.
- */
- public static void tryLoadUnbound()
- {
- synchronized(syncRoot)
- {
- try
- {
- System.loadLibrary("junbound");
- isAvailable = true;
- }
- catch(UnsatisfiedLinkError e)
- {
- isAvailable = false;
- }
- }
- }
-
- /**
- * Indicates whether the Unbound library is loaded.
- * @return True when the JNI wrapper could be loaded, false otherwise.
- */
- public static boolean isAvailable()
- {
- return isAvailable;
- }
-
- /**
- * Set debug verbosity for the context. Output is directed to stderr. Higher
- * debug level gives more output.
- *
- * @param context context.
- * @param level The debug level.
- */
- public static native void setDebugLevel(long context, int level);
-
- /**
- * Create a resolving and validation context.
- * @return a new context. default initialization. returns NULL on error.
- */
- public static native long createContext();
-
- /**
- * Destroy a validation context and free all its resources. Outstanding
- * async queries are killed and callbacks are not called for them.
- *
- * @param context context to delete
- */
- public static native void deleteContext(long context);
-
- /**
- * Set machine to forward DNS queries to, the caching resolver to use.
- * <p>
- * IP4 or IP6 address. Forwards all DNS requests to that machine, which is
- * expected to run a recursive resolver. If the proxy is not DNSSEC-capable,
- * validation may fail. Can be called several times, in that case the
- * addresses are used as backup servers.
- *
- * @param context context. At this time it is only possible to set
- * configuration before the first resolve is done.
- * @param server address, IP4 or IP6 in string format. If the server is
- * NULL, forwarding is disabled.
- */
- public static native void setForwarder(long context, String server);
-
- /**
- * Add a trust anchor to the given context.
- * <p>
- * The trust anchor is a string, on one line, that holds a valid DNSKEY or
- * DS RR.
- *
- * @param context context. At this time it is only possible to add trusted
- * keys before the first resolve is done.
- * @param anchor string, with zone-format RR on one line. [domainname] [TTL
- * optional] [type] [class optional] [rdata contents]
- */
- public static native void addTrustAnchor(long context, String anchor);
-
- /**
- * Perform resolution and validation of the target name.
- *
- * @param context context. The context is finalized, and can no longer
- * accept config changes.
- * @param name domain name in text format (a zero terminated text string).
- * @param rrtype type of RR in host order, 1 is A (address).
- * @param rrclass class of RR in host order, 1 is IN (for internet).
- * @return the result data is returned in a newly allocated result
- * structure. May be NULL on return, return value is set to an error
- * in that case (out of memory).
- * @throws UnboundException when an error occurred.
- */
- public static native UnboundResult resolve(long context, String name,
- int rrtype, int rrclass) throws UnboundException;
-
- /**
- * Perform resolution and validation of the target name.
- * <p>
- * Asynchronous, after a while, the callback will be called with your data
- * and the result.
- *
- * @param context context. If no thread or process has been created yet to
- * perform the work in the background, it is created now. The
- * context is finalized, and can no longer accept config changes.
- * @param name domain name in text format (a string).
- * @param rrtype type of RR in host order, 1 is A.
- * @param rrclass class of RR in host order, 1 is IN (for internet).
- * @param data this data is your own data (you can pass null), and is passed
- * on to the callback function.
- * @param cb this is called on completion of the resolution.
- * @return an identifier number is returned for the query as it is in
- * progress. It can be used to cancel the query.
- * @throws UnboundException when an error occurred.
- */
- public static native int resolveAsync(long context, String name,
- int rrtype, int rrclass, Object data, UnboundCallback cb)
- throws UnboundException;
-
- /**
- * Cancel an async query in progress. Its callback will not be called.
- *
- * @param context context.
- * @param asyncId which query to cancel.
- * @throws UnboundException This routine can error if the async_id passed
- * does not exist or has already been delivered. If another
- * thread is processing results at the same time, the result may
- * be delivered at the same time and the cancel fails with an
- * error. Also the cancel can fail due to a system error, no
- * memory or socket failures.
- */
- public static native void cancelAsync(long context, int asyncId)
- throws UnboundException;
-
- /**
- * Convert error value to a human readable string.
- *
- * @param code error code from one of the Unbound functions.
- * @return text string of the error code.
- */
- public static native String errorCodeToString(int code);
-
- /**
- * Wait for a context to finish with results. Call this routine to continue
- * processing results from the validating resolver. After the wait, there
- * are no more outstanding asynchronous queries.
- *
- * @param context context.
- * @throws UnboundException when an error occurred.
- */
- public static native void processAsync(long context)
- throws UnboundException;
-
- /**
- * Interface for the async resolve callback.
- */
- public interface UnboundCallback
- {
- /**
- * Called on completion of the async resolution.
- *
- * @param data the same object as passed to
- * {@link UnboundApi#resolveAsync(long, String, int, int,
- * Object, UnboundCallback)}
- * @param err 0 when a result has been found, an Unbound error code
- * otherwise
- * @param result a newly allocated result structure. The result may be
- * null, in that case err is set.
- */
- public void UnboundResolveCallback(Object data, int err,
- UnboundResult result);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/dns/UnboundResolver.java b/src/net/java/sip/communicator/impl/dns/UnboundResolver.java
deleted file mode 100644
index 309e58e..0000000
--- a/src/net/java/sip/communicator/impl/dns/UnboundResolver.java
+++ /dev/null
@@ -1,410 +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.dns;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import java.util.concurrent.*;
-
-import net.java.sip.communicator.service.dns.*;
-import net.java.sip.communicator.util.*;
-
-import org.xbill.DNS.*;
-
-/**
- * Implementation of the {@link Resolver} interface, wrapping the native NLnet
- * Labs Unbound resolver. Only the basic methods for queries are supported.
- *
- * @author Ingo Bauersachs
- */
-public class UnboundResolver
- implements CustomResolver
-{
- private final static Logger logger =
- Logger.getLogger(UnboundResolver.class);
-
- /**
- * Helper class to synchronize on asynchronous queries.
- */
- private static class CallbackData
- {
- /**
- * The resolver consumer that wishes to be informed when the request
- * completed.
- */
- ResolverListener listener;
-
- /**
- * The unbound session context.
- */
- long context;
-
- /**
- * The ID of the unbound async query.
- */
- int asyncId;
-
- /**
- * Java synchronization on top of unbound.
- */
- CountDownLatch sync = new CountDownLatch(1);
- }
-
- /**
- * Timeout for DNS queries.
- */
- private int timeout = 10000;
-
- /**
- * The recursive DNS servers answering our queries.
- */
- private String[] forwarders;
-
- /**
- * DNSSEC trust anchors for signed zones (usually for the root zone).
- */
- private List<String> trustAnchors = new LinkedList<String>();
-
- /**
- * Pool that executes our queries.
- */
- private ExecutorService threadPool;
-
- /**
- * Creates a new instance of this class.
- */
- public UnboundResolver()
- {
- threadPool = Executors.newCachedThreadPool();
- }
-
- /**
- * Sets a list of forwarders to use instead of the system default.
- *
- * @param forwarders list of servers to use for our queries.
- */
- public void setForwarders(String[] forwarders)
- {
- this.forwarders = forwarders;
- }
-
- /**
- * Clears any existing trust anchors previously added.
- */
- public void clearTrustAnchors()
- {
- trustAnchors.clear();
- }
-
- /**
- * Adds a DNSSEC trust anchor validation of the DNSKEYs.
- *
- * @param anchor trust anchor in the form of
- * "'zone' IN DS 'key tag' 'algorithm' 'digest type' 'digest'"
- */
- public void addTrustAnchor(String anchor)
- {
- trustAnchors.add(anchor);
- }
-
- /**
- * {@inheritDoc}
- */
- public SecureMessage send(final Message query) throws IOException
- {
- Future<SecureMessage> future = threadPool.submit(
- new Callable<SecureMessage>()
- {
- public SecureMessage call() throws Exception
- {
- if(logger.isDebugEnabled())
- logger.debug(query);
-
- SecureMessage secureMessage = null;
- final long context = prepareContext();
- try
- {
- UnboundResult result = UnboundApi.resolve(
- context,
- query.getQuestion().getName().toString(),
- query.getQuestion().getType(),
- query.getQuestion().getDClass()
- );
- secureMessage = new SecureMessage(result);
- validateMessage(secureMessage);
- }
- finally
- {
- UnboundApi.deleteContext(context);
- if(logger.isDebugEnabled() && secureMessage != null)
- logger.debug(secureMessage);
- }
-
- return secureMessage;
- }
- });
- try
- {
- return future.get(timeout, TimeUnit.SECONDS);
- }
- catch (InterruptedException e)
- {
- logger.error(e);
- throw new IOException(e.getMessage());
- }
- catch (ExecutionException e)
- {
- if(e.getCause() instanceof DnssecRuntimeException)
- throw new DnssecRuntimeException(e.getCause().getMessage());
- logger.error(e);
- throw new IOException(e.getMessage());
- }
- catch (TimeoutException e)
- {
- throw new SocketTimeoutException(e.getMessage());
- }
- }
-
- /**
- * Method to allow overriders to inspect the message. This class'
- * implementation does nothing.
- *
- * @param msg The message to inspect.
- * @throws DnssecRuntimeException if the inspector does not want the code to
- * continue normal processing of the answer.
- */
- protected void validateMessage(SecureMessage msg)
- throws DnssecRuntimeException
- {
- }
-
- /**
- * Prepares a unbound session context initialized with forwarders and trust
- * anchors.
- *
- * @return The context id
- */
- private long prepareContext()
- {
- final long context = UnboundApi.createContext();
- if(logger.isTraceEnabled())
- UnboundApi.setDebugLevel(context, 100);
- for(String fwd : forwarders == null
- ? ResolverConfig.getCurrentConfig().servers()
- : forwarders)
- {
- fwd = fwd.trim();
- if(NetworkUtils.isValidIPAddress(fwd))
- {
- if(fwd.startsWith("["))
- fwd = fwd.substring(1, fwd.length() - 1);
- UnboundApi.setForwarder(context, fwd);
- }
- }
- for(String anchor : trustAnchors)
- {
- UnboundApi.addTrustAnchor(context, anchor);
- }
- return context;
- }
-
- /**
- * Cleans up an Unbound session context.
- *
- * @param cbData The helper object of the asynchronous call.
- * @param cancelAsync Whether an outstanding asynchronous unbound query
- * should be canceled.
- */
- private static synchronized void deleteContext(CallbackData cbData,
- boolean cancelAsync)
- {
- if(cbData.context == 0)
- return;
-
- if(cancelAsync)
- {
- try
- {
- UnboundApi.cancelAsync(cbData.context, cbData.asyncId);
- }
- catch (UnboundException ignore)
- {}
- }
- UnboundApi.deleteContext(cbData.context);
- cbData.context = 0;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see org.xbill.DNS.Resolver#sendAsync(org.xbill.DNS.Message,
- * org.xbill.DNS.ResolverListener)
- */
- public CallbackData sendAsync(Message query, ResolverListener listener)
- {
- if(listener == null)
- throw new IllegalArgumentException("listener cannot be null");
-
- final long context = prepareContext();
- final CallbackData cbData = new CallbackData();
- cbData.listener = listener;
- cbData.context = context;
- int asyncId;
- try
- {
- asyncId = UnboundApi.resolveAsync(
- context,
- query.getQuestion().getName().toString(),
- query.getQuestion().getType(),
- query.getQuestion().getDClass(),
- cbData,
- new UnboundApi.UnboundCallback()
- {
- public void UnboundResolveCallback(Object data, int err,
- UnboundResult result)
- {
- CallbackData cbData = (CallbackData)data;
- deleteContext(cbData, false);
-
- ResolverListener l = cbData.listener;
- if(err == 0)
- {
- try
- {
- l.receiveMessage(data,
- new SecureMessage(result));
- }
- catch (IOException e)
- {
- l.handleException(data, e);
- }
- }
- else
- l.handleException(data,
- new Exception(
- UnboundApi.errorCodeToString(err)));
-
- cbData.sync.countDown();
- }
- }
- );
- }
- catch (UnboundException e)
- {
- listener.handleException(null, e);
- return null;
- }
- cbData.asyncId = asyncId;
- threadPool.execute(new Runnable()
- {
- public void run()
- {
- try
- {
- UnboundApi.processAsync(context);
- }
- catch(UnboundException ex)
- {
- cbData.listener.handleException(this, ex);
- deleteContext(cbData, false);
- cbData.sync.countDown();
- }
- }
- });
- return cbData;
- }
-
- /**
- * Not supported.
- * @throws UnsupportedOperationException
- */
- public void setEDNS(int level)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Not supported.
- * @throws UnsupportedOperationException
- */
- @SuppressWarnings("rawtypes")
- public void setEDNS(int level, int payloadSize, int flags, List options)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Not supported.
- * @throws UnsupportedOperationException
- */
- public void setIgnoreTruncation(boolean flag)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Not supported.
- * @throws UnsupportedOperationException
- */
- public void setPort(int port)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Not supported.
- * @throws UnsupportedOperationException
- */
- public void setTCP(boolean flag)
- {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Not supported.
- * @throws UnsupportedOperationException
- */
- public void setTSIGKey(TSIG key)
- {
- throw new UnsupportedOperationException();
- }
-
- /* (non-Javadoc)
- * @see org.xbill.DNS.Resolver#setTimeout(int)
- */
- public void setTimeout(int secs)
- {
- timeout = secs * 1000;
- }
-
- /* (non-Javadoc)
- * @see org.xbill.DNS.Resolver#setTimeout(int, int)
- */
- public void setTimeout(int secs, int msecs)
- {
- timeout = secs * 1000 + msecs;
- }
-
- /**
- * Does nothing.
- */
- public void reset()
- {
- }
-}
diff --git a/src/net/java/sip/communicator/impl/dns/UnboundResult.java b/src/net/java/sip/communicator/impl/dns/UnboundResult.java
deleted file mode 100644
index 85167d0..0000000
--- a/src/net/java/sip/communicator/impl/dns/UnboundResult.java
+++ /dev/null
@@ -1,128 +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.dns;
-
-/**
- * Class that contains the answer to query processed by the native Unbound
- * resolver. Corresponds to the <a
- * href="http://unbound.net/documentation/doxygen/structub__result.html"
- * >ub_result</a> data structure.
- *
- * The fields {@link #data} and {@link #canonname} are not filled.
- * <p>
- * The JavaDoc of these fields is directly copied from libunbound, licensed as
- * follows:
- * <p>
- * Copyright (c) 2007, NLnet Labs. All rights reserved.
- *
- * This software is open source.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * Neither the name of the NLNET LABS nor the names of its contributors may be
- * used to endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * @author Ingo Bauersachs
- */
-public class UnboundResult
-{
- /**
- * The original question, name text string.
- */
- String qname;
-
- /**
- * the type asked for
- */
- int qtype;
-
- /**
- * the type asked for
- */
- int qclass;
-
-
- /**
- * a list of network order DNS rdata items, terminated with a NULL pointer,
- * so that data[0] is the first result entry, data[1] the second, and the
- * last entry is NULL.
- */
- byte[][] data;
-
- /**
- * canonical name for the result (the final cname).
- */
- String canonname;
-
- /**
- * DNS RCODE for the result.
- */
- int rcode;
-
- /**
- * The DNS answer packet.
- */
- byte[] answerPacket;
-
-
- /**
- * If there is any data, this is true.
- */
- boolean haveData;
-
- /**
- * If there was no data, and the domain did not exist, this is true.
- */
- boolean nxDomain;
-
- /**
- * True, if the result is validated securely.
- */
- boolean secure;
-
- /**
- * If the result was not secure ({@link #secure} == false), and this result
- * is due to a security failure, bogus is true.
- */
- boolean bogus;
-
- /**
- * If the result is bogus this contains a string (zero terminated) that
- * describes the failure.
- */
- String whyBogus;
-}
diff --git a/src/net/java/sip/communicator/impl/dns/dns.manifest.mf b/src/net/java/sip/communicator/impl/dns/dns.manifest.mf
index 331eba1..33174b5 100644
--- a/src/net/java/sip/communicator/impl/dns/dns.manifest.mf
+++ b/src/net/java/sip/communicator/impl/dns/dns.manifest.mf
@@ -8,6 +8,8 @@ Import-Package: org.jitsi.util,
org.osgi.framework,
net.java.sip.communicator.util,
net.java.sip.communicator.plugin.desktoputil,
+ org.jitsi.dnssec,
+ org.jitsi.dnssec.validator,
org.jitsi.service.resources,
org.jitsi.service.fileaccess,
net.java.sip.communicator.service.resources,
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java
index d4d2ed5..cf42720 100644
--- a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java
+++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsEntryImpl.java
@@ -43,20 +43,14 @@ public class GoogleContactsEntryImpl
/**
* Google Talk protocol type.
*/
- private static final String YAHOO_PROTOCOL =
- "http://schemas.google.com/g/2005#YAHOO";
-
- /**
- * Google Talk protocol type.
- */
private static final String AIM_PROTOCOL =
"http://schemas.google.com/g/2005#AIM";
/**
* Google Talk protocol type.
*/
- private static final String MSN_PROTOCOL =
- "http://schemas.google.com/g/2005#MSN";
+ private static final String SKYPE_PROTOCOL =
+ "http://schemas.google.com/g/2005#SKYPE";
/**
* Google Talk protocol type.
@@ -432,17 +426,13 @@ public class GoogleContactsEntryImpl
{
proto = GoogleContactsEntry.IMProtocol.GOOGLETALK;
}
- else if(protocol.equals(YAHOO_PROTOCOL))
- {
- proto = GoogleContactsEntry.IMProtocol.YAHOO;
- }
else if(protocol.equals(AIM_PROTOCOL))
{
proto = GoogleContactsEntry.IMProtocol.AIM;
}
- else if(protocol.equals(MSN_PROTOCOL))
+ else if(protocol.equals(SKYPE_PROTOCOL))
{
- proto = GoogleContactsEntry.IMProtocol.MSN;
+ proto = GoogleContactsEntry.IMProtocol.SKYPE;
}
else if(protocol.equals(ICQ_PROTOCOL))
{
diff --git a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java
index d33e4da..66ea7ad 100644
--- a/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java
+++ b/src/net/java/sip/communicator/impl/googlecontacts/GoogleContactsQuery.java
@@ -279,14 +279,11 @@ public class GoogleContactsQuery
case ICQ:
imSubCat = ContactDetail.SubCategory.ICQ;
break;
- case YAHOO:
- imSubCat = ContactDetail.SubCategory.Yahoo;
- break;
case JABBER:
imSubCat = ContactDetail.SubCategory.Jabber;
break;
- case MSN:
- imSubCat = ContactDetail.SubCategory.MSN;
+ case SKYPE:
+ imSubCat = ContactDetail.SubCategory.Skype;
break;
case GOOGLETALK:
imSubCat = ContactDetail.SubCategory.GoogleTalk;
@@ -357,12 +354,6 @@ public class GoogleContactsQuery
OperationSetBasicTelephony.class,
ProtocolNames.JABBER);
break;
- case YAHOO:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.YAHOO);
- break;
default:
break;
}
diff --git a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
index 2e8735f..db2eac9 100644
--- a/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/gui/UIServiceImpl.java
@@ -74,7 +74,8 @@ public class UIServiceImpl
implements UIService,
ShutdownService,
ServiceListener,
- PropertyChangeListener
+ PropertyChangeListener,
+ UINotificationListener
{
/**
* The <tt>Logger</tt> used by the <tt>UIServiceImpl</tt> class and its
@@ -140,6 +141,7 @@ public class UIServiceImpl
*/
public UIServiceImpl()
{
+ UINotificationManager.addNotificationListener(this);
}
/**
@@ -197,8 +199,12 @@ public class UIServiceImpl
}
}
- if(ConfigurationUtils.isApplicationVisible())
+ if(ConfigurationUtils.isApplicationVisible()
+ || Boolean.getBoolean("disable-tray")
+ || ConfigurationUtils.isMinimizeInsteadOfHide())
+ {
mainFrame.setFrameVisible(true);
+ }
SwingUtilities.invokeLater(new RunLoginGui());
@@ -428,36 +434,16 @@ public class UIServiceImpl
}
/**
- * Implements {@link UIService#setExitOnMainWindowClose}. Sets the boolean
- * property which indicates whether the application should be exited when
- * the main application window is closed.
- *
- * @param exitOnMainWindowClose <tt>true</tt> if closing the main
- * application window should also be exiting the application; otherwise,
- * <tt>false</tt>
- */
- public void setExitOnMainWindowClose(boolean exitOnMainWindowClose)
- {
- mainFrame.setDefaultCloseOperation(
- exitOnMainWindowClose
- ? JFrame.DISPOSE_ON_CLOSE
- : JFrame.HIDE_ON_CLOSE);
- }
-
- /**
- * Implements {@link UIService#getExitOnMainWindowClose()}. Gets the boolean
- * property which indicates whether the application should be exited when
- * the main application window is closed.
- *
- * @return determines whether the UI impl would exit the application when
- * the main application window is closed.
+ * Called from the systray service when a tray has been initialized and
+ * hiding (instead of minimizing or exiting) is possible). If hiding is
+ * possible and the option to minimize is not selected, the application
+ * gets hidden on clicking 'X'.
+ *
+ * @param true if a tray icon was loaded.
*/
- public boolean getExitOnMainWindowClose()
+ public void setMainWindowCanHide(boolean canHide)
{
- return
- (mainFrame != null)
- && (mainFrame.getDefaultCloseOperation()
- == JFrame.DISPOSE_ON_CLOSE);
+ mainFrame.updateCloseAction(canHide);
}
/**
@@ -1642,4 +1628,41 @@ public class UIServiceImpl
ChatRoomAutoOpenConfigDialog.showChatRoomAutoOpenConfigDialog(
pps, chatRoomId);
}
+
+ /**
+ * Counts the number of unread notifications and forwards the sum to the
+ * systray service.
+ */
+ @Override
+ public void notificationReceived(UINotification notification)
+ {
+ forwardNotificationCount();
+ }
+
+ /**
+ * Counts the number of unread notifications and forwards the sum to the
+ * systray service.
+ */
+ @Override
+ public void notificationCleared(UINotification notification)
+ {
+ forwardNotificationCount();
+ }
+
+ private void forwardNotificationCount()
+ {
+ int count = 0;
+ for (UINotificationGroup g : UINotificationManager
+ .getNotificationGroups())
+ {
+ Iterator<UINotification> it =
+ UINotificationManager.getUnreadNotifications(g);
+ while (it.hasNext())
+ {
+ count += it.next().getUnreadObjects();
+ }
+ }
+
+ GuiActivator.getSystrayService().setNotificationCount(count);
+ }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
index a8ee80a..b450efe 100644
--- a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
+++ b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
@@ -334,13 +334,9 @@ public class MainFrame
*/
private void init()
{
- setDefaultCloseOperation(
- GuiActivator.getUIService().getExitOnMainWindowClose()
- ? JFrame.DISPOSE_ON_CLOSE
- : JFrame.HIDE_ON_CLOSE);
-
+ // at startup, we cannot hide yet
+ updateCloseAction(false);
registerKeyActions();
-
JComponent northPanel = createTopComponent();
this.setJMenuBar(menu);
@@ -394,6 +390,30 @@ public class MainFrame
}
}
+ /**
+ * If hiding is possible and the option to minimize is not selected, the
+ * application gets hidden on clicking 'X'.
+ *
+ * @param true if hiding is possible, i.e. a tray icon is loaded
+ */
+ public void updateCloseAction(boolean canHide)
+ {
+ if (ConfigurationUtils.isMinimizeInsteadOfHide())
+ {
+ logger.info("Updating close action: DO_NOTHING_ON_CLOSE");
+ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+ }
+ else
+ {
+ logger.info("Updating close action: " + (canHide
+ ? "HIDE_ON_CLOSE"
+ : "DISPOSE_ON_CLOSE"));
+ setDefaultCloseOperation(canHide
+ ? JFrame.HIDE_ON_CLOSE
+ : JFrame.DISPOSE_ON_CLOSE);
+ }
+ }
+
private Component createButtonPanel()
{
boolean isCallButtonEnabled = false;
@@ -1885,7 +1905,8 @@ public class MainFrame
*/
protected void windowClosed(WindowEvent event)
{
- if(GuiActivator.getUIService().getExitOnMainWindowClose())
+ if(getDefaultCloseOperation() == JFrame.EXIT_ON_CLOSE ||
+ getDefaultCloseOperation() == JFrame.DISPOSE_ON_CLOSE)
{
try
{
@@ -1919,9 +1940,17 @@ public class MainFrame
// On Mac systems the application is not quited on window close, so we
// don't need to warn the user.
- if (!GuiActivator.getUIService().getExitOnMainWindowClose()
- && !OSUtils.IS_MAC)
+ if (OSUtils.IS_MAC)
{
+ return;
+ }
+
+ switch (getDefaultCloseOperation())
+ {
+ case JFrame.EXIT_ON_CLOSE:
+ case JFrame.DISPOSE_ON_CLOSE:
+ return;
+ case JFrame.HIDE_ON_CLOSE:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
@@ -1940,8 +1969,11 @@ public class MainFrame
}
}
});
-
ConfigurationUtils.setApplicationVisible(false);
+ break;
+ case JFrame.DO_NOTHING_ON_CLOSE:
+ this.minimize();
+ break;
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/UINotification.java b/src/net/java/sip/communicator/impl/gui/main/UINotification.java
index fbfb6da..4304907 100644
--- a/src/net/java/sip/communicator/impl/gui/main/UINotification.java
+++ b/src/net/java/sip/communicator/impl/gui/main/UINotification.java
@@ -17,6 +17,8 @@
*/
package net.java.sip.communicator.impl.gui.main;
+import java.util.Objects;
+
/**
* The <tt>UINotification</tt> class represents a notification received in the
* user interface. This could be a missed call, voicemail, email notification or
@@ -172,4 +174,10 @@ public class UINotification
return true;
}
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(notificationName, parentGroup);
+ }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/UINotificationGroup.java b/src/net/java/sip/communicator/impl/gui/main/UINotificationGroup.java
index a2e3673..fb14645 100644
--- a/src/net/java/sip/communicator/impl/gui/main/UINotificationGroup.java
+++ b/src/net/java/sip/communicator/impl/gui/main/UINotificationGroup.java
@@ -99,7 +99,12 @@ public class UINotificationGroup
{
synchronized (unreadNotifications)
{
+ List<UINotification> copy = new ArrayList<>(unreadNotifications);
unreadNotifications.clear();
+ for (UINotification n : copy)
+ {
+ UINotificationManager.fireClearedEvent(n);
+ }
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/UINotificationListener.java b/src/net/java/sip/communicator/impl/gui/main/UINotificationListener.java
index fc5568d..932d96c 100644
--- a/src/net/java/sip/communicator/impl/gui/main/UINotificationListener.java
+++ b/src/net/java/sip/communicator/impl/gui/main/UINotificationListener.java
@@ -32,4 +32,11 @@ public interface UINotificationListener
* @param notification the notification that was received
*/
public void notificationReceived(UINotification notification);
+
+ /**
+ * Indicates that a notification has been cleared.
+ *
+ * @param notification the notification that was cleared.
+ */
+ public void notificationCleared(UINotification notification);
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/UINotificationManager.java b/src/net/java/sip/communicator/impl/gui/main/UINotificationManager.java
index b6001f7..2d42233 100644
--- a/src/net/java/sip/communicator/impl/gui/main/UINotificationManager.java
+++ b/src/net/java/sip/communicator/impl/gui/main/UINotificationManager.java
@@ -150,4 +150,23 @@ public class UINotificationManager
listeners.next().notificationReceived(notification);
}
}
+
+
+ /**
+ * Notifies interested <tt>UINotificationListener</tt> that a
+ * notification has been cleared.
+ *
+ * @param notification the cleared notification
+ */
+ static void fireClearedEvent(UINotification notification)
+ {
+ synchronized (notificationListeners)
+ {
+ Iterator<UINotificationListener> listeners
+ = notificationListeners.iterator();
+
+ while (listeners.hasNext())
+ listeners.next().notificationCleared(notification);
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/account/AccountsConfigurationPanel.java b/src/net/java/sip/communicator/impl/gui/main/account/AccountsConfigurationPanel.java
index 9511310..a85ffc3 100644
--- a/src/net/java/sip/communicator/impl/gui/main/account/AccountsConfigurationPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/account/AccountsConfigurationPanel.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,278 +15,278 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.account;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.beans.*;
-import java.util.List;
-
-import javax.swing.*;
-import javax.swing.event.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.account.*;
-
-import org.jitsi.service.configuration.*;
-import org.jitsi.service.resources.*;
-
-/**
- * The <tt>AccountsConfigurationPanel</tt> is the panel containing the accounts
- * list and according buttons shown in the options form.
- *
- * @author Yana Stamcheva
- * @author Lubomir Marinov
- */
-public class AccountsConfigurationPanel
- extends TransparentPanel
- implements ActionListener,
- ListSelectionListener,
- PropertyChangeListener
-{
- private final AccountList accountList;
-
- private final JButton newButton =
- new JButton(GuiActivator.getResources().getI18NString(
- "service.gui.ADD"));
-
- private final JButton editButton =
- new JButton(GuiActivator.getResources().getI18NString(
- "service.gui.EDIT"));
-
- private final JButton removeButton =
- new JButton(GuiActivator.getResources().getI18NString(
- "service.gui.DELETE"));
-
- /**
- * Creates and initializes this account configuration panel.
- */
- public AccountsConfigurationPanel()
- {
- super(new BorderLayout());
-
- accountList = new AccountList(this);
-
- /*
- * It seems that we can only delete one account at a time because our
- * confirmation dialog asks for one account.
- */
- accountList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-
- this.setPreferredSize(new Dimension(500, 400));
-
- JScrollPane accountListPane = new JScrollPane();
-
- accountListPane.getViewport().add(accountList);
- accountListPane.getVerticalScrollBar().setUnitIncrement(30);
-
- this.add(accountListPane, BorderLayout.CENTER);
-
- JPanel buttonsPanel =
- new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
-
- newButton.addActionListener(this);
- editButton.addActionListener(this);
- removeButton.addActionListener(this);
-
- this.newButton.setMnemonic(GuiActivator.getResources().getI18nMnemonic(
- "service.gui.ADD"));
- this.editButton
- .setMnemonic(GuiActivator.getResources().getI18nMnemonic(
- "service.gui.EDIT"));
- this.removeButton
- .setMnemonic(GuiActivator.getResources().getI18nMnemonic(
- "service.gui.DELETE"));
-
- buttonsPanel.add(newButton);
-
- buttonsPanel.add(editButton);
-
- buttonsPanel.add(removeButton);
-
- this.add(buttonsPanel, BorderLayout.SOUTH);
-
- accountList.addListSelectionListener(this);
- accountList.addPropertyChangeListener(
- AccountList.ACCOUNT_STATE_CHANGED, this);
- updateButtons();
- }
-
- /**
- * Handles the <tt>ActionEvent</tt> triggered when user clicks on on the
- * buttons. Shows the account registration wizard when user clicks on "New".
- *
- * @param evt the action event that has just occurred.
- */
- public void actionPerformed(ActionEvent evt)
- {
- Object sourceButton = evt.getSource();
-
- if (sourceButton.equals(newButton))
- {
- NewAccountDialog.showNewAccountDialog();
- }
- else if (sourceButton.equals(removeButton))
- {
- Account account = accountList.getSelectedAccount();
-
- if (account == null)
- return;
-
- AccountID accountID = account.getAccountID();
-
- ProtocolProviderFactory providerFactory =
- AccountUtils.getProtocolProviderFactory(
- accountID.getProtocolName());
-
- if (providerFactory != null)
- {
- int result = JOptionPane.showConfirmDialog(
- this,
- GuiActivator.getResources()
- .getI18NString("service.gui.REMOVE_ACCOUNT_MESSAGE"),
- GuiActivator.getResources().getI18NString(
- "service.gui.REMOVE_ACCOUNT"),
- JOptionPane.YES_NO_OPTION);
-
- if (result == JOptionPane.YES_OPTION)
- {
- ConfigurationService configService
- = GuiActivator.getConfigurationService();
- String prefix
- = "net.java.sip.communicator.impl.gui.accounts";
- List<String> accounts
- = configService.getPropertyNamesByPrefix(prefix, true);
-
- for (String accountRootPropName : accounts)
- {
- String accountUID
- = configService.getString(accountRootPropName);
-
- if (accountUID.equals(accountID.getAccountUniqueID()))
- {
- configService.setProperty(accountRootPropName, null);
- break;
- }
- }
- boolean isUninstalled
- = providerFactory.uninstallAccount(accountID);
-
- if (isUninstalled)
- {
- accountList.ensureAccountRemoved(accountID);
-
- // Notify the corresponding wizard that the account
- // would be removed.
- AccountRegWizardContainerImpl wizardContainer
- = (AccountRegWizardContainerImpl) GuiActivator
- .getUIService().getAccountRegWizardContainer();
-
- ProtocolProviderService protocolProvider =
- account.getProtocolProvider();
- AccountRegistrationWizard wizard =
- wizardContainer.getProtocolWizard(protocolProvider);
-
- if (wizard != null)
- wizard.accountRemoved(protocolProvider);
- }
- }
- }
- }
- else if (sourceButton.equals(editButton))
- {
- Account account = accountList.getSelectedAccount();
-
- if (account == null)
- return;
-
- AccountRegWizardContainerImpl wizard =
- (AccountRegWizardContainerImpl) GuiActivator.getUIService()
- .getAccountRegWizardContainer();
-
- AccountRegistrationWizard protocolWizard =
- wizard.getProtocolWizard(account.getProtocolProvider());
-
- ResourceManagementService resources = GuiActivator.getResources();
- if (protocolWizard != null)
- {
- wizard.setTitle(resources.getI18NString(
- "service.gui.ACCOUNT_REGISTRATION_WIZARD"));
-
- wizard.modifyAccount(account.getProtocolProvider());
- wizard.showDialog(false);
- }
- else
- {
- // There is no wizard for this account - just show an error
- // dialog:
- String title = resources.getI18NString("service.gui.ERROR");
- String message =
- resources.getI18NString("service.gui.EDIT_NOT_SUPPORTED");
- ErrorDialog dialog = new ErrorDialog(null, title, message);
- dialog.setVisible(true);
- }
- }
- }
-
- /**
- * Returns the edit button.
- *
- * @return the edit button
- */
- public JButton getEditButton()
- {
- return editButton;
- }
-
- /**
- * Updates enabled states of the buttons of this
- * <tt>AccountsConfigurationPanel</tt> to reflect their applicability to the
- * current selection in <tt>accountList</tt>.
- */
- private void updateButtons()
- {
- if(!SwingUtilities.isEventDispatchThread())
- {
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run()
- {
- updateButtons();
- }
- });
- return;
- }
-
- Account account = accountList.getSelectedAccount();
- boolean enabled = (account != null);
-
- editButton.setEnabled(enabled && account.isEnabled());
- removeButton.setEnabled(enabled);
- }
-
- /**
- * Implements ListSelectionListener#valueChanged(ListSelectionEvent).
- * @param e the <tt>ListSelectionEvent</tt> that notified us
- */
- public void valueChanged(ListSelectionEvent e)
- {
- if (!e.getValueIsAdjusting())
- updateButtons();
- }
-
- /**
- * This method gets called when a property is changed.
- *
- * @param evt A PropertyChangeEvent object describing the event source
- * and the property that has changed.
- */
- public void propertyChange(PropertyChangeEvent evt)
- {
- // update buttons whenever an account changes its state
- updateButtons();
- }
-}
+package net.java.sip.communicator.impl.gui.main.account;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.beans.*;
+import java.util.List;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.account.*;
+
+import org.jitsi.service.configuration.*;
+import org.jitsi.service.resources.*;
+
+/**
+ * The <tt>AccountsConfigurationPanel</tt> is the panel containing the accounts
+ * list and according buttons shown in the options form.
+ *
+ * @author Yana Stamcheva
+ * @author Lubomir Marinov
+ */
+public class AccountsConfigurationPanel
+ extends TransparentPanel
+ implements ActionListener,
+ ListSelectionListener,
+ PropertyChangeListener
+{
+ private final AccountList accountList;
+
+ private final JButton newButton =
+ new JButton(GuiActivator.getResources().getI18NString(
+ "service.gui.ADD"));
+
+ private final JButton editButton =
+ new JButton(GuiActivator.getResources().getI18NString(
+ "service.gui.EDIT"));
+
+ private final JButton removeButton =
+ new JButton(GuiActivator.getResources().getI18NString(
+ "service.gui.DELETE"));
+
+ /**
+ * Creates and initializes this account configuration panel.
+ */
+ public AccountsConfigurationPanel()
+ {
+ super(new BorderLayout());
+
+ accountList = new AccountList(this);
+
+ /*
+ * It seems that we can only delete one account at a time because our
+ * confirmation dialog asks for one account.
+ */
+ accountList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+
+ this.setPreferredSize(new Dimension(500, 400));
+
+ JScrollPane accountListPane = new JScrollPane();
+
+ accountListPane.getViewport().add(accountList);
+ accountListPane.getVerticalScrollBar().setUnitIncrement(30);
+
+ this.add(accountListPane, BorderLayout.CENTER);
+
+ JPanel buttonsPanel =
+ new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
+
+ newButton.addActionListener(this);
+ editButton.addActionListener(this);
+ removeButton.addActionListener(this);
+
+ this.newButton.setMnemonic(GuiActivator.getResources().getI18nMnemonic(
+ "service.gui.ADD"));
+ this.editButton
+ .setMnemonic(GuiActivator.getResources().getI18nMnemonic(
+ "service.gui.EDIT"));
+ this.removeButton
+ .setMnemonic(GuiActivator.getResources().getI18nMnemonic(
+ "service.gui.DELETE"));
+
+ buttonsPanel.add(newButton);
+
+ buttonsPanel.add(editButton);
+
+ buttonsPanel.add(removeButton);
+
+ this.add(buttonsPanel, BorderLayout.SOUTH);
+
+ accountList.addListSelectionListener(this);
+ accountList.addPropertyChangeListener(
+ AccountList.ACCOUNT_STATE_CHANGED, this);
+ updateButtons();
+ }
+
+ /**
+ * Handles the <tt>ActionEvent</tt> triggered when user clicks on on the
+ * buttons. Shows the account registration wizard when user clicks on "New".
+ *
+ * @param evt the action event that has just occurred.
+ */
+ public void actionPerformed(ActionEvent evt)
+ {
+ Object sourceButton = evt.getSource();
+
+ if (sourceButton.equals(newButton))
+ {
+ NewAccountDialog.showNewAccountDialog();
+ }
+ else if (sourceButton.equals(removeButton))
+ {
+ Account account = accountList.getSelectedAccount();
+
+ if (account == null)
+ return;
+
+ AccountID accountID = account.getAccountID();
+
+ ProtocolProviderFactory providerFactory =
+ AccountUtils.getProtocolProviderFactory(
+ accountID.getProtocolName());
+
+ if (providerFactory != null)
+ {
+ int result = JOptionPane.showConfirmDialog(
+ this,
+ GuiActivator.getResources()
+ .getI18NString("service.gui.REMOVE_ACCOUNT_MESSAGE"),
+ GuiActivator.getResources().getI18NString(
+ "service.gui.REMOVE_ACCOUNT"),
+ JOptionPane.YES_NO_OPTION);
+
+ if (result == JOptionPane.YES_OPTION)
+ {
+ ConfigurationService configService
+ = GuiActivator.getConfigurationService();
+ String prefix
+ = "net.java.sip.communicator.impl.gui.accounts";
+ List<String> accounts
+ = configService.getPropertyNamesByPrefix(prefix, true);
+
+ for (String accountRootPropName : accounts)
+ {
+ String accountUID
+ = configService.getString(accountRootPropName);
+
+ if (accountUID.equals(accountID.getAccountUniqueID()))
+ {
+ configService.setProperty(accountRootPropName, null);
+ break;
+ }
+ }
+ boolean isUninstalled
+ = providerFactory.uninstallAccount(accountID);
+
+ if (isUninstalled)
+ {
+ accountList.ensureAccountRemoved(accountID);
+
+ // Notify the corresponding wizard that the account
+ // would be removed.
+ AccountRegWizardContainerImpl wizardContainer
+ = (AccountRegWizardContainerImpl) GuiActivator
+ .getUIService().getAccountRegWizardContainer();
+
+ ProtocolProviderService protocolProvider =
+ account.getProtocolProvider();
+ AccountRegistrationWizard wizard =
+ wizardContainer.getProtocolWizard(protocolProvider);
+
+ if (wizard != null)
+ wizard.accountRemoved(protocolProvider);
+ }
+ }
+ }
+ }
+ else if (sourceButton.equals(editButton))
+ {
+ Account account = accountList.getSelectedAccount();
+
+ if (account == null)
+ return;
+
+ AccountRegWizardContainerImpl wizard =
+ (AccountRegWizardContainerImpl) GuiActivator.getUIService()
+ .getAccountRegWizardContainer();
+
+ AccountRegistrationWizard protocolWizard =
+ wizard.getProtocolWizard(account.getProtocolProvider());
+
+ ResourceManagementService resources = GuiActivator.getResources();
+ if (protocolWizard != null)
+ {
+ wizard.setTitle(resources.getI18NString(
+ "service.gui.ACCOUNT_REGISTRATION_WIZARD"));
+
+ wizard.modifyAccount(account.getProtocolProvider());
+ wizard.showDialog(false);
+ }
+ else
+ {
+ // There is no wizard for this account - just show an error
+ // dialog:
+ String title = resources.getI18NString("service.gui.ERROR");
+ String message =
+ resources.getI18NString("service.gui.EDIT_NOT_SUPPORTED");
+ ErrorDialog dialog = new ErrorDialog(null, title, message);
+ dialog.setVisible(true);
+ }
+ }
+ }
+
+ /**
+ * Returns the edit button.
+ *
+ * @return the edit button
+ */
+ public JButton getEditButton()
+ {
+ return editButton;
+ }
+
+ /**
+ * Updates enabled states of the buttons of this
+ * <tt>AccountsConfigurationPanel</tt> to reflect their applicability to the
+ * current selection in <tt>accountList</tt>.
+ */
+ private void updateButtons()
+ {
+ if(!SwingUtilities.isEventDispatchThread())
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ updateButtons();
+ }
+ });
+ return;
+ }
+
+ Account account = accountList.getSelectedAccount();
+ boolean enabled = (account != null);
+
+ editButton.setEnabled(enabled && account.isEnabled());
+ removeButton.setEnabled(enabled);
+ }
+
+ /**
+ * Implements ListSelectionListener#valueChanged(ListSelectionEvent).
+ * @param e the <tt>ListSelectionEvent</tt> that notified us
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ if (!e.getValueIsAdjusting())
+ updateButtons();
+ }
+
+ /**
+ * This method gets called when a property is changed.
+ *
+ * @param evt A PropertyChangeEvent object describing the event source
+ * and the property that has changed.
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // update buttons whenever an account changes its state
+ updateButtons();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
index 697a3fc..de63f79 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,619 +15,619 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-
-/**
- * The dialog created for a given call.
- *
- * @author Yana Stamcheva
- * @author Adam Netocny
- * @author Lyubomir Marinov
- */
-public class CallDialog
- extends SIPCommFrame
- implements CallContainer,
- CallTitleListener
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * Enabling force minimized mode will always open call dialog minimized.
- * The call dialog still can be shown, but by default it will be minimized.
- */
- private static final String FORCE_MINIMIZED_MODE
- = "net.java.sip.communicator.impl.gui.main.call.FORCE_MINIMIZED_MODE";
-
- /**
- * Finds a <tt>Container</tt> which is an ancestor of a specific
- * <tt>Component</tt>, has a set <tt>preferredSize</tt> and is closest to
- * the specified <tt>Component</tt> up the ancestor hierarchy.
- *
- * @param component the <tt>Component</tt> whose ancestor hierarchy is to be
- * searched upwards
- * @return a <tt>Container</tt>, if any, which is an ancestor of the
- * specified <tt>component</tt>, has a set <tt>preferredSize</tt> and is
- * closest to the specified <tt>component</tt> up the ancestor hierarchy
- */
- private static Container findClosestAncestorWithSetPreferredSize(
- Component component)
- {
- if ((component instanceof Container) && component.isPreferredSizeSet())
- return (Container) component;
- else
- {
- Container parent;
-
- while ((parent = component.getParent()) != null)
- {
- if (parent.isPreferredSizeSet())
- return parent;
- else
- component = parent;
- }
- return null;
- }
- }
-
- /**
- * The panel, where all call components are added.
- */
- private CallPanel callPanel;
-
- private final WindowStateListener windowStateListener
- = new WindowStateListener()
- {
- public void windowStateChanged(WindowEvent ev)
- {
- switch (ev.getID())
- {
- case WindowEvent.WINDOW_DEACTIVATED:
- case WindowEvent.WINDOW_ICONIFIED:
- case WindowEvent.WINDOW_LOST_FOCUS:
- setFullScreen(false);
- break;
- }
- }
- };
-
- /**
- * Creates a <tt>CallDialog</tt> by specifying the underlying call panel.
- */
- public CallDialog()
- {
- super(true, false);
-
- setMinimumSize(new Dimension(360, 300));
- }
-
- /**
- * Adds a call panel.
- *
- * @param callPanel the call panel to add to this dialog
- */
- public void addCallPanel(CallPanel callPanel)
- {
- this.callPanel = callPanel;
-
- getContentPane().add(callPanel);
-
- callPanel.addCallTitleListener(this);
- setTitle(callPanel.getCallTitle());
-
- if (!isVisible())
- {
- pack();
-
- // checks whether we need to open the call dialog in minimized mode
- if(GuiActivator.getConfigurationService()
- .getBoolean(FORCE_MINIMIZED_MODE, false))
- {
- setState(ICONIFIED);
- }
- setVisible(true);
- }
- }
-
- /**
- * Called when the title of the given <tt>CallPanel</tt> changes.
- *
- * @param callPanel the <tt>CallPanel</tt>, which title has changed
- */
- public void callTitleChanged(CallPanel callPanel)
- {
- if (this.callPanel.equals(callPanel))
- setTitle(callPanel.getCallTitle());
- }
-
- /**
- * {@inheritDoc}
- *
- * Hang ups the call/telephony conference depicted by this
- * <tt>CallDialog</tt> on close.
- */
- @Override
- protected void close(boolean escape)
- {
- if (escape)
- {
- /*
- * In full-screen mode, ESC does not close this CallDialog but exits
- * from full-screen to windowed mode.
- */
- if (isFullScreen())
- {
- setFullScreen(false);
- return;
- }
- }
- else
- {
- /*
- * If the window has been closed by clicking the X button or
- * pressing the key combination corresponding to the same button we
- * close the window first and then perform all hang up operations.
- */
-
- callPanel.disposeCallInfoFrame();
- // We hide the window here. It will be disposed when the call has
- // been ended.
- setVisible(false);
- }
-
- // Then perform hang up operations.
- callPanel.actionPerformedOnHangupButton(escape);
- }
-
- /**
- * {@inheritDoc}
- *
- * The delay implemented by <tt>CallDialog</tt> is 5 seconds.
- */
- public void close(final CallPanel callPanel, boolean delay)
- {
- if (this.callPanel.equals(callPanel))
- {
- if (delay)
- {
- Timer timer
- = new Timer(
- 5000,
- new ActionListener()
- {
- public void actionPerformed(ActionEvent ev)
- {
- dispose();
- }
- });
-
- timer.setRepeats(false);
- timer.start();
- }
- else
- {
- dispose();
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * <tt>CallDialog</tt> prepares the <tt>CallPanel</tt> it contains for
- * garbage collection.
- */
- @Override
- public void dispose()
- {
- super.dispose();
-
- /*
- * Technically, CallManager adds/removes the callPanel to/from this
- * instance. It may want to just move it to another CallContainer so it
- * does not sound right that we are disposing of it. But we do not have
- * such a case at this time so try to reduce the risk of memory leaks.
- */
- if (this.callPanel != null)
- {
- callPanel.disposeCallInfoFrame();
- callPanel.dispose();
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * Attempts to adjust the size of this <tt>Frame</tt> as requested in the
- * AWT event dispatching thread.
- * <p>
- * The method may be executed on the AWT event dispatching thread only
- * because whoever is making the decision to request an adjustment of the
- * Frame size in relation to a AWT Component should be analyzing that same
- * Component in the AWT event dispatching thread only.
- * </p>
- *
- * @throws RuntimeException if the method is not called on the AWT event
- * dispatching thread
- */
- public void ensureSize(Component component, int width, int height)
- {
- CallManager.assertIsEventDispatchingThread();
-
- Frame frame = CallPeerRendererUtils.getFrame(component);
-
- if (frame == null)
- return;
- else if ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH)
- == Frame.MAXIMIZED_BOTH)
- {
- /*
- * Forcing the size of a Component which is displayed in a maximized
- * window does not sound like anything we want to do.
- */
- return;
- }
- else if (frame.equals(
- frame.getGraphicsConfiguration().getDevice()
- .getFullScreenWindow()))
- {
- /*
- * Forcing the size of a Component which is displayed in a
- * full-screen window does not sound like anything we want to do.
- */
- return;
- }
- else if (!frame.equals(this))
- {
- /* This Frame will try to adjust only its own size. */
- return;
- }
- else if ((component.getHeight() >= height)
- && (component.getWidth() >= width))
- {
- /*
- * We will only enlarge the frame size. If the component has already
- * been given at least what it is requesting, do not enlarge the
- * frame size because the whole calculation is prone to inaccuracy.
- */
- return;
- }
- else
- {
- /*
- * If there is no callPanel, it is unlikely that this CallDialog
- * will be asked to ensureSize. Anyway, support the scenario just in
- * case. In light of the absence of a callPanel to guide this
- * CallDialog about the preferred size, we do not have much of a
- * choice but to trust the method arguments.
- */
- if (callPanel != null)
- {
- /*
- * If there is a callPanel, we are likely to get a much better
- * estimation about the preferred size by asking the callPanel
- * rather than by trusting the method arguments. For example,
- * the visual Component displaying the video streaming from the
- * local user/peer to the remote peer(s) will think that its
- * preferred size is the one to base this Frame's size on but
- * that may be misleading because the local video may not be
- * displayed with its preferred size even if this Frame's size
- * will accommodate it.
- */
- /*
- * Just asking the callPanel about its preferredSize would've
- * been terrificly great. Unfortunately, that is presently
- * futile because the callPanel may have a preferredSize while
- * we are still required to display visual Components displaying
- * video in their non-scaled size. The same goes for any
- * Container which is an ancestor of the specified component.
- */
- Container ancestor
- = findClosestAncestorWithSetPreferredSize(component);
-
- if (ancestor == null)
- ancestor = callPanel;
- /*
- * If the ancestor has a forced preferredSize, its LayoutManager
- * may be able to give a good enough estimation.
- */
- if (ancestor.isPreferredSizeSet())
- {
- LayoutManager ancestorLayout = ancestor.getLayout();
-
- if (ancestorLayout != null)
- {
- Dimension preferredLayoutSize
- = ancestorLayout.preferredLayoutSize(ancestor);
-
- if (preferredLayoutSize != null)
- {
- component = ancestor;
- width = preferredLayoutSize.width;
- height = preferredLayoutSize.height;
- }
- }
- }
- else
- {
- /*
- * If the ancestor doesn't have a preferredSize forced, then
- * we may think that it will calculate an appropriate
- * preferredSize itself.
- */
- Dimension prefSize = ancestor.getPreferredSize();
-
- if (prefSize != null)
- {
- component = ancestor;
- width = prefSize.width;
- height = prefSize.height;
- }
- }
- }
-
- /*
- * If the component (which may be an ancestor of the Component
- * specified as an argument to the ensureSize method at this point)
- * has not been given a size, we will make a mistake if we try to
- * use it for the purposes of determining how much this Frame is to
- * be enlarged.
- */
- Dimension componentSize = component.getSize();
-
- if ((componentSize.width < 1) || (componentSize.height < 1))
- return;
-
- Dimension frameSize = frame.getSize();
- int newFrameWidth = frameSize.width + width - componentSize.width;
- int newFrameHeight
- = frameSize.height + height - componentSize.height;
-
- // Respect the minimum size.
- Dimension minSize = frame.getMinimumSize();
-
- if (newFrameWidth < minSize.width)
- newFrameWidth = minSize.width;
- if (newFrameHeight < minSize.height)
- newFrameHeight = minSize.height;
-
- // Don't get bigger than the screen.
- Rectangle screenBounds
- = frame.getGraphicsConfiguration().getBounds();
-
- if (newFrameWidth > screenBounds.width)
- newFrameWidth = screenBounds.width;
- if (newFrameHeight > screenBounds.height)
- newFrameHeight = screenBounds.height;
-
- /*
- * If we're going to make too small a change, don't even bother.
- * Besides, we don't want some weird recursive resizing.
- * Additionally, do not reduce the Frame size.
- */
- boolean changeWidth = ((newFrameWidth - frameSize.width) > 1);
- boolean changeHeight = ((newFrameHeight - frameSize.height) > 1);
-
- if (changeWidth || changeHeight)
- {
- if (!changeWidth)
- newFrameWidth = frameSize.width;
- else if (!changeHeight)
- newFrameHeight = frameSize.height;
-
- /*
- * The latest requirement with respect to the behavior upon
- * resizing is to center the Frame.
- */
- int newFrameX
- = screenBounds.x
- + (screenBounds.width - newFrameWidth) / 2;
- int newFrameY
- = screenBounds.y
- + (screenBounds.height - newFrameHeight) / 2;
-
- // Do not let the top left go out of the screen.
- if (newFrameX < screenBounds.x)
- newFrameX = screenBounds.x;
- if (newFrameY < screenBounds.y)
- newFrameY = screenBounds.y;
-
- frame.setBounds(
- newFrameX, newFrameY,
- newFrameWidth, newFrameHeight);
-
- /*
- * Make sure that the component which originally requested the
- * update to the size of the frame realizes the change as soon
- * as possible; otherwise, it may request yet another update.
- */
- if (frame.isDisplayable())
- {
- if (frame.isValid())
- frame.doLayout();
- else
- frame.validate();
- frame.repaint();
- }
- else
- frame.doLayout();
- }
- }
- }
-
- /**
- * Returns the frame of the call window.
- *
- * @return the frame of the call window
- */
- public JFrame getFrame()
- {
- return this;
- }
-
- /**
- * Overrides getMinimumSize and checks the minimum size that
- * is needed to display buttons and use it for minimum size if
- * needed.
- * @return minimum size.
- */
- @Override
- public Dimension getMinimumSize()
- {
- Dimension minSize = super.getMinimumSize();
-
- if(callPanel != null)
- {
- int minButtonWidth = callPanel.getMinimumButtonWidth();
-
- if(minButtonWidth > minSize.getWidth())
- minSize = new Dimension(minButtonWidth, 300);
- }
-
- return minSize;
- }
-
- /**
- * Indicates if the given <tt>callPanel</tt> is currently visible.
- *
- * @param callPanel the <tt>CallPanel</tt>, for which we verify
- * @return <tt>true</tt> if the given call container is visible in this
- * call window, otherwise - <tt>false</tt>
- */
- public boolean isCallVisible(CallPanel callPanel)
- {
- return this.callPanel.equals(callPanel) ? isVisible() : false;
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isFullScreen()
- {
- return isFullScreen(getFrame());
- }
-
- /**
- * Determines whether a specific <tt>Window</tt> is displayed in full-screen
- * mode.
- *
- * @param window the <tt>Window</tt> to be checked whether it is displayed
- * in full-screen mode
- * @return <tt>true</tt> if the specified <tt>window</tt> is displayed in
- * full-screen mode; otherwise, <tt>false</tt>
- */
- public static boolean isFullScreen(Window window)
- {
- GraphicsConfiguration graphicsConfiguration
- = window.getGraphicsConfiguration();
-
- if (graphicsConfiguration != null)
- {
- GraphicsDevice device = graphicsConfiguration.getDevice();
-
- if (device != null)
- return window.equals(device.getFullScreenWindow());
- }
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- public void setFullScreen(boolean fullScreen)
- {
- GraphicsConfiguration graphicsConfiguration
- = getGraphicsConfiguration();
-
- if (graphicsConfiguration != null)
- {
- GraphicsDevice device = graphicsConfiguration.getDevice();
-
- if (device != null)
- {
- boolean thisIsFullScreen = equals(device.getFullScreenWindow());
- boolean firePropertyChange = false;
- boolean setVisible = isVisible();
-
- try
- {
- if (fullScreen)
- {
- if (!thisIsFullScreen)
- {
- /*
- * XXX The setUndecorated method will only work if
- * this Window is not displayable.
- */
- windowDispose();
- setUndecorated(true);
-
- device.setFullScreenWindow(this);
- firePropertyChange = true;
- }
- }
- else if (thisIsFullScreen)
- {
- /*
- * XXX The setUndecorated method will only work if this
- * Window is not displayable.
- */
- windowDispose();
- setUndecorated(false);
-
- device.setFullScreenWindow(null);
- firePropertyChange = true;
- }
-
- if (firePropertyChange)
- {
- if (fullScreen)
- {
- addWindowStateListener(windowStateListener);
-
- /*
- * If full-screen mode, a black background is the
- * most common.
- */
- getContentPane().setBackground(Color.BLACK);
- }
- else
- {
- removeWindowStateListener(windowStateListener);
-
- /*
- * In windowed mode, a system-defined background is
- * the most common.
- */
- getContentPane().setBackground(null);
- }
-
- firePropertyChange(
- PROP_FULL_SCREEN,
- thisIsFullScreen,
- fullScreen);
- }
- }
- finally
- {
- /*
- * Regardless of whether this Window successfully entered or
- * exited full-screen mode, make sure that remains visible.
- */
- if (setVisible)
- setVisible(true);
- }
- }
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+
+/**
+ * The dialog created for a given call.
+ *
+ * @author Yana Stamcheva
+ * @author Adam Netocny
+ * @author Lyubomir Marinov
+ */
+public class CallDialog
+ extends SIPCommFrame
+ implements CallContainer,
+ CallTitleListener
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Enabling force minimized mode will always open call dialog minimized.
+ * The call dialog still can be shown, but by default it will be minimized.
+ */
+ private static final String FORCE_MINIMIZED_MODE
+ = "net.java.sip.communicator.impl.gui.main.call.FORCE_MINIMIZED_MODE";
+
+ /**
+ * Finds a <tt>Container</tt> which is an ancestor of a specific
+ * <tt>Component</tt>, has a set <tt>preferredSize</tt> and is closest to
+ * the specified <tt>Component</tt> up the ancestor hierarchy.
+ *
+ * @param component the <tt>Component</tt> whose ancestor hierarchy is to be
+ * searched upwards
+ * @return a <tt>Container</tt>, if any, which is an ancestor of the
+ * specified <tt>component</tt>, has a set <tt>preferredSize</tt> and is
+ * closest to the specified <tt>component</tt> up the ancestor hierarchy
+ */
+ private static Container findClosestAncestorWithSetPreferredSize(
+ Component component)
+ {
+ if ((component instanceof Container) && component.isPreferredSizeSet())
+ return (Container) component;
+ else
+ {
+ Container parent;
+
+ while ((parent = component.getParent()) != null)
+ {
+ if (parent.isPreferredSizeSet())
+ return parent;
+ else
+ component = parent;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * The panel, where all call components are added.
+ */
+ private CallPanel callPanel;
+
+ private final WindowStateListener windowStateListener
+ = new WindowStateListener()
+ {
+ public void windowStateChanged(WindowEvent ev)
+ {
+ switch (ev.getID())
+ {
+ case WindowEvent.WINDOW_DEACTIVATED:
+ case WindowEvent.WINDOW_ICONIFIED:
+ case WindowEvent.WINDOW_LOST_FOCUS:
+ setFullScreen(false);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Creates a <tt>CallDialog</tt> by specifying the underlying call panel.
+ */
+ public CallDialog()
+ {
+ super(true, false);
+
+ setMinimumSize(new Dimension(360, 300));
+ }
+
+ /**
+ * Adds a call panel.
+ *
+ * @param callPanel the call panel to add to this dialog
+ */
+ public void addCallPanel(CallPanel callPanel)
+ {
+ this.callPanel = callPanel;
+
+ getContentPane().add(callPanel);
+
+ callPanel.addCallTitleListener(this);
+ setTitle(callPanel.getCallTitle());
+
+ if (!isVisible())
+ {
+ pack();
+
+ // checks whether we need to open the call dialog in minimized mode
+ if(GuiActivator.getConfigurationService()
+ .getBoolean(FORCE_MINIMIZED_MODE, false))
+ {
+ setState(ICONIFIED);
+ }
+ setVisible(true);
+ }
+ }
+
+ /**
+ * Called when the title of the given <tt>CallPanel</tt> changes.
+ *
+ * @param callPanel the <tt>CallPanel</tt>, which title has changed
+ */
+ public void callTitleChanged(CallPanel callPanel)
+ {
+ if (this.callPanel.equals(callPanel))
+ setTitle(callPanel.getCallTitle());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Hang ups the call/telephony conference depicted by this
+ * <tt>CallDialog</tt> on close.
+ */
+ @Override
+ protected void close(boolean escape)
+ {
+ if (escape)
+ {
+ /*
+ * In full-screen mode, ESC does not close this CallDialog but exits
+ * from full-screen to windowed mode.
+ */
+ if (isFullScreen())
+ {
+ setFullScreen(false);
+ return;
+ }
+ }
+ else
+ {
+ /*
+ * If the window has been closed by clicking the X button or
+ * pressing the key combination corresponding to the same button we
+ * close the window first and then perform all hang up operations.
+ */
+
+ callPanel.disposeCallInfoFrame();
+ // We hide the window here. It will be disposed when the call has
+ // been ended.
+ setVisible(false);
+ }
+
+ // Then perform hang up operations.
+ callPanel.actionPerformedOnHangupButton(escape);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The delay implemented by <tt>CallDialog</tt> is 5 seconds.
+ */
+ public void close(final CallPanel callPanel, boolean delay)
+ {
+ if (this.callPanel.equals(callPanel))
+ {
+ if (delay)
+ {
+ Timer timer
+ = new Timer(
+ 5000,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent ev)
+ {
+ dispose();
+ }
+ });
+
+ timer.setRepeats(false);
+ timer.start();
+ }
+ else
+ {
+ dispose();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <tt>CallDialog</tt> prepares the <tt>CallPanel</tt> it contains for
+ * garbage collection.
+ */
+ @Override
+ public void dispose()
+ {
+ super.dispose();
+
+ /*
+ * Technically, CallManager adds/removes the callPanel to/from this
+ * instance. It may want to just move it to another CallContainer so it
+ * does not sound right that we are disposing of it. But we do not have
+ * such a case at this time so try to reduce the risk of memory leaks.
+ */
+ if (this.callPanel != null)
+ {
+ callPanel.disposeCallInfoFrame();
+ callPanel.dispose();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Attempts to adjust the size of this <tt>Frame</tt> as requested in the
+ * AWT event dispatching thread.
+ * <p>
+ * The method may be executed on the AWT event dispatching thread only
+ * because whoever is making the decision to request an adjustment of the
+ * Frame size in relation to a AWT Component should be analyzing that same
+ * Component in the AWT event dispatching thread only.
+ * </p>
+ *
+ * @throws RuntimeException if the method is not called on the AWT event
+ * dispatching thread
+ */
+ public void ensureSize(Component component, int width, int height)
+ {
+ CallManager.assertIsEventDispatchingThread();
+
+ Frame frame = CallPeerRendererUtils.getFrame(component);
+
+ if (frame == null)
+ return;
+ else if ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH)
+ == Frame.MAXIMIZED_BOTH)
+ {
+ /*
+ * Forcing the size of a Component which is displayed in a maximized
+ * window does not sound like anything we want to do.
+ */
+ return;
+ }
+ else if (frame.equals(
+ frame.getGraphicsConfiguration().getDevice()
+ .getFullScreenWindow()))
+ {
+ /*
+ * Forcing the size of a Component which is displayed in a
+ * full-screen window does not sound like anything we want to do.
+ */
+ return;
+ }
+ else if (!frame.equals(this))
+ {
+ /* This Frame will try to adjust only its own size. */
+ return;
+ }
+ else if ((component.getHeight() >= height)
+ && (component.getWidth() >= width))
+ {
+ /*
+ * We will only enlarge the frame size. If the component has already
+ * been given at least what it is requesting, do not enlarge the
+ * frame size because the whole calculation is prone to inaccuracy.
+ */
+ return;
+ }
+ else
+ {
+ /*
+ * If there is no callPanel, it is unlikely that this CallDialog
+ * will be asked to ensureSize. Anyway, support the scenario just in
+ * case. In light of the absence of a callPanel to guide this
+ * CallDialog about the preferred size, we do not have much of a
+ * choice but to trust the method arguments.
+ */
+ if (callPanel != null)
+ {
+ /*
+ * If there is a callPanel, we are likely to get a much better
+ * estimation about the preferred size by asking the callPanel
+ * rather than by trusting the method arguments. For example,
+ * the visual Component displaying the video streaming from the
+ * local user/peer to the remote peer(s) will think that its
+ * preferred size is the one to base this Frame's size on but
+ * that may be misleading because the local video may not be
+ * displayed with its preferred size even if this Frame's size
+ * will accommodate it.
+ */
+ /*
+ * Just asking the callPanel about its preferredSize would've
+ * been terrificly great. Unfortunately, that is presently
+ * futile because the callPanel may have a preferredSize while
+ * we are still required to display visual Components displaying
+ * video in their non-scaled size. The same goes for any
+ * Container which is an ancestor of the specified component.
+ */
+ Container ancestor
+ = findClosestAncestorWithSetPreferredSize(component);
+
+ if (ancestor == null)
+ ancestor = callPanel;
+ /*
+ * If the ancestor has a forced preferredSize, its LayoutManager
+ * may be able to give a good enough estimation.
+ */
+ if (ancestor.isPreferredSizeSet())
+ {
+ LayoutManager ancestorLayout = ancestor.getLayout();
+
+ if (ancestorLayout != null)
+ {
+ Dimension preferredLayoutSize
+ = ancestorLayout.preferredLayoutSize(ancestor);
+
+ if (preferredLayoutSize != null)
+ {
+ component = ancestor;
+ width = preferredLayoutSize.width;
+ height = preferredLayoutSize.height;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * If the ancestor doesn't have a preferredSize forced, then
+ * we may think that it will calculate an appropriate
+ * preferredSize itself.
+ */
+ Dimension prefSize = ancestor.getPreferredSize();
+
+ if (prefSize != null)
+ {
+ component = ancestor;
+ width = prefSize.width;
+ height = prefSize.height;
+ }
+ }
+ }
+
+ /*
+ * If the component (which may be an ancestor of the Component
+ * specified as an argument to the ensureSize method at this point)
+ * has not been given a size, we will make a mistake if we try to
+ * use it for the purposes of determining how much this Frame is to
+ * be enlarged.
+ */
+ Dimension componentSize = component.getSize();
+
+ if ((componentSize.width < 1) || (componentSize.height < 1))
+ return;
+
+ Dimension frameSize = frame.getSize();
+ int newFrameWidth = frameSize.width + width - componentSize.width;
+ int newFrameHeight
+ = frameSize.height + height - componentSize.height;
+
+ // Respect the minimum size.
+ Dimension minSize = frame.getMinimumSize();
+
+ if (newFrameWidth < minSize.width)
+ newFrameWidth = minSize.width;
+ if (newFrameHeight < minSize.height)
+ newFrameHeight = minSize.height;
+
+ // Don't get bigger than the screen.
+ Rectangle screenBounds
+ = frame.getGraphicsConfiguration().getBounds();
+
+ if (newFrameWidth > screenBounds.width)
+ newFrameWidth = screenBounds.width;
+ if (newFrameHeight > screenBounds.height)
+ newFrameHeight = screenBounds.height;
+
+ /*
+ * If we're going to make too small a change, don't even bother.
+ * Besides, we don't want some weird recursive resizing.
+ * Additionally, do not reduce the Frame size.
+ */
+ boolean changeWidth = ((newFrameWidth - frameSize.width) > 1);
+ boolean changeHeight = ((newFrameHeight - frameSize.height) > 1);
+
+ if (changeWidth || changeHeight)
+ {
+ if (!changeWidth)
+ newFrameWidth = frameSize.width;
+ else if (!changeHeight)
+ newFrameHeight = frameSize.height;
+
+ /*
+ * The latest requirement with respect to the behavior upon
+ * resizing is to center the Frame.
+ */
+ int newFrameX
+ = screenBounds.x
+ + (screenBounds.width - newFrameWidth) / 2;
+ int newFrameY
+ = screenBounds.y
+ + (screenBounds.height - newFrameHeight) / 2;
+
+ // Do not let the top left go out of the screen.
+ if (newFrameX < screenBounds.x)
+ newFrameX = screenBounds.x;
+ if (newFrameY < screenBounds.y)
+ newFrameY = screenBounds.y;
+
+ frame.setBounds(
+ newFrameX, newFrameY,
+ newFrameWidth, newFrameHeight);
+
+ /*
+ * Make sure that the component which originally requested the
+ * update to the size of the frame realizes the change as soon
+ * as possible; otherwise, it may request yet another update.
+ */
+ if (frame.isDisplayable())
+ {
+ if (frame.isValid())
+ frame.doLayout();
+ else
+ frame.validate();
+ frame.repaint();
+ }
+ else
+ frame.doLayout();
+ }
+ }
+ }
+
+ /**
+ * Returns the frame of the call window.
+ *
+ * @return the frame of the call window
+ */
+ public JFrame getFrame()
+ {
+ return this;
+ }
+
+ /**
+ * Overrides getMinimumSize and checks the minimum size that
+ * is needed to display buttons and use it for minimum size if
+ * needed.
+ * @return minimum size.
+ */
+ @Override
+ public Dimension getMinimumSize()
+ {
+ Dimension minSize = super.getMinimumSize();
+
+ if(callPanel != null)
+ {
+ int minButtonWidth = callPanel.getMinimumButtonWidth();
+
+ if(minButtonWidth > minSize.getWidth())
+ minSize = new Dimension(minButtonWidth, 300);
+ }
+
+ return minSize;
+ }
+
+ /**
+ * Indicates if the given <tt>callPanel</tt> is currently visible.
+ *
+ * @param callPanel the <tt>CallPanel</tt>, for which we verify
+ * @return <tt>true</tt> if the given call container is visible in this
+ * call window, otherwise - <tt>false</tt>
+ */
+ public boolean isCallVisible(CallPanel callPanel)
+ {
+ return this.callPanel.equals(callPanel) ? isVisible() : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isFullScreen()
+ {
+ return isFullScreen(getFrame());
+ }
+
+ /**
+ * Determines whether a specific <tt>Window</tt> is displayed in full-screen
+ * mode.
+ *
+ * @param window the <tt>Window</tt> to be checked whether it is displayed
+ * in full-screen mode
+ * @return <tt>true</tt> if the specified <tt>window</tt> is displayed in
+ * full-screen mode; otherwise, <tt>false</tt>
+ */
+ public static boolean isFullScreen(Window window)
+ {
+ GraphicsConfiguration graphicsConfiguration
+ = window.getGraphicsConfiguration();
+
+ if (graphicsConfiguration != null)
+ {
+ GraphicsDevice device = graphicsConfiguration.getDevice();
+
+ if (device != null)
+ return window.equals(device.getFullScreenWindow());
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setFullScreen(boolean fullScreen)
+ {
+ GraphicsConfiguration graphicsConfiguration
+ = getGraphicsConfiguration();
+
+ if (graphicsConfiguration != null)
+ {
+ GraphicsDevice device = graphicsConfiguration.getDevice();
+
+ if (device != null)
+ {
+ boolean thisIsFullScreen = equals(device.getFullScreenWindow());
+ boolean firePropertyChange = false;
+ boolean setVisible = isVisible();
+
+ try
+ {
+ if (fullScreen)
+ {
+ if (!thisIsFullScreen)
+ {
+ /*
+ * XXX The setUndecorated method will only work if
+ * this Window is not displayable.
+ */
+ windowDispose();
+ setUndecorated(true);
+
+ device.setFullScreenWindow(this);
+ firePropertyChange = true;
+ }
+ }
+ else if (thisIsFullScreen)
+ {
+ /*
+ * XXX The setUndecorated method will only work if this
+ * Window is not displayable.
+ */
+ windowDispose();
+ setUndecorated(false);
+
+ device.setFullScreenWindow(null);
+ firePropertyChange = true;
+ }
+
+ if (firePropertyChange)
+ {
+ if (fullScreen)
+ {
+ addWindowStateListener(windowStateListener);
+
+ /*
+ * If full-screen mode, a black background is the
+ * most common.
+ */
+ getContentPane().setBackground(Color.BLACK);
+ }
+ else
+ {
+ removeWindowStateListener(windowStateListener);
+
+ /*
+ * In windowed mode, a system-defined background is
+ * the most common.
+ */
+ getContentPane().setBackground(null);
+ }
+
+ firePropertyChange(
+ PROP_FULL_SCREEN,
+ thisIsFullScreen,
+ fullScreen);
+ }
+ }
+ finally
+ {
+ /*
+ * Regardless of whether this Window successfully entered or
+ * exited full-screen mode, make sure that remains visible.
+ */
+ if (setVisible)
+ setVisible(true);
+ }
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
index 095b3dd..d1c0a42 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
@@ -130,6 +130,14 @@ public class CallHistoryButton
}
/**
+ * Does nothing because this class causes the clearing.
+ */
+ @Override
+ public void notificationCleared(UINotification notification)
+ {
+ }
+
+ /**
* Sets the history view.
*/
private void setHistoryView()
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java b/src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java
index ad8f6fd..872e861 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java
@@ -34,6 +34,7 @@ import net.java.sip.communicator.util.*;
import org.ice4j.ice.*;
import org.jitsi.service.neomedia.*;
+import org.jitsi.service.neomedia.stats.*;
import org.jitsi.service.resources.*;
import org.jitsi.util.*;
@@ -469,7 +470,7 @@ public class CallInfoFrame
StringBuffer stringBuffer,
MediaType mediaType)
{
- MediaStreamStats mediaStreamStats
+ MediaStreamStats2 mediaStreamStats
= mediaStream.getMediaStreamStats();
if(mediaStreamStats == null)
@@ -624,18 +625,19 @@ public class CallInfoFrame
resources.getI18NString(
"service.gui.callinfo.BANDWITH"),
"&darr; "
- + (int) mediaStreamStats.getDownloadRateKiloBitPerSec()
+ + (int) mediaStreamStats.getReceiveStats().getBitrate()/1024
+ " Kbps "
+ " &uarr; "
- + (int) mediaStreamStats.getUploadRateKiloBitPerSec()
+ + (int) mediaStreamStats.getSendStats().getBitrate()/1024
+ " Kbps"));
stringBuffer.append(
getLineString(
resources.getI18NString("service.gui.callinfo.LOSS_RATE"),
- "&darr; " + (int) mediaStreamStats.getDownloadPercentLoss()
+ "&darr;"
+ + (int) (mediaStreamStats.getReceiveStats().getLossRate() * 100)
+ "% &uarr; "
- + (int) mediaStreamStats.getUploadPercentLoss()
+ + (int) (mediaStreamStats.getSendStats().getLossRate() * 100)
+ "%"));
stringBuffer.append(
getLineString(
@@ -668,21 +670,23 @@ public class CallInfoFrame
+ mediaStreamStats.getPacketQueueCountPackets() + "/"
+ mediaStreamStats.getPacketQueueSize() + " packets"));
- long rttMs = mediaStreamStats.getRttMs();
- if(rttMs != -1)
+ long sendRttMs = mediaStreamStats.getSendStats().getRtt();
+ long recvRttMs = mediaStreamStats.getReceiveStats().getRtt();
+ if(recvRttMs != -1 || sendRttMs != -1)
{
stringBuffer.append(
getLineString(resources.getI18NString(
"service.gui.callinfo.RTT"),
- rttMs + " ms"));
+ (recvRttMs != -1 ? "&darr; " + recvRttMs + " ms" : "") +
+ (sendRttMs != -1 ? "&uarr; " + sendRttMs + " ms" : "")));
}
stringBuffer.append(
getLineString(resources.getI18NString(
"service.gui.callinfo.JITTER"),
- "&darr; " + (int) mediaStreamStats.getDownloadJitterMs()
+ "&darr; " + (int) mediaStreamStats.getReceiveStats().getJitter()
+ " ms &uarr; "
- + (int) mediaStreamStats.getUploadJitterMs() + " ms"));
+ + (int) mediaStreamStats.getSendStats().getJitter() + " ms"));
}
/**
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java b/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
index 54f48ee..6235e1c 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,276 +15,276 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import java.awt.datatransfer.*;
-import java.awt.im.*;
-import java.io.*;
-import java.util.*;
-
-import javax.swing.*;
-
-import org.jitsi.service.resources.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.contactlist.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * A <tt>TransferHandler</tt> that handles dropping of <tt>UIContact</tt>s or
- * <tt>String</tt> addresses on a <tt>CallConference</tt>. Dropping such data on
- * the <tt>CallDialog</tt> will turn a one-to-one <tt>Call</tt> into a telephony
- * conference.
- *
- * @author Yana Stamcheva
- */
-public class CallTransferHandler
- extends ExtendedTransferHandler
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * The data flavor used when transferring <tt>UIContact</tt>s.
- */
- protected static final DataFlavor uiContactDataFlavor
- = new DataFlavor(UIContact.class, "UIContact");
-
- /**
- * The logger.
- */
- private static final Logger logger
- = Logger.getLogger(CallTransferHandler.class);
-
- /**
- * The <tt>CallConference</tt> into which the dropped callees are to be
- * invited.
- */
- private final CallConference callConference;
-
- /**
- * Initializes a new <tt>CallTransferHandler</tt> instance which is to
- * invite dropped callees to a telephony conference specified by a specific
- * <tt>Call</tt> which participates in it.
- *
- * @param call the <tt>Call</tt> which specifies the telephony conference to
- * which dropped callees are to be invited
- */
- public CallTransferHandler(Call call)
- {
- this(call.getConference());
- }
-
- /**
- * Initializes a new <tt>CallTransferHandler</tt> instance which is to
- * invite dropped callees to a specific <tt>CallConference</tt>.
- *
- * @param callConference the <tt>CallConference</tt> to which dropped
- * callees are to be invited
- */
- public CallTransferHandler(CallConference callConference)
- {
- this.callConference = callConference;
- }
-
- /**
- * Indicates whether a component will accept an import of the given
- * set of data flavors prior to actually attempting to import it. We return
- * <tt>true</tt> to indicate that the transfer with at least one of the
- * given flavors would work and <tt>false</tt> to reject the transfer.
- * <p>
- * @param comp component
- * @param flavor the data formats available
- * @return true if the data can be inserted into the component, false
- * otherwise
- * @throws NullPointerException if <code>support</code> is {@code null}
- */
- @Override
- public boolean canImport(JComponent comp, DataFlavor[] flavor)
- {
- for (DataFlavor f : flavor)
- {
- if (f.equals(DataFlavor.stringFlavor)
- || f.equals(uiContactDataFlavor))
- {
- return (comp instanceof JPanel);
- }
- }
- return false;
- }
-
- /**
- * Handles transfers to the chat panel from the clip board or a
- * DND drop operation. The <tt>Transferable</tt> parameter contains the
- * data that needs to be imported.
- * <p>
- * @param comp the component to receive the transfer;
- * @param t the data to import
- * @return true if the data was inserted into the component and false
- * otherwise
- */
- @Override
- public boolean importData(JComponent comp, Transferable t)
- {
- String callee = null;
- ProtocolProviderService provider = null;
-
- if (t.isDataFlavorSupported(uiContactDataFlavor))
- {
- Object o = null;
-
- try
- {
- o = t.getTransferData(uiContactDataFlavor);
- }
- catch (UnsupportedFlavorException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to drop meta contact.", e);
- }
- catch (IOException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to drop meta contact.", e);
- }
-
- if (o instanceof ContactNode)
- {
- UIContact uiContact = ((ContactNode) o).getContactDescriptor();
- Iterator<UIContactDetail> contactDetails
- = uiContact
- .getContactDetailsForOperationSet(
- OperationSetBasicTelephony.class)
- .iterator();
-
- while (contactDetails.hasNext())
- {
- UIContactDetail detail = contactDetails.next();
- ProtocolProviderService detailProvider
- = detail.getPreferredProtocolProvider(
- OperationSetBasicTelephony.class);
-
- if (detailProvider != null)
- {
- /*
- * Currently for videobridge conferences we only support
- * adding contacts via the account with the videobridge
- */
- if (callConference.isJitsiVideobridge())
- {
- for (Call call : callConference.getCalls())
- {
- if (detailProvider == call.getProtocolProvider())
- {
- callee = detail.getAddress();
- provider = detailProvider;
- break;
- }
- }
- }
- else
- {
- callee = detail.getAddress();
- provider = detailProvider;
- break;
- }
- }
- }
-
- if (callee == null)
- {
- /*
- * It turns out that the error message to be reported would
- * like to display information about the account which could
- * not add the dropped callee to the telephony conference.
- * Unfortunately, a telephony conference may have multiple
- * accounts involved. Anyway, choose the first account
- * involved in the telephony conference.
- */
- ProtocolProviderService callProvider
- = callConference.getCalls().get(0)
- .getProtocolProvider();
-
- ResourceManagementService resources
- = GuiActivator.getResources();
- AccountID accountID = callProvider.getAccountID();
-
- new ErrorDialog(null,
- resources.getI18NString("service.gui.ERROR"),
- resources.getI18NString(
- "service.gui.CALL_NOT_SUPPORTING_PARTICIPANT",
- new String[]
- {
- accountID.getService(),
- accountID.getUserID(),
- uiContact.getDisplayName()
- }))
- .showDialog();
- }
- }
- }
- else if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
- {
- InputContext inputContext = comp.getInputContext();
-
- if (inputContext != null)
- inputContext.endComposition();
-
- try
- {
- BufferedReader reader
- = new BufferedReader(
- DataFlavor.stringFlavor.getReaderForText(t));
-
- try
- {
- String line;
- StringBuilder calleeBuilder = new StringBuilder();
-
- while ((line = reader.readLine()) != null)
- calleeBuilder.append(line);
-
- callee = calleeBuilder.toString();
- /*
- * The value of the local variable provider will be null
- * because we have a String only and hence we have no
- * associated ProtocolProviderService.
- * CallManager.inviteToConferenceCall will accept it.
- */
- }
- finally
- {
- reader.close();
- }
- }
- catch (UnsupportedFlavorException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to drop string.", e);
- }
- catch (IOException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to drop string.", e);
- }
- }
-
- if (callee == null)
- return false;
- else
- {
- Map<ProtocolProviderService, List<String>> callees
- = new HashMap<ProtocolProviderService, List<String>>();
-
- callees.put(provider, Arrays.asList(callee));
- CallManager.inviteToConferenceCall(callees, callConference);
-
- return true;
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.datatransfer.*;
+import java.awt.im.*;
+import java.io.*;
+import java.util.*;
+
+import javax.swing.*;
+
+import org.jitsi.service.resources.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * A <tt>TransferHandler</tt> that handles dropping of <tt>UIContact</tt>s or
+ * <tt>String</tt> addresses on a <tt>CallConference</tt>. Dropping such data on
+ * the <tt>CallDialog</tt> will turn a one-to-one <tt>Call</tt> into a telephony
+ * conference.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallTransferHandler
+ extends ExtendedTransferHandler
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * The data flavor used when transferring <tt>UIContact</tt>s.
+ */
+ protected static final DataFlavor uiContactDataFlavor
+ = new DataFlavor(UIContact.class, "UIContact");
+
+ /**
+ * The logger.
+ */
+ private static final Logger logger
+ = Logger.getLogger(CallTransferHandler.class);
+
+ /**
+ * The <tt>CallConference</tt> into which the dropped callees are to be
+ * invited.
+ */
+ private final CallConference callConference;
+
+ /**
+ * Initializes a new <tt>CallTransferHandler</tt> instance which is to
+ * invite dropped callees to a telephony conference specified by a specific
+ * <tt>Call</tt> which participates in it.
+ *
+ * @param call the <tt>Call</tt> which specifies the telephony conference to
+ * which dropped callees are to be invited
+ */
+ public CallTransferHandler(Call call)
+ {
+ this(call.getConference());
+ }
+
+ /**
+ * Initializes a new <tt>CallTransferHandler</tt> instance which is to
+ * invite dropped callees to a specific <tt>CallConference</tt>.
+ *
+ * @param callConference the <tt>CallConference</tt> to which dropped
+ * callees are to be invited
+ */
+ public CallTransferHandler(CallConference callConference)
+ {
+ this.callConference = callConference;
+ }
+
+ /**
+ * Indicates whether a component will accept an import of the given
+ * set of data flavors prior to actually attempting to import it. We return
+ * <tt>true</tt> to indicate that the transfer with at least one of the
+ * given flavors would work and <tt>false</tt> to reject the transfer.
+ * <p>
+ * @param comp component
+ * @param flavor the data formats available
+ * @return true if the data can be inserted into the component, false
+ * otherwise
+ * @throws NullPointerException if <code>support</code> is {@code null}
+ */
+ @Override
+ public boolean canImport(JComponent comp, DataFlavor[] flavor)
+ {
+ for (DataFlavor f : flavor)
+ {
+ if (f.equals(DataFlavor.stringFlavor)
+ || f.equals(uiContactDataFlavor))
+ {
+ return (comp instanceof JPanel);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handles transfers to the chat panel from the clip board or a
+ * DND drop operation. The <tt>Transferable</tt> parameter contains the
+ * data that needs to be imported.
+ * <p>
+ * @param comp the component to receive the transfer;
+ * @param t the data to import
+ * @return true if the data was inserted into the component and false
+ * otherwise
+ */
+ @Override
+ public boolean importData(JComponent comp, Transferable t)
+ {
+ String callee = null;
+ ProtocolProviderService provider = null;
+
+ if (t.isDataFlavorSupported(uiContactDataFlavor))
+ {
+ Object o = null;
+
+ try
+ {
+ o = t.getTransferData(uiContactDataFlavor);
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop meta contact.", e);
+ }
+ catch (IOException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop meta contact.", e);
+ }
+
+ if (o instanceof ContactNode)
+ {
+ UIContact uiContact = ((ContactNode) o).getContactDescriptor();
+ Iterator<UIContactDetail> contactDetails
+ = uiContact
+ .getContactDetailsForOperationSet(
+ OperationSetBasicTelephony.class)
+ .iterator();
+
+ while (contactDetails.hasNext())
+ {
+ UIContactDetail detail = contactDetails.next();
+ ProtocolProviderService detailProvider
+ = detail.getPreferredProtocolProvider(
+ OperationSetBasicTelephony.class);
+
+ if (detailProvider != null)
+ {
+ /*
+ * Currently for videobridge conferences we only support
+ * adding contacts via the account with the videobridge
+ */
+ if (callConference.isJitsiVideobridge())
+ {
+ for (Call call : callConference.getCalls())
+ {
+ if (detailProvider == call.getProtocolProvider())
+ {
+ callee = detail.getAddress();
+ provider = detailProvider;
+ break;
+ }
+ }
+ }
+ else
+ {
+ callee = detail.getAddress();
+ provider = detailProvider;
+ break;
+ }
+ }
+ }
+
+ if (callee == null)
+ {
+ /*
+ * It turns out that the error message to be reported would
+ * like to display information about the account which could
+ * not add the dropped callee to the telephony conference.
+ * Unfortunately, a telephony conference may have multiple
+ * accounts involved. Anyway, choose the first account
+ * involved in the telephony conference.
+ */
+ ProtocolProviderService callProvider
+ = callConference.getCalls().get(0)
+ .getProtocolProvider();
+
+ ResourceManagementService resources
+ = GuiActivator.getResources();
+ AccountID accountID = callProvider.getAccountID();
+
+ new ErrorDialog(null,
+ resources.getI18NString("service.gui.ERROR"),
+ resources.getI18NString(
+ "service.gui.CALL_NOT_SUPPORTING_PARTICIPANT",
+ new String[]
+ {
+ accountID.getService(),
+ accountID.getUserID(),
+ uiContact.getDisplayName()
+ }))
+ .showDialog();
+ }
+ }
+ }
+ else if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
+ {
+ InputContext inputContext = comp.getInputContext();
+
+ if (inputContext != null)
+ inputContext.endComposition();
+
+ try
+ {
+ BufferedReader reader
+ = new BufferedReader(
+ DataFlavor.stringFlavor.getReaderForText(t));
+
+ try
+ {
+ String line;
+ StringBuilder calleeBuilder = new StringBuilder();
+
+ while ((line = reader.readLine()) != null)
+ calleeBuilder.append(line);
+
+ callee = calleeBuilder.toString();
+ /*
+ * The value of the local variable provider will be null
+ * because we have a String only and hence we have no
+ * associated ProtocolProviderService.
+ * CallManager.inviteToConferenceCall will accept it.
+ */
+ }
+ finally
+ {
+ reader.close();
+ }
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop string.", e);
+ }
+ catch (IOException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop string.", e);
+ }
+ }
+
+ if (callee == null)
+ return false;
+ else
+ {
+ Map<ProtocolProviderService, List<String>> callees
+ = new HashMap<ProtocolProviderService, List<String>>();
+
+ callees.put(provider, Arrays.asList(callee));
+ CallManager.inviteToConferenceCall(callees, callConference);
+
+ return true;
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java b/src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java
index 3e823ba..c56a415 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,176 +15,176 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import java.awt.*;
-import java.util.*;
-import java.util.List;
-
-/**
- * Implements a <tt>LayoutManager</tt> for the full-screen <tt>Call</tt>
- * display.
- *
- * @author Lyubomir Marinov
- */
-public class FullScreenLayout
- implements LayoutManager
-{
- public static final String CENTER = "CENTER";
-
- public static final String SOUTH = "SOUTH";
-
- private Component center;
-
- /**
- * The indicator which determines whether {@link #south} is to be laid out
- * on top of {@link #center} i.e. as an overlay.
- */
- private final boolean overlay;
-
- private Component south;
-
- /**
- * The vertical gap between the center and the south components.
- */
- private int yGap = 0;
-
- /**
- * Initializes a new <tt>FullScreenLayout</tt> instance.
- *
- * @param overlay <tt>true</tt> to lay out the <tt>Component</tt> at
- * {@link #SOUTH} on top of the <tt>Component</tt> at {@link #CENTER} i.e as
- * an overlay; otherwise, <tt>false</tt>
- * @oaram yGap the gap betwen the center and the south component
- */
- public FullScreenLayout(boolean overlay, int yGap)
- {
- this.overlay = overlay;
- this.yGap = yGap;
- }
-
- /**
- * Adds the given component to this layout.
- *
- * @param name the name of the constraint (CENTER or SOUTH)
- * @param comp the component to add to this layout
- */
- public void addLayoutComponent(String name, Component comp)
- {
- if (CENTER.equals(name))
- center = comp;
- else if (SOUTH.equals(name))
- south = comp;
- }
-
- /**
- * Gets a <tt>List</tt> of the <tt>Component</tt>s to be laid out by this
- * <tt>LayoutManager</tt> i.e. the non-<tt>null</tt> of {@link #center}
- * and {@link #south}.
- *
- * @return a <tt>List</tt> of the <tt>Component</tt>s to be laid out by this
- * <tt>LayoutManager</tt>
- */
- private List<Component> getLayoutComponents()
- {
- List<Component> layoutComponents = new ArrayList<Component>(2);
-
- if (center != null)
- layoutComponents.add(center);
- if (south != null)
- layoutComponents.add(south);
- return layoutComponents;
- }
-
- /**
- * Lays out the components added in the given parent container
- *
- * @param parent the parent container to lay out
- */
- public void layoutContainer(Container parent)
- {
- int southWidth;
- int southHeight;
-
- if (south == null)
- {
- southWidth = southHeight = 0;
- }
- else
- {
- Dimension southSize = south.getPreferredSize();
-
- southWidth = southSize.width;
- southHeight = southSize.height;
- }
-
- Dimension parentSize = parent.getSize();
-
- if (center != null)
- {
- /*
- * If the Component at the SOUTH is not to be shown as an overlay,
- * make room for it bellow the Component at the CENTER.
- */
- int yOffset = overlay ? 0 : southHeight + yGap;
-
- center.setBounds(
- 0,
- 0,
- parentSize.width,
- parentSize.height - yOffset);
- }
- if (south != null)
- {
- south.setBounds(
- (parentSize.width - southWidth) / 2,
- parentSize.height - southHeight,
- southWidth,
- southHeight);
- }
- }
-
- public Dimension minimumLayoutSize(Container parent)
- {
- List<Component> components = getLayoutComponents();
- Dimension size = new Dimension(0, 0);
-
- for (Component component : components)
- {
- Dimension componentSize = component.getMinimumSize();
-
- size.width = Math.max(size.width, componentSize.width);
- if (overlay)
- size.height = Math.max(size.height, componentSize.height);
- else
- size.height += componentSize.height;
- }
- return size;
- }
-
- public Dimension preferredLayoutSize(Container parent)
- {
- List<Component> components = getLayoutComponents();
- Dimension size = new Dimension(0, 0);
-
- for (Component component : components)
- {
- Dimension componentSize = component.getPreferredSize();
-
- size.width = Math.max(size.width, componentSize.width);
- if (overlay)
- size.height = Math.max(size.height, componentSize.height);
- else
- size.height += componentSize.height;
- }
- return size;
- }
-
- public void removeLayoutComponent(Component comp)
- {
- if (comp.equals(center))
- center = null;
- else if (comp.equals(south))
- south = null;
- }
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * Implements a <tt>LayoutManager</tt> for the full-screen <tt>Call</tt>
+ * display.
+ *
+ * @author Lyubomir Marinov
+ */
+public class FullScreenLayout
+ implements LayoutManager
+{
+ public static final String CENTER = "CENTER";
+
+ public static final String SOUTH = "SOUTH";
+
+ private Component center;
+
+ /**
+ * The indicator which determines whether {@link #south} is to be laid out
+ * on top of {@link #center} i.e. as an overlay.
+ */
+ private final boolean overlay;
+
+ private Component south;
+
+ /**
+ * The vertical gap between the center and the south components.
+ */
+ private int yGap = 0;
+
+ /**
+ * Initializes a new <tt>FullScreenLayout</tt> instance.
+ *
+ * @param overlay <tt>true</tt> to lay out the <tt>Component</tt> at
+ * {@link #SOUTH} on top of the <tt>Component</tt> at {@link #CENTER} i.e as
+ * an overlay; otherwise, <tt>false</tt>
+ * @oaram yGap the gap betwen the center and the south component
+ */
+ public FullScreenLayout(boolean overlay, int yGap)
+ {
+ this.overlay = overlay;
+ this.yGap = yGap;
+ }
+
+ /**
+ * Adds the given component to this layout.
+ *
+ * @param name the name of the constraint (CENTER or SOUTH)
+ * @param comp the component to add to this layout
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ if (CENTER.equals(name))
+ center = comp;
+ else if (SOUTH.equals(name))
+ south = comp;
+ }
+
+ /**
+ * Gets a <tt>List</tt> of the <tt>Component</tt>s to be laid out by this
+ * <tt>LayoutManager</tt> i.e. the non-<tt>null</tt> of {@link #center}
+ * and {@link #south}.
+ *
+ * @return a <tt>List</tt> of the <tt>Component</tt>s to be laid out by this
+ * <tt>LayoutManager</tt>
+ */
+ private List<Component> getLayoutComponents()
+ {
+ List<Component> layoutComponents = new ArrayList<Component>(2);
+
+ if (center != null)
+ layoutComponents.add(center);
+ if (south != null)
+ layoutComponents.add(south);
+ return layoutComponents;
+ }
+
+ /**
+ * Lays out the components added in the given parent container
+ *
+ * @param parent the parent container to lay out
+ */
+ public void layoutContainer(Container parent)
+ {
+ int southWidth;
+ int southHeight;
+
+ if (south == null)
+ {
+ southWidth = southHeight = 0;
+ }
+ else
+ {
+ Dimension southSize = south.getPreferredSize();
+
+ southWidth = southSize.width;
+ southHeight = southSize.height;
+ }
+
+ Dimension parentSize = parent.getSize();
+
+ if (center != null)
+ {
+ /*
+ * If the Component at the SOUTH is not to be shown as an overlay,
+ * make room for it bellow the Component at the CENTER.
+ */
+ int yOffset = overlay ? 0 : southHeight + yGap;
+
+ center.setBounds(
+ 0,
+ 0,
+ parentSize.width,
+ parentSize.height - yOffset);
+ }
+ if (south != null)
+ {
+ south.setBounds(
+ (parentSize.width - southWidth) / 2,
+ parentSize.height - southHeight,
+ southWidth,
+ southHeight);
+ }
+ }
+
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ List<Component> components = getLayoutComponents();
+ Dimension size = new Dimension(0, 0);
+
+ for (Component component : components)
+ {
+ Dimension componentSize = component.getMinimumSize();
+
+ size.width = Math.max(size.width, componentSize.width);
+ if (overlay)
+ size.height = Math.max(size.height, componentSize.height);
+ else
+ size.height += componentSize.height;
+ }
+ return size;
+ }
+
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ List<Component> components = getLayoutComponents();
+ Dimension size = new Dimension(0, 0);
+
+ for (Component component : components)
+ {
+ Dimension componentSize = component.getPreferredSize();
+
+ size.width = Math.max(size.width, componentSize.width);
+ if (overlay)
+ size.height = Math.max(size.height, componentSize.height);
+ else
+ size.height += componentSize.height;
+ }
+ return size;
+ }
+
+ public void removeLayoutComponent(Component comp)
+ {
+ if (comp.equals(center))
+ center = null;
+ else if (comp.equals(south))
+ south = null;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java
index 76c4139..0d6206a 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java
@@ -256,8 +256,12 @@ public abstract class PreCallDialog
receivedCallWindow.setAlwaysOnTop(true);
+ // Register the JFrame so the dialog window is able to get dragged
+ // across the screen
+ ComponentMover.registerComponent(receivedCallWindow);
+
// prevents dialog window to get unwanted key events and when going
- // on top on linux, it steals focus and if we are accidently
+ // on top on linux, it steals focus and if we are accidentally
// writing something and pressing enter a call get answered
receivedCallWindow.setFocusableWindowState(false);
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java
index 2e2ed5b..b49e0a3 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,117 +15,117 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.skin.*;
-
-import org.jitsi.service.neomedia.*;
-
-/**
- * Base class for security panels that show encryption specific UI controls.
- *
- * @author Ingo Bauersachs
- */
-public abstract class SecurityPanel<T extends SrtpControl>
- extends FadeInBalloonPanel
- implements Skinnable
-{
- /**
- * The currently used security control.
- */
- protected T securityControl;
-
- /**
- * Create security panel using the security control.
- * @param securityControl
- */
- public SecurityPanel(T securityControl)
- {
- this.securityControl = securityControl;
- }
-
- /**
- * The currently used security control.
- * @return
- */
- public T getSecurityControl()
- {
- return securityControl;
- }
-
- /**
- * The currently used security control.
- * @return
- */
- public void setSecurityControl(T securityControl)
- {
- this.securityControl = securityControl;
- }
-
- /**
- * Creates the security panel depending on the concrete implementation of
- * the passed security controller.
- *
- * @param srtpControl the security controller that provides the information
- * to be shown on the UI
- * @return An instance of a {@link SecurityPanel} for the security
- * controller or an {@link TransparentPanel} if the controller is
- * unknown or does not have any controls to show.
- */
- public static SecurityPanel<?> create(
- SwingCallPeerRenderer peerRenderer,
- CallPeer callPeer,
- SrtpControl srtpControl)
- {
- if(srtpControl instanceof ZrtpControl)
- {
- return
- new ZrtpSecurityPanel(
- peerRenderer,
- callPeer,
- (ZrtpControl) srtpControl);
- }
- else
- {
- return
- new SecurityPanel<SrtpControl>(srtpControl)
- {
- public void loadSkin() {}
-
- @Override
- public void securityOn(CallPeerSecurityOnEvent evt) {}
-
- @Override
- public void securityOff(CallPeerSecurityOffEvent evt) {}
-
- @Override
- public void securityTimeout(
- CallPeerSecurityTimeoutEvent evt) {}
- };
- }
- }
-
- /**
- * Indicates that the security is turned on.
- *
- * @param evt details about the event that caused this message.
- */
- public abstract void securityOn(CallPeerSecurityOnEvent evt);
-
- /**
- * Indicates that the security is turned off.
- *
- * @param evt details about the event that caused this message.
- */
- public abstract void securityOff(CallPeerSecurityOffEvent evt);
-
- /**
- * Indicates that the security is timeouted, is not supported by the
- * other end.
- * @param evt Details about the event that caused this message.
- */
- public abstract void securityTimeout(CallPeerSecurityTimeoutEvent evt);
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.skin.*;
+
+import org.jitsi.service.neomedia.*;
+
+/**
+ * Base class for security panels that show encryption specific UI controls.
+ *
+ * @author Ingo Bauersachs
+ */
+public abstract class SecurityPanel<T extends SrtpControl>
+ extends FadeInBalloonPanel
+ implements Skinnable
+{
+ /**
+ * The currently used security control.
+ */
+ protected T securityControl;
+
+ /**
+ * Create security panel using the security control.
+ * @param securityControl
+ */
+ public SecurityPanel(T securityControl)
+ {
+ this.securityControl = securityControl;
+ }
+
+ /**
+ * The currently used security control.
+ * @return
+ */
+ public T getSecurityControl()
+ {
+ return securityControl;
+ }
+
+ /**
+ * The currently used security control.
+ * @return
+ */
+ public void setSecurityControl(T securityControl)
+ {
+ this.securityControl = securityControl;
+ }
+
+ /**
+ * Creates the security panel depending on the concrete implementation of
+ * the passed security controller.
+ *
+ * @param srtpControl the security controller that provides the information
+ * to be shown on the UI
+ * @return An instance of a {@link SecurityPanel} for the security
+ * controller or an {@link TransparentPanel} if the controller is
+ * unknown or does not have any controls to show.
+ */
+ public static SecurityPanel<?> create(
+ SwingCallPeerRenderer peerRenderer,
+ CallPeer callPeer,
+ SrtpControl srtpControl)
+ {
+ if(srtpControl instanceof ZrtpControl)
+ {
+ return
+ new ZrtpSecurityPanel(
+ peerRenderer,
+ callPeer,
+ (ZrtpControl) srtpControl);
+ }
+ else
+ {
+ return
+ new SecurityPanel<SrtpControl>(srtpControl)
+ {
+ public void loadSkin() {}
+
+ @Override
+ public void securityOn(CallPeerSecurityOnEvent evt) {}
+
+ @Override
+ public void securityOff(CallPeerSecurityOffEvent evt) {}
+
+ @Override
+ public void securityTimeout(
+ CallPeerSecurityTimeoutEvent evt) {}
+ };
+ }
+ }
+
+ /**
+ * Indicates that the security is turned on.
+ *
+ * @param evt details about the event that caused this message.
+ */
+ public abstract void securityOn(CallPeerSecurityOnEvent evt);
+
+ /**
+ * Indicates that the security is turned off.
+ *
+ * @param evt details about the event that caused this message.
+ */
+ public abstract void securityOff(CallPeerSecurityOffEvent evt);
+
+ /**
+ * Indicates that the security is timeouted, is not supported by the
+ * other end.
+ * @param evt Details about the event that caused this message.
+ */
+ public abstract void securityTimeout(CallPeerSecurityTimeoutEvent evt);
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java
index f2f2bd2..982738b 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,140 +15,140 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * Represents a <tt>Dialog</tt> which allows specifying the target contact
- * address of a transfer-call operation.
- *
- * @author Yana Stamcheva
- */
-public class TransferCallDialog
- extends OneChoiceInviteDialog
-{
- /**
- * The peer to transfer.
- */
- private final CallPeer transferPeer;
-
- /**
- * Creates a <tt>TransferCallDialog</tt> by specifying the peer to transfer
- * @param peer the peer to transfer
- */
- public TransferCallDialog(final CallPeer peer)
- {
- super(GuiActivator.getResources()
- .getI18NString("service.gui.TRANSFER_CALL_TITLE"));
-
- this.transferPeer = peer;
-
- this.initContactListData(peer.getProtocolProvider());
-
- this.setInfoText(GuiActivator.getResources()
- .getI18NString("service.gui.TRANSFER_CALL_MSG"));
- this.setOkButtonText(GuiActivator.getResources()
- .getI18NString("service.gui.TRANSFER"));
-
- this.setMinimumSize(new Dimension(300, 300));
-
- addOkButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- UIContact uiContact = getSelectedContact();
-
- if (uiContact != null)
- {
- transferToContact(uiContact);
- }
-
- setVisible(false);
- dispose();
- }
- });
- addCancelButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- setVisible(false);
- dispose();
- }
- });
- }
-
- /**
- * Initializes contact list sources.
- */
- private void initContactSources()
- {
- DemuxContactSourceService demuxCSService
- = GuiActivator.getDemuxContactSourceService();
-
- // If the DemuxContactSourceService isn't registered we use the default
- // contact source set.
- if (demuxCSService == null)
- return;
-
- Iterator<UIContactSource> sourcesIter
- = new ArrayList<UIContactSource>(
- contactList.getContactSources()).iterator();
-
- contactList.removeAllContactSources();
-
- while (sourcesIter.hasNext())
- {
- ContactSourceService contactSource
- = sourcesIter.next().getContactSourceService();
-
- contactList.addContactSource(
- demuxCSService.createDemuxContactSource(contactSource));
- }
- }
-
- /**
- * Initializes the left contact list with the contacts that could be added
- * to the current chat session.
- *
- * @param protocolProvider the protocol provider from which to initialize
- * the contact list data
- */
- private void initContactListData(ProtocolProviderService protocolProvider)
- {
- initContactSources();
-
- contactList.addContactSource(
- new ProtocolContactSourceServiceImpl(
- protocolProvider, OperationSetBasicTelephony.class));
- contactList.addContactSource(
- new StringContactSourceServiceImpl(
- protocolProvider, OperationSetBasicTelephony.class));
-
- contactList.applyDefaultFilter();
- }
-
- /**
- * Transfer the transfer peer to the given <tt>UIContact</tt>.
- *
- * @param uiContact the contact to transfer to
- */
- private void transferToContact(UIContact uiContact)
- {
- UIContactDetail contactDetail = uiContact
- .getDefaultContactDetail(
- OperationSetBasicTelephony.class);
-
- CallManager.transferCall( transferPeer,
- contactDetail.getAddress());
- }
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Represents a <tt>Dialog</tt> which allows specifying the target contact
+ * address of a transfer-call operation.
+ *
+ * @author Yana Stamcheva
+ */
+public class TransferCallDialog
+ extends OneChoiceInviteDialog
+{
+ /**
+ * The peer to transfer.
+ */
+ private final CallPeer transferPeer;
+
+ /**
+ * Creates a <tt>TransferCallDialog</tt> by specifying the peer to transfer
+ * @param peer the peer to transfer
+ */
+ public TransferCallDialog(final CallPeer peer)
+ {
+ super(GuiActivator.getResources()
+ .getI18NString("service.gui.TRANSFER_CALL_TITLE"));
+
+ this.transferPeer = peer;
+
+ this.initContactListData(peer.getProtocolProvider());
+
+ this.setInfoText(GuiActivator.getResources()
+ .getI18NString("service.gui.TRANSFER_CALL_MSG"));
+ this.setOkButtonText(GuiActivator.getResources()
+ .getI18NString("service.gui.TRANSFER"));
+
+ this.setMinimumSize(new Dimension(300, 300));
+
+ addOkButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ UIContact uiContact = getSelectedContact();
+
+ if (uiContact != null)
+ {
+ transferToContact(uiContact);
+ }
+
+ setVisible(false);
+ dispose();
+ }
+ });
+ addCancelButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ setVisible(false);
+ dispose();
+ }
+ });
+ }
+
+ /**
+ * Initializes contact list sources.
+ */
+ private void initContactSources()
+ {
+ DemuxContactSourceService demuxCSService
+ = GuiActivator.getDemuxContactSourceService();
+
+ // If the DemuxContactSourceService isn't registered we use the default
+ // contact source set.
+ if (demuxCSService == null)
+ return;
+
+ Iterator<UIContactSource> sourcesIter
+ = new ArrayList<UIContactSource>(
+ contactList.getContactSources()).iterator();
+
+ contactList.removeAllContactSources();
+
+ while (sourcesIter.hasNext())
+ {
+ ContactSourceService contactSource
+ = sourcesIter.next().getContactSourceService();
+
+ contactList.addContactSource(
+ demuxCSService.createDemuxContactSource(contactSource));
+ }
+ }
+
+ /**
+ * Initializes the left contact list with the contacts that could be added
+ * to the current chat session.
+ *
+ * @param protocolProvider the protocol provider from which to initialize
+ * the contact list data
+ */
+ private void initContactListData(ProtocolProviderService protocolProvider)
+ {
+ initContactSources();
+
+ contactList.addContactSource(
+ new ProtocolContactSourceServiceImpl(
+ protocolProvider, OperationSetBasicTelephony.class));
+ contactList.addContactSource(
+ new StringContactSourceServiceImpl(
+ protocolProvider, OperationSetBasicTelephony.class));
+
+ contactList.applyDefaultFilter();
+ }
+
+ /**
+ * Transfer the transfer peer to the given <tt>UIContact</tt>.
+ *
+ * @param uiContact the contact to transfer to
+ */
+ private void transferToContact(UIContact uiContact)
+ {
+ UIContactDetail contactDetail = uiContact
+ .getDefaultContactDetail(
+ OperationSetBasicTelephony.class);
+
+ CallManager.transferCall( transferPeer,
+ contactDetail.getAddress());
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
index 80eedc6..bb32858 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,570 +15,570 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call.conference;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-import java.util.List;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.call.*;
-import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * The invite dialog is the one shown when the user clicks on the conference
- * button in the chat toolbar.
- *
- * @author Yana Stamcheva
- * @author Lyubomir Marinov
- */
-public class ConferenceInviteDialog
- extends InviteDialog
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * The account selector box.
- */
- private final JComboBox accountSelectorBox = new JComboBox();
-
- /**
- * The last selected account.
- */
- private Object lastSelectedAccount;
-
- /**
- * The telephony conference into which this instance is to invite
- * participants.
- */
- private final CallConference conference;
-
- /**
- * The current provider contact source.
- */
- private ContactSourceService currentProviderContactSource;
-
- /**
- * The current string contact source.
- */
- private ContactSourceService currentStringContactSource;
-
- /**
- * The previously selected protocol provider, with which this dialog has
- * been instantiated.
- */
- private ProtocolProviderService preselectedProtocolProvider;
-
- /**
- * Indicates whether this conference invite dialog is associated with a
- * Jitsi Videobridge invite.
- */
- private final boolean isJitsiVideobridge;
-
- /**
- * Initializes a new <tt>ConferenceInviteDialog</tt> instance which is to
- * invite contacts/participants in a specific telephony conference.
- *
- * @param conference the telephony conference in which the new instance is
- * to invite contacts/participants
- */
- public ConferenceInviteDialog(
- CallConference conference,
- ProtocolProviderService preselectedProvider,
- List<ProtocolProviderService> protocolProviders,
- final boolean isJitsiVideobridge)
- {
- // Set the correct dialog title depending if we're going to create a
- // video bridge conference call
- super((isJitsiVideobridge
- ? GuiActivator.getResources()
- .getI18NString("service.gui.INVITE_CONTACT_TO_VIDEO_BRIDGE")
- : GuiActivator.getResources()
- .getI18NString("service.gui.INVITE_CONTACT_TO_CALL")),
- false);
-
- this.conference = conference;
- this.preselectedProtocolProvider = preselectedProvider;
- this.isJitsiVideobridge = isJitsiVideobridge;
-
- if (preselectedProtocolProvider == null)
- initAccountSelectorPanel(protocolProviders);
-
- // init the list, as we check whether features are supported
- // it may take some time if we have too much contacts
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run()
- {
- initContactSources();
-
- // Initialize the list of contacts to select from.
- if (preselectedProtocolProvider != null)
- initContactListData(preselectedProtocolProvider);
- else
- initContactListData(
- (ProtocolProviderService) accountSelectorBox
- .getSelectedItem());
- }
- });
-
- this.addInviteButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- Collection<UIContact> selectedContacts
- = destContactList.getContacts(null);
-
- if (selectedContacts != null && selectedContacts.size() > 0)
- {
- if (preselectedProtocolProvider == null)
- preselectedProtocolProvider
- = (ProtocolProviderService) accountSelectorBox
- .getSelectedItem();
-
- if (isJitsiVideobridge)
- inviteJitsiVideobridgeContacts( preselectedProtocolProvider,
- selectedContacts);
- else
- inviteContacts(selectedContacts);
-
- // Store the last used account in order to pre-select it
- // next time.
- ConfigurationUtils.setLastCallConferenceProvider(
- preselectedProtocolProvider);
-
- dispose();
- }
- else
- {
- // TODO: The underlying invite dialog should show a message
- // to the user that she should select at least two contacts
- // in order to create a conference.
- }
- }
- });
-
- this.addCancelButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- dispose();
- }
- });
- }
-
- /**
- * Constructs the <tt>ConferenceInviteDialog</tt>.
- */
- public ConferenceInviteDialog()
- {
- this(null, null, null, false);
- }
-
- /**
- * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
- * already created conference. To use when inviting contacts to an existing
- * conference is needed.
- *
- * @param conference the existing <tt>CallConference</tt>
- */
- public ConferenceInviteDialog(CallConference conference)
- {
- this(conference, null, null, false);
- }
-
- /**
- * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
- * already created conference. To use when inviting contacts to an existing
- * conference is needed.
- *
- * @param conference the existing <tt>CallConference</tt>
- */
- public ConferenceInviteDialog(
- CallConference conference,
- ProtocolProviderService preselectedProtocolProvider,
- boolean isJitsiVideobridge)
- {
- this(conference, preselectedProtocolProvider, null, isJitsiVideobridge);
- }
-
- /**
- * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
- * preselected protocol provider to be used and if this is an invite for
- * a video bridge conference.
- *
- * @param protocolProviders the protocol providers list
- * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
- * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
- */
- public ConferenceInviteDialog(
- List<ProtocolProviderService> protocolProviders,
- boolean isJitsiVideobridge)
- {
- this(null, null, protocolProviders, isJitsiVideobridge);
- }
-
- /**
- * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
- * preselected protocol provider to be used and if this is an invite for
- * a video bridge conference.
- *
- * @param selectedConfProvider the preselected protocol provider
- * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
- * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
- */
- public ConferenceInviteDialog(
- ProtocolProviderService selectedConfProvider,
- boolean isJitsiVideobridge)
- {
- this(null, selectedConfProvider, null, isJitsiVideobridge);
- }
-
- /**
- * Initializes the account selector panel.
- *
- * @param protocolProviders the list of protocol providers we'd like to
- * show in the account selector box
- */
- private void initAccountSelectorPanel(
- List<ProtocolProviderService> protocolProviders)
- {
- JLabel accountSelectorLabel = new JLabel(
- GuiActivator.getResources().getI18NString("service.gui.CALL_VIA"));
-
- TransparentPanel accountSelectorPanel
- = new TransparentPanel(new BorderLayout());
-
- accountSelectorPanel.setBorder(
- BorderFactory.createEmptyBorder(5, 5, 5, 5));
- accountSelectorPanel.add(accountSelectorLabel, BorderLayout.WEST);
- accountSelectorPanel.add(accountSelectorBox, BorderLayout.CENTER);
-
- // Initialize the account selector box.
- if (protocolProviders != null && protocolProviders.size() > 0)
- this.initAccountListData(protocolProviders);
- else
- this.initAccountListData();
-
- this.accountSelectorBox.setRenderer(new DefaultListCellRenderer()
- {
- private static final long serialVersionUID = 0L;
-
- @Override
- public Component getListCellRendererComponent(JList list,
- Object value, int index, boolean isSelected,
- boolean cellHasFocus)
- {
- ProtocolProviderService protocolProvider
- = (ProtocolProviderService) value;
-
- if (protocolProvider != null)
- {
- this.setText(
- protocolProvider.getAccountID().getDisplayName());
- this.setIcon(
- ImageLoader.getAccountStatusImage(protocolProvider));
- }
-
- return this;
- }
- });
-
- this.accountSelectorBox.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- Object accountSelectorBoxSelectedItem
- = accountSelectorBox.getSelectedItem();
-
- if (lastSelectedAccount == null
- || !lastSelectedAccount
- .equals(accountSelectorBoxSelectedItem))
- {
- lastSelectedAccount = accountSelectorBoxSelectedItem;
-
- initContactListData(
- (ProtocolProviderService) accountSelectorBox
- .getSelectedItem());
-
- if (isJitsiVideobridge)
- destContactList.removeAll();
- }
- }
- });
-
- this.getContentPane().add(accountSelectorPanel, BorderLayout.NORTH);
- }
-
- /**
- * Initializes the account selector box with the given list of
- * <tt>ProtocolProviderService</tt>-s.
- *
- * @param protocolProviders the list of <tt>ProtocolProviderService</tt>-s
- * we'd like to show in the account selector box
- */
- private void initAccountListData(
- List<ProtocolProviderService> protocolProviders)
- {
- Iterator<ProtocolProviderService> providersIter
- = protocolProviders.iterator();
-
- while (providersIter.hasNext())
- {
- ProtocolProviderService protocolProvider
- = providersIter.next();
-
- accountSelectorBox.addItem(protocolProvider);
- }
-
- if (accountSelectorBox.getItemCount() > 0)
- accountSelectorBox.setSelectedIndex(0);
- }
-
- /**
- * Initializes the account list.
- */
- private void initAccountListData()
- {
- Iterator<ProtocolProviderService> protocolProviders
- = GuiActivator.getUIService().getMainFrame().getProtocolProviders();
-
- while(protocolProviders.hasNext())
- {
- ProtocolProviderService protocolProvider
- = protocolProviders.next();
- OperationSet opSet
- = protocolProvider.getOperationSet(
- OperationSetTelephonyConferencing.class);
-
- if ((opSet != null) && protocolProvider.isRegistered())
- accountSelectorBox.addItem(protocolProvider);
- }
-
- // Try to select the last used account if available.
- ProtocolProviderService pps
- = ConfigurationUtils.getLastCallConferenceProvider();
-
- if (pps == null && conference != null)
- {
- /*
- * Pick up the first account from the ones participating in the
- * associated telephony conference which supports
- * OperationSetTelephonyConferencing.
- */
- for (Call call : conference.getCalls())
- {
- ProtocolProviderService callPps = call.getProtocolProvider();
-
- if (callPps.getOperationSet(
- OperationSetTelephonyConferencing.class)
- != null)
- {
- pps = callPps;
- break;
- }
- }
- }
-
- if (pps != null)
- accountSelectorBox.setSelectedItem(pps);
- else if (accountSelectorBox.getItemCount() > 0)
- accountSelectorBox.setSelectedIndex(0);
- }
-
- /**
- * Initializes contact list sources.
- */
- private void initContactSources()
- {
- DemuxContactSourceService demuxCSService
- = GuiActivator.getDemuxContactSourceService();
-
- // If the DemuxContactSourceService isn't registered we use the default
- // contact source set.
- if (demuxCSService == null)
- return;
-
- Iterator<UIContactSource> sourcesIter
- = new ArrayList<UIContactSource>(
- srcContactList.getContactSources()).iterator();
-
- srcContactList.removeAllContactSources();
-
- while (sourcesIter.hasNext())
- {
- ContactSourceService contactSource
- = sourcesIter.next().getContactSourceService();
-
- srcContactList.addContactSource(
- demuxCSService.createDemuxContactSource(contactSource));
- }
- }
-
- /**
- * Initializes the left contact list with the contacts that could be added
- * to the current chat session.
- * @param protocolProvider the protocol provider from which to initialize
- * the contact list data
- */
- private void initContactListData(ProtocolProviderService protocolProvider)
- {
- this.setCurrentProvider(protocolProvider);
-
- Iterator<UIContactSource> sourcesIter
- = new ArrayList<UIContactSource>(
- srcContactList.getContactSources()).iterator();
-
- while (sourcesIter.hasNext())
- {
- ContactSourceService contactSource
- = sourcesIter.next().getContactSourceService();
-
- if (contactSource instanceof ProtocolAwareContactSourceService)
- {
- ((ProtocolAwareContactSourceService) contactSource)
- .setPreferredProtocolProvider(
- OperationSetBasicTelephony.class, protocolProvider);
- }
- }
-
- srcContactList.removeContactSource(currentProviderContactSource);
- srcContactList.removeContactSource(currentStringContactSource);
-
- currentProviderContactSource
- = new ProtocolContactSourceServiceImpl(
- protocolProvider,
- OperationSetBasicTelephony.class);
- currentStringContactSource
- = new StringContactSourceServiceImpl(
- protocolProvider,
- OperationSetBasicTelephony.class);
-
- srcContactList.addContactSource(currentProviderContactSource);
- srcContactList.addContactSource(currentStringContactSource);
-
- srcContactList.applyDefaultFilter();
- }
-
- /**
- * Invites the contacts to the chat conference.
- *
- * @param contacts the list of contacts to invite
- */
- private void inviteContacts(Collection<UIContact> contacts)
- {
- ProtocolProviderService selectedProvider;
- Map<ProtocolProviderService, List<String>> selectedProviderCallees
- = new HashMap<ProtocolProviderService, List<String>>();
- List<String> callees;
-
- Iterator<UIContact> contactsIter = contacts.iterator();
-
- while (contactsIter.hasNext())
- {
- UIContact uiContact = contactsIter.next();
-
- Iterator<UIContactDetail> contactDetailsIter = uiContact
- .getContactDetailsForOperationSet(
- OperationSetBasicTelephony.class).iterator();
-
- // We invite the first protocol contact that corresponds to the
- // invite provider.
- if (contactDetailsIter.hasNext())
- {
- UIContactDetail inviteDetail = contactDetailsIter.next();
- selectedProvider = inviteDetail
- .getPreferredProtocolProvider(
- OperationSetBasicTelephony.class);
-
- if (selectedProvider == null)
- {
- selectedProvider
- = (ProtocolProviderService)
- accountSelectorBox.getSelectedItem();
- }
-
- if(selectedProvider != null
- && selectedProviderCallees.get(selectedProvider) != null)
- {
- callees = selectedProviderCallees.get(selectedProvider);
- }
- else
- {
- callees = new ArrayList<String>();
- }
-
- callees.add(inviteDetail.getAddress());
- selectedProviderCallees.put(selectedProvider, callees);
- }
- }
-
- if(conference != null)
- {
- CallManager.inviteToConferenceCall(
- selectedProviderCallees,
- conference);
- }
- else
- {
- CallManager.createConferenceCall(selectedProviderCallees);
- }
- }
-
- /**
- * Invites the contacts to the chat conference.
- *
- * @param contacts the list of contacts to invite
- */
- private void inviteJitsiVideobridgeContacts(
- ProtocolProviderService preselectedProvider,
- Collection<UIContact> contacts)
- {
- List<String> callees = new ArrayList<String>();
-
- Iterator<UIContact> contactsIter = contacts.iterator();
-
- while (contactsIter.hasNext())
- {
- UIContact uiContact = contactsIter.next();
-
- Iterator<UIContactDetail> contactDetailsIter = uiContact
- .getContactDetailsForOperationSet(
- OperationSetBasicTelephony.class).iterator();
-
- // We invite the first protocol contact that corresponds to the
- // invite provider.
- if (contactDetailsIter.hasNext())
- {
- UIContactDetail inviteDetail = contactDetailsIter.next();
-
- callees.add(inviteDetail.getAddress());
- }
- }
-
- if(conference != null)
- {
- CallManager.inviteToJitsiVideobridgeConfCall(
- callees.toArray(new String[callees.size()]),
- conference.getCalls().get(0));
- }
- else
- {
- CallManager.createJitsiVideobridgeConfCall(
- preselectedProvider,
- callees.toArray(new String[callees.size()]));
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.call.conference;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.call.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * The invite dialog is the one shown when the user clicks on the conference
+ * button in the chat toolbar.
+ *
+ * @author Yana Stamcheva
+ * @author Lyubomir Marinov
+ */
+public class ConferenceInviteDialog
+ extends InviteDialog
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * The account selector box.
+ */
+ private final JComboBox accountSelectorBox = new JComboBox();
+
+ /**
+ * The last selected account.
+ */
+ private Object lastSelectedAccount;
+
+ /**
+ * The telephony conference into which this instance is to invite
+ * participants.
+ */
+ private final CallConference conference;
+
+ /**
+ * The current provider contact source.
+ */
+ private ContactSourceService currentProviderContactSource;
+
+ /**
+ * The current string contact source.
+ */
+ private ContactSourceService currentStringContactSource;
+
+ /**
+ * The previously selected protocol provider, with which this dialog has
+ * been instantiated.
+ */
+ private ProtocolProviderService preselectedProtocolProvider;
+
+ /**
+ * Indicates whether this conference invite dialog is associated with a
+ * Jitsi Videobridge invite.
+ */
+ private final boolean isJitsiVideobridge;
+
+ /**
+ * Initializes a new <tt>ConferenceInviteDialog</tt> instance which is to
+ * invite contacts/participants in a specific telephony conference.
+ *
+ * @param conference the telephony conference in which the new instance is
+ * to invite contacts/participants
+ */
+ public ConferenceInviteDialog(
+ CallConference conference,
+ ProtocolProviderService preselectedProvider,
+ List<ProtocolProviderService> protocolProviders,
+ final boolean isJitsiVideobridge)
+ {
+ // Set the correct dialog title depending if we're going to create a
+ // video bridge conference call
+ super((isJitsiVideobridge
+ ? GuiActivator.getResources()
+ .getI18NString("service.gui.INVITE_CONTACT_TO_VIDEO_BRIDGE")
+ : GuiActivator.getResources()
+ .getI18NString("service.gui.INVITE_CONTACT_TO_CALL")),
+ false);
+
+ this.conference = conference;
+ this.preselectedProtocolProvider = preselectedProvider;
+ this.isJitsiVideobridge = isJitsiVideobridge;
+
+ if (preselectedProtocolProvider == null)
+ initAccountSelectorPanel(protocolProviders);
+
+ // init the list, as we check whether features are supported
+ // it may take some time if we have too much contacts
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ initContactSources();
+
+ // Initialize the list of contacts to select from.
+ if (preselectedProtocolProvider != null)
+ initContactListData(preselectedProtocolProvider);
+ else
+ initContactListData(
+ (ProtocolProviderService) accountSelectorBox
+ .getSelectedItem());
+ }
+ });
+
+ this.addInviteButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ Collection<UIContact> selectedContacts
+ = destContactList.getContacts(null);
+
+ if (selectedContacts != null && selectedContacts.size() > 0)
+ {
+ if (preselectedProtocolProvider == null)
+ preselectedProtocolProvider
+ = (ProtocolProviderService) accountSelectorBox
+ .getSelectedItem();
+
+ if (isJitsiVideobridge)
+ inviteJitsiVideobridgeContacts( preselectedProtocolProvider,
+ selectedContacts);
+ else
+ inviteContacts(selectedContacts);
+
+ // Store the last used account in order to pre-select it
+ // next time.
+ ConfigurationUtils.setLastCallConferenceProvider(
+ preselectedProtocolProvider);
+
+ dispose();
+ }
+ else
+ {
+ // TODO: The underlying invite dialog should show a message
+ // to the user that she should select at least two contacts
+ // in order to create a conference.
+ }
+ }
+ });
+
+ this.addCancelButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ dispose();
+ }
+ });
+ }
+
+ /**
+ * Constructs the <tt>ConferenceInviteDialog</tt>.
+ */
+ public ConferenceInviteDialog()
+ {
+ this(null, null, null, false);
+ }
+
+ /**
+ * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
+ * already created conference. To use when inviting contacts to an existing
+ * conference is needed.
+ *
+ * @param conference the existing <tt>CallConference</tt>
+ */
+ public ConferenceInviteDialog(CallConference conference)
+ {
+ this(conference, null, null, false);
+ }
+
+ /**
+ * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
+ * already created conference. To use when inviting contacts to an existing
+ * conference is needed.
+ *
+ * @param conference the existing <tt>CallConference</tt>
+ */
+ public ConferenceInviteDialog(
+ CallConference conference,
+ ProtocolProviderService preselectedProtocolProvider,
+ boolean isJitsiVideobridge)
+ {
+ this(conference, preselectedProtocolProvider, null, isJitsiVideobridge);
+ }
+
+ /**
+ * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
+ * preselected protocol provider to be used and if this is an invite for
+ * a video bridge conference.
+ *
+ * @param protocolProviders the protocol providers list
+ * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
+ * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
+ */
+ public ConferenceInviteDialog(
+ List<ProtocolProviderService> protocolProviders,
+ boolean isJitsiVideobridge)
+ {
+ this(null, null, protocolProviders, isJitsiVideobridge);
+ }
+
+ /**
+ * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
+ * preselected protocol provider to be used and if this is an invite for
+ * a video bridge conference.
+ *
+ * @param selectedConfProvider the preselected protocol provider
+ * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
+ * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
+ */
+ public ConferenceInviteDialog(
+ ProtocolProviderService selectedConfProvider,
+ boolean isJitsiVideobridge)
+ {
+ this(null, selectedConfProvider, null, isJitsiVideobridge);
+ }
+
+ /**
+ * Initializes the account selector panel.
+ *
+ * @param protocolProviders the list of protocol providers we'd like to
+ * show in the account selector box
+ */
+ private void initAccountSelectorPanel(
+ List<ProtocolProviderService> protocolProviders)
+ {
+ JLabel accountSelectorLabel = new JLabel(
+ GuiActivator.getResources().getI18NString("service.gui.CALL_VIA"));
+
+ TransparentPanel accountSelectorPanel
+ = new TransparentPanel(new BorderLayout());
+
+ accountSelectorPanel.setBorder(
+ BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ accountSelectorPanel.add(accountSelectorLabel, BorderLayout.WEST);
+ accountSelectorPanel.add(accountSelectorBox, BorderLayout.CENTER);
+
+ // Initialize the account selector box.
+ if (protocolProviders != null && protocolProviders.size() > 0)
+ this.initAccountListData(protocolProviders);
+ else
+ this.initAccountListData();
+
+ this.accountSelectorBox.setRenderer(new DefaultListCellRenderer()
+ {
+ private static final long serialVersionUID = 0L;
+
+ @Override
+ public Component getListCellRendererComponent(JList list,
+ Object value, int index, boolean isSelected,
+ boolean cellHasFocus)
+ {
+ ProtocolProviderService protocolProvider
+ = (ProtocolProviderService) value;
+
+ if (protocolProvider != null)
+ {
+ this.setText(
+ protocolProvider.getAccountID().getDisplayName());
+ this.setIcon(
+ ImageLoader.getAccountStatusImage(protocolProvider));
+ }
+
+ return this;
+ }
+ });
+
+ this.accountSelectorBox.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ Object accountSelectorBoxSelectedItem
+ = accountSelectorBox.getSelectedItem();
+
+ if (lastSelectedAccount == null
+ || !lastSelectedAccount
+ .equals(accountSelectorBoxSelectedItem))
+ {
+ lastSelectedAccount = accountSelectorBoxSelectedItem;
+
+ initContactListData(
+ (ProtocolProviderService) accountSelectorBox
+ .getSelectedItem());
+
+ if (isJitsiVideobridge)
+ destContactList.removeAll();
+ }
+ }
+ });
+
+ this.getContentPane().add(accountSelectorPanel, BorderLayout.NORTH);
+ }
+
+ /**
+ * Initializes the account selector box with the given list of
+ * <tt>ProtocolProviderService</tt>-s.
+ *
+ * @param protocolProviders the list of <tt>ProtocolProviderService</tt>-s
+ * we'd like to show in the account selector box
+ */
+ private void initAccountListData(
+ List<ProtocolProviderService> protocolProviders)
+ {
+ Iterator<ProtocolProviderService> providersIter
+ = protocolProviders.iterator();
+
+ while (providersIter.hasNext())
+ {
+ ProtocolProviderService protocolProvider
+ = providersIter.next();
+
+ accountSelectorBox.addItem(protocolProvider);
+ }
+
+ if (accountSelectorBox.getItemCount() > 0)
+ accountSelectorBox.setSelectedIndex(0);
+ }
+
+ /**
+ * Initializes the account list.
+ */
+ private void initAccountListData()
+ {
+ Iterator<ProtocolProviderService> protocolProviders
+ = GuiActivator.getUIService().getMainFrame().getProtocolProviders();
+
+ while(protocolProviders.hasNext())
+ {
+ ProtocolProviderService protocolProvider
+ = protocolProviders.next();
+ OperationSet opSet
+ = protocolProvider.getOperationSet(
+ OperationSetTelephonyConferencing.class);
+
+ if ((opSet != null) && protocolProvider.isRegistered())
+ accountSelectorBox.addItem(protocolProvider);
+ }
+
+ // Try to select the last used account if available.
+ ProtocolProviderService pps
+ = ConfigurationUtils.getLastCallConferenceProvider();
+
+ if (pps == null && conference != null)
+ {
+ /*
+ * Pick up the first account from the ones participating in the
+ * associated telephony conference which supports
+ * OperationSetTelephonyConferencing.
+ */
+ for (Call call : conference.getCalls())
+ {
+ ProtocolProviderService callPps = call.getProtocolProvider();
+
+ if (callPps.getOperationSet(
+ OperationSetTelephonyConferencing.class)
+ != null)
+ {
+ pps = callPps;
+ break;
+ }
+ }
+ }
+
+ if (pps != null)
+ accountSelectorBox.setSelectedItem(pps);
+ else if (accountSelectorBox.getItemCount() > 0)
+ accountSelectorBox.setSelectedIndex(0);
+ }
+
+ /**
+ * Initializes contact list sources.
+ */
+ private void initContactSources()
+ {
+ DemuxContactSourceService demuxCSService
+ = GuiActivator.getDemuxContactSourceService();
+
+ // If the DemuxContactSourceService isn't registered we use the default
+ // contact source set.
+ if (demuxCSService == null)
+ return;
+
+ Iterator<UIContactSource> sourcesIter
+ = new ArrayList<UIContactSource>(
+ srcContactList.getContactSources()).iterator();
+
+ srcContactList.removeAllContactSources();
+
+ while (sourcesIter.hasNext())
+ {
+ ContactSourceService contactSource
+ = sourcesIter.next().getContactSourceService();
+
+ srcContactList.addContactSource(
+ demuxCSService.createDemuxContactSource(contactSource));
+ }
+ }
+
+ /**
+ * Initializes the left contact list with the contacts that could be added
+ * to the current chat session.
+ * @param protocolProvider the protocol provider from which to initialize
+ * the contact list data
+ */
+ private void initContactListData(ProtocolProviderService protocolProvider)
+ {
+ this.setCurrentProvider(protocolProvider);
+
+ Iterator<UIContactSource> sourcesIter
+ = new ArrayList<UIContactSource>(
+ srcContactList.getContactSources()).iterator();
+
+ while (sourcesIter.hasNext())
+ {
+ ContactSourceService contactSource
+ = sourcesIter.next().getContactSourceService();
+
+ if (contactSource instanceof ProtocolAwareContactSourceService)
+ {
+ ((ProtocolAwareContactSourceService) contactSource)
+ .setPreferredProtocolProvider(
+ OperationSetBasicTelephony.class, protocolProvider);
+ }
+ }
+
+ srcContactList.removeContactSource(currentProviderContactSource);
+ srcContactList.removeContactSource(currentStringContactSource);
+
+ currentProviderContactSource
+ = new ProtocolContactSourceServiceImpl(
+ protocolProvider,
+ OperationSetBasicTelephony.class);
+ currentStringContactSource
+ = new StringContactSourceServiceImpl(
+ protocolProvider,
+ OperationSetBasicTelephony.class);
+
+ srcContactList.addContactSource(currentProviderContactSource);
+ srcContactList.addContactSource(currentStringContactSource);
+
+ srcContactList.applyDefaultFilter();
+ }
+
+ /**
+ * Invites the contacts to the chat conference.
+ *
+ * @param contacts the list of contacts to invite
+ */
+ private void inviteContacts(Collection<UIContact> contacts)
+ {
+ ProtocolProviderService selectedProvider;
+ Map<ProtocolProviderService, List<String>> selectedProviderCallees
+ = new HashMap<ProtocolProviderService, List<String>>();
+ List<String> callees;
+
+ Iterator<UIContact> contactsIter = contacts.iterator();
+
+ while (contactsIter.hasNext())
+ {
+ UIContact uiContact = contactsIter.next();
+
+ Iterator<UIContactDetail> contactDetailsIter = uiContact
+ .getContactDetailsForOperationSet(
+ OperationSetBasicTelephony.class).iterator();
+
+ // We invite the first protocol contact that corresponds to the
+ // invite provider.
+ if (contactDetailsIter.hasNext())
+ {
+ UIContactDetail inviteDetail = contactDetailsIter.next();
+ selectedProvider = inviteDetail
+ .getPreferredProtocolProvider(
+ OperationSetBasicTelephony.class);
+
+ if (selectedProvider == null)
+ {
+ selectedProvider
+ = (ProtocolProviderService)
+ accountSelectorBox.getSelectedItem();
+ }
+
+ if(selectedProvider != null
+ && selectedProviderCallees.get(selectedProvider) != null)
+ {
+ callees = selectedProviderCallees.get(selectedProvider);
+ }
+ else
+ {
+ callees = new ArrayList<String>();
+ }
+
+ callees.add(inviteDetail.getAddress());
+ selectedProviderCallees.put(selectedProvider, callees);
+ }
+ }
+
+ if(conference != null)
+ {
+ CallManager.inviteToConferenceCall(
+ selectedProviderCallees,
+ conference);
+ }
+ else
+ {
+ CallManager.createConferenceCall(selectedProviderCallees);
+ }
+ }
+
+ /**
+ * Invites the contacts to the chat conference.
+ *
+ * @param contacts the list of contacts to invite
+ */
+ private void inviteJitsiVideobridgeContacts(
+ ProtocolProviderService preselectedProvider,
+ Collection<UIContact> contacts)
+ {
+ List<String> callees = new ArrayList<String>();
+
+ Iterator<UIContact> contactsIter = contacts.iterator();
+
+ while (contactsIter.hasNext())
+ {
+ UIContact uiContact = contactsIter.next();
+
+ Iterator<UIContactDetail> contactDetailsIter = uiContact
+ .getContactDetailsForOperationSet(
+ OperationSetBasicTelephony.class).iterator();
+
+ // We invite the first protocol contact that corresponds to the
+ // invite provider.
+ if (contactDetailsIter.hasNext())
+ {
+ UIContactDetail inviteDetail = contactDetailsIter.next();
+
+ callees.add(inviteDetail.getAddress());
+ }
+ }
+
+ if(conference != null)
+ {
+ CallManager.inviteToJitsiVideobridgeConfCall(
+ callees.toArray(new String[callees.size()]),
+ conference.getCalls().get(0));
+ }
+ else
+ {
+ CallManager.createJitsiVideobridgeConfCall(
+ preselectedProvider,
+ callees.toArray(new String[callees.size()]));
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java
index 0dd6ef5..07c653d 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,1395 +15,1395 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call.conference;
-
-import java.awt.*;
-import java.util.*;
-import java.util.List;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.main.call.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.plugin.desktoputil.TransparentPanel;
-
-import org.jitsi.util.swing.*;
-
-/**
- * Extends <tt>BasicConferenceCallPanel</tt> to implement a user interface
- * <tt>Component</tt> which depicts a <tt>CallConference</tt> with audio and
- * video and is contained in a <tt>CallPanel</tt>.
- *
- * @author Yana Stamcheva
- * @author Lyubomir Marinov
- */
-public class VideoConferenceCallPanel
- extends BasicConferenceCallPanel
-{
- /**
- * The <tt>Logger</tt> used by the <tt>VideoConferenceCallPanel</tt> class
- * and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(VideoConferenceCallPanel.class);
-
- /**
- * The compile-time flag which indicates whether each video displayed by
- * <tt>VideoConferenceCallPanel</tt> is to be depicted with an associated
- * tool bar showing information and controls related to the (local or
- * remote) peer sending the respective video.
- */
- private static final boolean SHOW_TOOLBARS = true;
-
- /**
- * The facility which aids this instance with the video-related information.
- */
- private final UIVideoHandler2 uiVideoHandler;
-
- /**
- * The <tt>Observer</tt> which listens to {@link #uiVideoHandler} about
- * changes in the video-related information.
- */
- private final Observer uiVideoHandlerObserver
- = new Observer()
- {
- public void update(Observable o, Object arg)
- {
- updateViewFromModel();
- }
- };
-
- /**
- * The <tt>VideoContainer</tt> which occupies this whole <tt>Component</tt>
- * and arranges the visual <tt>Component</tt>s displaying the video
- * streaming between the local peer/user and the remote peer(s).
- */
- private final VideoContainer videoContainer;
-
- /**
- * The set of visual <tt>Component</tt>s displaying video streaming between
- * the local peer/user and the remote peers which are depicted by this
- * instance.
- */
- private final Set<Component> videos = new HashSet<Component>();
-
- /**
- * The thumbnail container.
- */
- private final ThumbnailConferenceCallPanel thumbnailContainer;
-
- private final JPanel thumbnailPanel;
-
- /**
- * Initializes a new <tt>VideoConferenceCallPanel</tt> instance which is to
- * be used by a specific <tt>CallPanel</tt> to depict a specific
- * <tt>CallConference</tt>. The new instance will depict both the
- * audio-related and the video-related information.
- *
- * @param callPanel the <tt>CallPanel</tt> which will use the new instance
- * to depict the specified <tt>CallConference</tt>.
- * @param callConference the <tt>CallConference</tt> to be depicted by the
- * new instance
- * @param uiVideoHandler the utility which is to aid the new instance in
- * dealing with the video-related information
- */
- public VideoConferenceCallPanel(
- CallPanel callPanel,
- CallConference callConference,
- UIVideoHandler2 uiVideoHandler)
- {
- super(callPanel, callConference);
-
- this.uiVideoHandler = uiVideoHandler;
-
- thumbnailPanel = new JPanel(new BorderLayout());
- thumbnailContainer
- = new ThumbnailConferenceCallPanel( callPanel,
- callConference,
- uiVideoHandler);
-
- videoContainer = createVideoContainer();
-
- /*
- * Our user interface hierarchy has been initialized so we are ready to
- * begin receiving events warranting updates of this view from its
- * model.
- */
- uiVideoHandler.addObserver(uiVideoHandlerObserver);
-
- /*
- * Notify the super that this instance has completed its initialization
- * and the view that it implements is ready to be updated from the
- * model.
- */
- initializeComplete();
- }
-
- private void addConferenceMemberContainers(
- ConferenceParticipantContainer cpc)
- {
- List<ConferenceParticipantContainer> cmcs
- = cpc.conferenceMemberContainers;
-
- if ((cmcs != null) && !cmcs.isEmpty())
- {
- for (ConferenceParticipantContainer cmc : cmcs)
- {
- if (!cmc.toBeRemoved)
- {
- videoContainer.add(
- cmc.getComponent(),
- VideoLayout.CENTER_REMOTE);
- }
- }
- }
- }
-
- private Component createDefaultPhotoPanel(Call call)
- {
- OperationSetServerStoredAccountInfo accountInfo
- = call.getProtocolProvider().getOperationSet(
- OperationSetServerStoredAccountInfo.class);
- ImageIcon photoLabelIcon = null;
-
- if (accountInfo != null)
- {
- byte[] accountImage = AccountInfoUtils.getImage(accountInfo);
-
- // do not set empty images
- if ((accountImage != null) && (accountImage.length > 0))
- photoLabelIcon = new ImageIcon(accountImage);
- }
- if (photoLabelIcon == null)
- {
- photoLabelIcon
- = new ImageIcon(
- ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO));
- }
-
- return createDefaultPhotoPanel(photoLabelIcon);
- }
-
- private Component createDefaultPhotoPanel(CallPeer callPeer)
- {
- byte[] peerImage = CallManager.getPeerImage(callPeer);
- ImageIcon photoLabelIcon
- = (peerImage == null)
- ? new ImageIcon(
- ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO))
- : new ImageIcon(peerImage);
-
- return createDefaultPhotoPanel(photoLabelIcon);
- }
-
- private Component createDefaultPhotoPanel(ConferenceMember conferenceMember)
- {
- return
- createDefaultPhotoPanel(
- new ImageIcon(
- ImageLoader.getImage(
- ImageLoader.DEFAULT_USER_PHOTO)));
- }
-
- /**
- * Creates a new <tt>Component</tt> which is to display a specific
- * <tt>ImageIcon</tt> representing the photo of a participant in a call.
- *
- * @param photoLabelIcon the <tt>ImageIcon</tt> which represents the photo
- * of a participant in a call and which is to be displayed by the new
- * <tt>Component</tt>
- * @return a new <tt>Component</tt> which displays the specified
- * <tt>photoLabelIcon</tt>
- */
- private Component createDefaultPhotoPanel(ImageIcon photoLabelIcon)
- {
- JLabel photoLabel = new JLabel();
-
- photoLabel.setIcon(photoLabelIcon);
-
- @SuppressWarnings("serial")
- JPanel photoPanel
- = new TransparentPanel(new GridBagLayout())
- {
- /**
- * @{inheritDoc}
- */
- @Override
- public void paintComponent(Graphics g)
- {
- super.paintComponent(g);
-
- g = g.create();
- try
- {
- AntialiasingManager.activateAntialiasing(g);
-
- g.setColor(Color.GRAY);
- g.fillRoundRect(
- 0, 0, this.getWidth(), this.getHeight(),
- 6, 6);
- }
- finally
- {
- g.dispose();
- }
- }
- };
-
- photoPanel.setPreferredSize(new Dimension(320, 240));
-
- GridBagConstraints photoPanelConstraints = new GridBagConstraints();
-
- photoPanelConstraints.anchor = GridBagConstraints.CENTER;
- photoPanelConstraints.fill = GridBagConstraints.NONE;
- photoPanel.add(photoLabel, photoPanelConstraints);
-
- return photoPanel;
- }
-
- /**
- * Initializes a new <tt>VideoContainer</tt> instance which is to contain
- * the visual/video <tt>Component</tt>s of the telephony conference depicted
- * by this instance.
- */
- private VideoContainer createVideoContainer()
- {
- VideoContainer videoContainer = new VideoContainer(null, true);
-
- thumbnailPanel.setBackground(Color.DARK_GRAY);
- thumbnailPanel.add(thumbnailContainer, BorderLayout.NORTH);
-
- add(thumbnailPanel, BorderLayout.EAST);
- add(videoContainer, BorderLayout.CENTER);
-
- return videoContainer;
- }
-
- /**
- * Shows/hides the participants thumbnails list.
- *
- * @param show <tt>true</tt> to show the participants list, <tt>false</tt>
- * to hide it
- */
- public void showThumbnailsList(boolean show)
- {
- thumbnailPanel.setVisible(show);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void dispose()
- {
- try
- {
- uiVideoHandler.deleteObserver(uiVideoHandlerObserver);
- }
- finally
- {
- super.dispose();
- }
- }
-
- /**
- * Determines whether a specific <tt>ConferenceMember</tt> represents the
- * same conference participant as a specific <tt>CallPeer</tt>. If the
- * specified <tt>conferenceMember</tt> is <tt>null</tt>, returns
- * <tt>true</tt>. Otherwise, determines whether the addresses of the
- * specified <tt>conferenceMember</tt> and the specified <tt>callPeer</tt>
- * identify one and the same entity.
- *
- * @param conferenceMember the <tt>ConferenceMember</tt> to be checked
- * whether is represents the same conference participant as the specified
- * <tt>callPeer</tt>. If it is <tt>null</tt>, <tt>true</tt> is returned.
- * @param callPeer the <tt>CallPeer</tt> to be checked whether it represents
- * the same conference participant as the specified
- * <tt>conferenceMember</tt>
- * @return <tt>true</tt> if the specified <tt>conferenceMember</tt> and the
- * specified <tt>callPeer</tt> represent the same conference participant or
- * the specified <tt>conferenceMember</tt> is <tt>null</tt>; otherwise,
- * <tt>false</tt>
- */
- private boolean isConferenceMemberCallPeer(
- ConferenceMember conferenceMember,
- CallPeer callPeer)
- {
- return
- (conferenceMember == null)
- ? true
- : CallManager.addressesAreEqual(
- conferenceMember.getAddress(),
- callPeer.getAddress());
- }
-
- /**
- * Determines whether a specific <tt>ConferenceMember</tt> represents the
- * local peer/user. Since this instance depicts a whole telephony
- * conference, the local peer/user may be participating with multiple
- * <tt>Call</tt>s in it. The <tt>Call</tt>s may be through different
- * (local) accounts. That's why the implementation determines whether the
- * address of the specified <tt>conferenceMember</tt> identifies the address
- * of a (local) accounts involved in the telephony conference depicted by
- * this instance.
- *
- * @param conferenceMember the <tt>ConferenceMember</tt> to be checked
- * whether it represents the local peer/user
- * @return <tt>true</tt> if the specified <tt>conferenceMember</tt>
- * represents the local peer/user; otherwise, <tt>false</tt>
- */
- private boolean isConferenceMemberLocalUser(
- ConferenceMember conferenceMember)
- {
- String address = conferenceMember.getAddress();
-
- for (Call call : callConference.getCalls())
- {
- if (CallManager.addressesAreEqual(
- address,
- call.getProtocolProvider().getAccountID()
- .getAccountAddress()))
- {
- return true;
- }
- }
- return false;
- }
-
- private void removeConferenceMemberContainers(
- ConferenceParticipantContainer cpc,
- boolean all)
- {
- List<ConferenceParticipantContainer> cmcs
- = cpc.conferenceMemberContainers;
-
- if ((cmcs != null) && !cmcs.isEmpty())
- {
- Iterator<ConferenceParticipantContainer> i = cmcs.iterator();
-
- while (i.hasNext())
- {
- ConferenceParticipantContainer cmc = i.next();
-
- if (all || cmc.toBeRemoved)
- {
- i.remove();
-
- videoContainer.remove(cmc.getComponent());
- cmc.dispose();
- }
- }
- }
- }
-
- /**
- * Updates the <tt>ConferenceParticipantContainer</tt>s which depict the
- * <tt>ConferenceMember</tt>s of the <tt>CallPeer</tt> depicted by a
- * specific <tt>ConferenceParticipantContainer</tt>.
- *
- * @param cpc the <tt>ConferenceParticipantContainer</tt> which depicts the
- * <tt>CallPeer</tt> whose <tt>ConferenceMember</tt>s are to be depicted
- * @param videos the visual <tt>Component</tt>s displaying video streaming
- * from the remote peer (represented by <tt>cpc</tt>) to the local peer/user
- * @param videoTelephony the <tt>OperationSetVideoTelephony</tt> which
- * retrieved the specified <tt>videos</tt> from the <tt>CallPeer</tt>
- * depicted by <tt>cpc</tt>. While the <tt>CallPeer</tt> could be queried
- * for it, such a query would waste more resources at run time given that
- * the invoker has it already.
- */
- private void updateConferenceMemberContainers(
- ConferenceParticipantContainer cpc,
- List<Component> videos,
- OperationSetVideoTelephony videoTelephony)
- {
- CallPeer callPeer = (CallPeer) cpc.getParticipant();
- List<ConferenceParticipantContainer> cmcs
- = cpc.conferenceMemberContainers;
-
- /*
- * Invalidate all conferenceMemberContainers. Then validate which of
- * them are to remain and which of them are to really be removed
- * later on.
- */
- if (cmcs != null)
- {
- for (ConferenceParticipantContainer cmc : cmcs)
- cmc.toBeRemoved = true;
- }
-
- /*
- * Depict the remote videos. They may or may not be associated with
- * ConferenceMembers so the ConferenceMembers which have no
- * associated videos will be depicted afterwards.
- */
- if (videos != null)
- {
- Component video = cpc.getVideo();
-
- for (Component conferenceMemberVideo : videos)
- {
- /*
- * One of the remote videos is already used to depict the
- * callPeer.
- */
- if (conferenceMemberVideo == video)
- continue;
-
- boolean addNewConferenceParticipantContainer = true;
- ConferenceMember conferenceMember
- = videoTelephony.getConferenceMember(
- callPeer,
- conferenceMemberVideo);
-
- if (cmcs == null)
- {
- cmcs = new LinkedList<ConferenceParticipantContainer>();
- cpc.conferenceMemberContainers = cmcs;
- }
- else
- {
- for (ConferenceParticipantContainer cmc : cmcs)
- {
- Object cmcParticipant = cmc.getParticipant();
-
- if (conferenceMember == null)
- {
- if (cmcParticipant == callPeer)
- {
- Component cmcVideo = cmc.getVideo();
-
- if (cmcVideo == null)
- {
- cmc.setVideo(conferenceMemberVideo);
- cmc.toBeRemoved = false;
- addNewConferenceParticipantContainer
- = false;
- break;
- }
- else if (cmcVideo == conferenceMemberVideo)
- {
- cmc.toBeRemoved = false;
- addNewConferenceParticipantContainer
- = false;
- break;
- }
- }
- }
- else if (cmcParticipant == conferenceMember)
- {
- cmc.setVideo(conferenceMemberVideo);
- cmc.toBeRemoved = false;
- addNewConferenceParticipantContainer = false;
- break;
- }
- }
- }
-
- if (addNewConferenceParticipantContainer)
- {
- ConferenceParticipantContainer cmc
- = (conferenceMember == null)
- ? new ConferenceParticipantContainer(
- callPeer,
- conferenceMemberVideo)
- : new ConferenceParticipantContainer(
- conferenceMember,
- conferenceMemberVideo);
-
- cmcs.add(cmc);
- }
- }
- }
-
- /*
- * Depict the ConferenceMembers which have not been depicted yet.
- * They have no associated videos.
- */
- List<ConferenceMember> conferenceMembers
- = callPeer.getConferenceMembers();
-
- if (!conferenceMembers.isEmpty())
- {
- if (cmcs == null)
- {
- cmcs = new LinkedList<ConferenceParticipantContainer>();
- cpc.conferenceMemberContainers = cmcs;
- }
- for (ConferenceMember conferenceMember : conferenceMembers)
- {
- /*
- * If the callPeer reports itself as a ConferenceMember, then
- * we've already depicted it with cpc.
- */
- if (isConferenceMemberCallPeer(conferenceMember, callPeer))
- continue;
- /*
- * If the callPeer reports the local peer/user as a
- * ConferenceMember, then we've already depicted it.
- */
- if (isConferenceMemberLocalUser(conferenceMember))
- continue;
-
- boolean addNewConferenceParticipantContainer = true;
-
- for (ConferenceParticipantContainer cmc : cmcs)
- {
- if (cmc.getParticipant() == conferenceMember)
- {
- /*
- * It is possible to have a ConferenceMember who is
- * sending video but we just do not have the SSRC of
- * that video to associate the video with the
- * ConferenceMember. In such a case, we may be depicting
- * the ConferenceMember twice: once with video without a
- * ConferenceMember and once with a ConferenceMember
- * without video. This will surely be the case at the
- * time of this writing with non-focus participants in a
- * telephony conference hosted on a Jitsi Videobridge.
- * Such a display is undesirable. If the
- * conferenceMember is known to send video, we will not
- * display it until we associated it with a video. This
- * way, if a ConferenceMember is not sending video, we
- * will depict it and we can be sure that no video
- * without a ConferenceMember association will be
- * depicting it a second time.
- */
- if (cmc.toBeRemoved
- && !conferenceMember
- .getVideoStatus()
- .allowsSending())
- {
- cmc.setVideo(null);
- cmc.toBeRemoved = false;
- }
- addNewConferenceParticipantContainer = false;
- break;
- }
- }
-
- if (addNewConferenceParticipantContainer)
- {
- ConferenceParticipantContainer cmc
- = new ConferenceParticipantContainer(
- conferenceMember,
- null);
-
- cmcs.add(cmc);
- }
- }
- }
-
- if ((cmcs != null) && !cmcs.isEmpty())
- {
- removeConferenceMemberContainers(cpc, false);
- /*
- * If cpc is already added to the user interface hierarchy of this
- * instance, then it was there before the update procedure and it
- * was determined to be appropriate to continue to depict its model.
- * Consequently, its Component will be neither added to (because it
- * was already added) nor removed from the user interface hierarchy
- * of this instance. That's why we have make sure that the
- * Components of its conferenceMemberContainers are also added to
- * the user interface.
- */
- if (UIVideoHandler2.isAncestor(this, cpc.getComponent()))
- addConferenceMemberContainers(cpc);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected ConferenceCallPeerRenderer updateViewFromModel(
- ConferenceCallPeerRenderer callPeerPanel,
- CallPeer callPeer)
- {
- if (callPeer == null)
- {
- /*
- * The local peer/user will be represented by a Call which has a
- * CallPeer who provides local video. However, if the user has
- * selected to hide the local video, the local peer/user will not be
- * represented at all.
- */
- Component video = null;
-
- if (uiVideoHandler.isLocalVideoVisible())
- {
- for (Call aCall : callConference.getCalls())
- {
- Iterator<? extends CallPeer> callPeerIter
- = aCall.getCallPeers();
- OperationSetVideoTelephony videoTelephony
- = aCall.getProtocolProvider().getOperationSet(
- OperationSetVideoTelephony.class);
-
- while (callPeerIter.hasNext())
- {
- callPeer = callPeerIter.next();
-
- if (videoTelephony != null)
- {
- try
- {
- video
- = videoTelephony.getLocalVisualComponent(
- callPeer);
- }
- catch (OperationFailedException ofe)
- {
- logger.error(
- "Failed to retrieve the local video"
- + " for display",
- ofe);
- }
- if (video != null)
- break;
- }
- }
- if (video != null)
- break;
- }
- }
-
- if (callPeer == null)
- callPeerPanel = null;
- else
- {
- Call call = callPeer.getCall();
-
- if (callPeerPanel instanceof ConferenceParticipantContainer)
- {
- ConferenceParticipantContainer cpc
- = (ConferenceParticipantContainer) callPeerPanel;
-
- if (cpc.getParticipant() == call)
- cpc.setVideo(video);
- else
- callPeerPanel = null;
- }
- else
- callPeerPanel = null;
- if (callPeerPanel == null)
- {
- callPeerPanel
- = new ConferenceParticipantContainer(call, video);
- }
- }
- }
- else
- {
- /*
- * The specified callPeer will be represented by one of its remote
- * videos which is not associated with a ConferenceMember or is
- * associated with a ConferenceMember representing the callPeer
- * itself.
- */
- OperationSetVideoTelephony videoTelephony
- = callPeer.getProtocolProvider().getOperationSet(
- OperationSetVideoTelephony.class);
- List<Component> videos = null;
- Component video = null;
-
- if (videoTelephony != null)
- {
- videos = videoTelephony.getVisualComponents(callPeer);
- if ((videos != null) && !videos.isEmpty())
- {
- for (Component aVideo : videos)
- {
- ConferenceMember conferenceMember
- = videoTelephony.getConferenceMember(
- callPeer,
- aVideo);
-
- if (isConferenceMemberCallPeer(
- conferenceMember,
- callPeer))
- {
- video = aVideo;
- break;
- }
- }
- }
- }
-
- ConferenceParticipantContainer cpc = null;
-
- if (callPeerPanel instanceof ConferenceParticipantContainer)
- {
- cpc = (ConferenceParticipantContainer) callPeerPanel;
- if (cpc.getParticipant() == callPeer)
- cpc.setVideo(video);
- else
- cpc = null;
- }
- if (cpc == null)
- cpc = new ConferenceParticipantContainer(callPeer, video);
- callPeerPanel = cpc;
-
- // Update the conferenceMemberContainers of the cpc.
- updateConferenceMemberContainers(cpc, videos, videoTelephony);
- }
- return callPeerPanel;
- }
-
- /**
- * {@inheritDoc}
- *
- * If {@link #SHOW_TOOLBARS} is <tt>false</tt>, disables the use of
- * <tt>ConferenceParticipantContainer</tt>. A reason for such a value of
- * <tt>SHOW_TOOLBARS</tt> may be that the functionality implemented in the
- * model may not fully support mapping of visual <tt>Component</tt>s
- * displaying video to telephony conference participants (e.g. in telephony
- * conferences utilizing the Jitsi Videobridge server-side technology). In
- * such a case displays the videos only, does not map videos to participants
- * and does not display participants who do not have videos.
- */
- @Override
- protected void updateViewFromModelInEventDispatchThread()
- {
- if (SHOW_TOOLBARS)
- {
- super.updateViewFromModelInEventDispatchThread();
- return;
- }
-
- /*
- * Determine the set of visual Components displaying video streaming
- * between the local peer/user and the remote peers which are to be
- * depicted by this instance.
- */
- Component localVideo = null;
- Set<Component> videos = new HashSet<Component>();
-
- for (Call call : callConference.getCalls())
- {
- OperationSetVideoTelephony videoTelephony
- = call.getProtocolProvider().getOperationSet(
- OperationSetVideoTelephony.class);
-
- if (videoTelephony == null)
- continue;
-
- Iterator<? extends CallPeer> callPeerIter = call.getCallPeers();
-
- while (callPeerIter.hasNext())
- {
- CallPeer callPeer = callPeerIter.next();
-
- /*
- * TODO VideoConferenceCallPanel respects
- * UIVideoHandler2.isLocalVideoVisible() in order to react to
- * the associated button at the bottom of the CallPanel.
- * However, it does not add a close button on top of the local
- * video in contrast to OneToOneCallPeerPanel. Overall, the
- * result is questionable.
- */
- if (uiVideoHandler.isLocalVideoVisible()
- && (localVideo == null))
- {
- try
- {
- localVideo
- = videoTelephony.getLocalVisualComponent(callPeer);
- }
- catch (OperationFailedException ofe)
- {
- /*
- * We'll just try to get the local video through another
- * CallPeer then.
- */
- }
- if (localVideo != null)
- videos.add(localVideo);
- }
-
- List<Component> callPeerRemoteVideos
- = videoTelephony.getVisualComponents(callPeer);
-
- videos.addAll(callPeerRemoteVideos);
- }
- }
-
- /*
- * Remove the Components of this view which are no longer present in the
- * model.
- */
- Iterator<Component> thisVideoIter = this.videos.iterator();
-
- while (thisVideoIter.hasNext())
- {
- Component thisVideo = thisVideoIter.next();
-
- if (!videos.contains(thisVideo))
- {
- thisVideoIter.remove();
- videoContainer.remove(thisVideo);
- }
-
- /*
- * If a video is known to be depicted by this view and is still
- * present in the model, then we could remove it from the set of
- * videos present in the model in order to prevent going through the
- * procedure of adding it to this view. However, we choose to play
- * on the safe side.
- */
- }
-
- /*
- * Add the Components of the model which are not depicted by this view.
- */
- for (Component video : videos)
- {
- if (!UIVideoHandler2.isAncestor(videoContainer, video))
- {
- this.videos.add(video);
- videoContainer.add(
- video,
- (video == localVideo)
- ? VideoLayout.LOCAL
- : VideoLayout.CENTER_REMOTE);
- }
- }
- }
-
- @Override
- protected void viewForModelAdded(
- ConferenceCallPeerRenderer callPeerPanel,
- CallPeer callPeer)
- {
- videoContainer.add(
- callPeerPanel.getComponent(),
- VideoLayout.CENTER_REMOTE);
- if ((callPeer != null)
- && (callPeerPanel instanceof ConferenceParticipantContainer))
- {
- addConferenceMemberContainers(
- (ConferenceParticipantContainer) callPeerPanel);
- }
- }
-
- @Override
- protected void viewForModelRemoved(
- ConferenceCallPeerRenderer callPeerPanel,
- CallPeer callPeer)
- {
- videoContainer.remove(callPeerPanel.getComponent());
- if ((callPeer != null)
- && (callPeerPanel instanceof ConferenceParticipantContainer))
- {
- removeConferenceMemberContainers(
- (ConferenceParticipantContainer) callPeerPanel,
- true);
- }
- }
-
- /**
- * Implements an AWT <tt>Component</tt> which contains the user interface
- * elements depicting a specific participant in the telephony conference
- * depicted by a <tt>VideoConferenceCallPanel</tt>.
- */
- private class ConferenceParticipantContainer
- extends TransparentPanel
- implements ConferenceCallPeerRenderer
- {
- /**
- * The list of <tt>ConferenceParticipantContainer</tt>s which represent
- * the <tt>ConferenceMember</tt>s of the participant represented by this
- * instance. Since a <tt>CallPeer</tt> may send the local peer/user
- * multiple videos without providing a way to associate a
- * ConferenceMember with each one of them, the list may contain
- * <tt>ConferenceParticipantContainer</tt>s which do not represent a
- * specific <tt>ConferenceMember</tt> instance but rather a video sent
- * by a <tt>CallPeer</tt> to the local peer/user which looks like (in
- * the terms of <tt>VideoConferenceCallPanel) a member of a conference
- * organized by the <tt>CallPeer</tt> in question.
- * <p>
- * Implements a state which is private to
- * <tt>VideoConferenceCallPanel</tt> and is of no concern to
- * <tt>ConferenceParticipantContainer</tt>.
- * </p>
- */
- List<ConferenceParticipantContainer> conferenceMemberContainers;
-
- /**
- * The indicator which determines whether this instance is to be removed
- * because it has become out-of-date, obsolete, unnecessary.
- * <p>
- * Implements a state which is private to
- * <tt>VideoConferenceCallPanel</tt> and is of no concern to
- * <tt>ConferenceParticipantContainer</tt>.
- * </p>
- */
- boolean toBeRemoved;
-
- /**
- * The <tt>BasicConferenceParticipantPanel</tt> which is displayed at
- * the bottom of this instance, bellow the {@link #video} (i.e.
- * {@link #videoContainer}) and is referred to as the tool bar.
- */
- private final BasicConferenceParticipantPanel<?> toolBar;
-
- /**
- * The visual <tt>Component</tt>, if any, displaying video which is
- * depicted by this instance.
- */
- private Component video;
-
- /**
- * The <tt>VideoContainer</tt> which lays out the video depicted by this
- * instance i.e. {@link #video}.
- */
- private final VideoContainer videoContainer;
-
- /**
- * The <tt>CallPeer</tt> associated with this container, if it has been
- * created to represent a <tt>CallPeer</tt>.
- */
- private CallPeer callPeer;
-
- /**
- * The <tt>conferenceMember</tt> associated with this container, if it
- * has been created to represent a <tt>conferenceMember</tt>.
- */
- private ConferenceMember conferenceMember;
-
- /**
- * Indicates that this container contains information for the local
- * user.
- */
- private boolean isLocalUser;
-
- /**
- * Initializes a new <tt>ConferenceParticipantContainer</tt> instance
- * which is to depict the local peer/user.
- *
- * @param call a <tt>Call</tt> which is to provide information about the
- * local peer/user
- * @param video the visual <tt>Component</tt>, if any, displaying the
- * video streaming from the local peer/user to the remote peer(s)
- */
- public ConferenceParticipantContainer(Call call, Component video)
- {
- this(
- createDefaultPhotoPanel(call),
- video,
- new ConferencePeerPanel(
- VideoConferenceCallPanel.this,
- call,
- true),
- null, null, true);
- }
-
- public ConferenceParticipantContainer(
- CallPeer callPeer,
- Component video)
- {
- this( createDefaultPhotoPanel(callPeer),
- video,
- new ConferencePeerPanel(
- VideoConferenceCallPanel.this,
- callPeer,
- true),
- callPeer, null, false);
- }
-
- private ConferenceParticipantContainer(
- Component noVideo,
- Component video,
- BasicConferenceParticipantPanel<?> toolBar,
- CallPeer callPeer,
- ConferenceMember conferenceMember,
- boolean isLocalUser)
- {
- super(new BorderLayout());
-
- this.callPeer = callPeer;
- this.conferenceMember = conferenceMember;
- this.isLocalUser = isLocalUser;
-
- videoContainer = new VideoContainer(noVideo, false);
- add(videoContainer, BorderLayout.CENTER);
-
- this.toolBar = toolBar;
- if (this.toolBar != null)
- add(this.toolBar, BorderLayout.SOUTH);
-
- if (video != null)
- {
- setVideo(video);
- }
- else
- setVisible(false);
- }
-
- public ConferenceParticipantContainer(
- ConferenceMember conferenceMember,
- Component video)
- {
- this(
- createDefaultPhotoPanel(conferenceMember),
- video,
- new ConferenceMemberPanel(
- VideoConferenceCallPanel.this,
- conferenceMember,
- true),
- null, conferenceMember, false);
- }
-
- public void dispose()
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.dispose();
-
- // Dispose of the conferenceMemberContainers if any.
- /*
- * XXX The field conferenceMemberContainers implements a state
- * private to VideoConferenceCallPanel which the latter makes sure
- * to access on the AWT event dispatching thread only. Since we are
- * going out of our way here to help VideoConferenceCallPanel,
- * ensure that the mentioned synchronization rule is not violated.
- */
- CallManager.assertIsEventDispatchingThread();
- if (conferenceMemberContainers != null)
- {
- for (ConferenceParticipantContainer cmc
- : conferenceMemberContainers)
- {
- cmc.dispose();
- }
- }
- }
-
- public CallPanel getCallPanel()
- {
- return getCallRenderer().getCallContainer();
- }
-
- public SwingCallRenderer getCallRenderer()
- {
- return VideoConferenceCallPanel.this;
- }
-
- public Component getComponent()
- {
- return this;
- }
-
- private ConferenceCallPeerRenderer
- getConferenceCallPeerRendererDelegate()
- {
- return
- (toolBar instanceof ConferenceCallPeerRenderer)
- ? (ConferenceCallPeerRenderer) toolBar
- : null;
- }
-
- /**
- * Gets the conference participant depicted by this instance.
- *
- * @return the conference participant depicted by this instance
- */
- public Object getParticipant()
- {
- return (toolBar == null) ? null : toolBar.getParticipant();
- }
-
- public Component getVideo()
- {
- return video;
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only. Otherwise, returns <tt>false</tt>.
- */
- public boolean isLocalVideoVisible()
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- return (delegate == null) ? false : delegate.isLocalVideoVisible();
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void printDTMFTone(char dtmfChar)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.printDTMFTone(dtmfChar);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityNegotiationStarted(
- CallPeerSecurityNegotiationStartedEvent ev)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityNegotiationStarted(ev);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityOff(CallPeerSecurityOffEvent ev)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityOff(ev);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityOn(CallPeerSecurityOnEvent ev)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityOn(ev);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityPending()
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityPending();
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityTimeout(CallPeerSecurityTimeoutEvent ev)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityTimeout(ev);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setErrorReason(String reason)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setErrorReason(reason);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setLocalVideoVisible(boolean visible)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setLocalVideoVisible(visible);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setMute(boolean mute)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setMute(mute);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setOnHold(boolean onHold)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setOnHold(onHold);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setPeerImage(byte[] image)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setPeerImage(image);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setPeerName(String name)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setPeerName(name);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setPeerState(
- CallPeerState oldState,
- CallPeerState newState,
- String stateString)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setPeerState(oldState, newState, stateString);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setSecurityPanelVisible(boolean visible)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setSecurityPanelVisible(visible);
- }
-
- /**
- * Sets the visual <tt>Component</tt> displaying the video associated
- * with the participant depicted by this instance.
- *
- * @param video the visual <tt>Component</tt> displaying video which is
- * to be associated with the participant depicted by this instance
- */
- void setVideo(Component video)
- {
- if (this.video != video)
- {
- if (this.video != null)
- videoContainer.remove(this.video);
-
- this.video = video;
-
- if (this.video != null)
- {
- setVisible(true);
- videoContainer.add(this.video, VideoLayout.CENTER_REMOTE);
- }
- else
- setVisible(false);
-
- // Update thumbnails container according to video status.
- if (thumbnailContainer != null)
- {
- if (conferenceMember != null)
- thumbnailContainer
- .updateThumbnail(conferenceMember, (video != null));
- else if (callPeer != null)
- thumbnailContainer
- .updateThumbnail(callPeer, (video != null));
- else if (isLocalUser)
- thumbnailContainer
- .updateThumbnail((video != null));
- }
- }
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.call.conference;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.main.call.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.plugin.desktoputil.TransparentPanel;
+
+import org.jitsi.util.swing.*;
+
+/**
+ * Extends <tt>BasicConferenceCallPanel</tt> to implement a user interface
+ * <tt>Component</tt> which depicts a <tt>CallConference</tt> with audio and
+ * video and is contained in a <tt>CallPanel</tt>.
+ *
+ * @author Yana Stamcheva
+ * @author Lyubomir Marinov
+ */
+public class VideoConferenceCallPanel
+ extends BasicConferenceCallPanel
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>VideoConferenceCallPanel</tt> class
+ * and its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(VideoConferenceCallPanel.class);
+
+ /**
+ * The compile-time flag which indicates whether each video displayed by
+ * <tt>VideoConferenceCallPanel</tt> is to be depicted with an associated
+ * tool bar showing information and controls related to the (local or
+ * remote) peer sending the respective video.
+ */
+ private static final boolean SHOW_TOOLBARS = true;
+
+ /**
+ * The facility which aids this instance with the video-related information.
+ */
+ private final UIVideoHandler2 uiVideoHandler;
+
+ /**
+ * The <tt>Observer</tt> which listens to {@link #uiVideoHandler} about
+ * changes in the video-related information.
+ */
+ private final Observer uiVideoHandlerObserver
+ = new Observer()
+ {
+ public void update(Observable o, Object arg)
+ {
+ updateViewFromModel();
+ }
+ };
+
+ /**
+ * The <tt>VideoContainer</tt> which occupies this whole <tt>Component</tt>
+ * and arranges the visual <tt>Component</tt>s displaying the video
+ * streaming between the local peer/user and the remote peer(s).
+ */
+ private final VideoContainer videoContainer;
+
+ /**
+ * The set of visual <tt>Component</tt>s displaying video streaming between
+ * the local peer/user and the remote peers which are depicted by this
+ * instance.
+ */
+ private final Set<Component> videos = new HashSet<Component>();
+
+ /**
+ * The thumbnail container.
+ */
+ private final ThumbnailConferenceCallPanel thumbnailContainer;
+
+ private final JPanel thumbnailPanel;
+
+ /**
+ * Initializes a new <tt>VideoConferenceCallPanel</tt> instance which is to
+ * be used by a specific <tt>CallPanel</tt> to depict a specific
+ * <tt>CallConference</tt>. The new instance will depict both the
+ * audio-related and the video-related information.
+ *
+ * @param callPanel the <tt>CallPanel</tt> which will use the new instance
+ * to depict the specified <tt>CallConference</tt>.
+ * @param callConference the <tt>CallConference</tt> to be depicted by the
+ * new instance
+ * @param uiVideoHandler the utility which is to aid the new instance in
+ * dealing with the video-related information
+ */
+ public VideoConferenceCallPanel(
+ CallPanel callPanel,
+ CallConference callConference,
+ UIVideoHandler2 uiVideoHandler)
+ {
+ super(callPanel, callConference);
+
+ this.uiVideoHandler = uiVideoHandler;
+
+ thumbnailPanel = new JPanel(new BorderLayout());
+ thumbnailContainer
+ = new ThumbnailConferenceCallPanel( callPanel,
+ callConference,
+ uiVideoHandler);
+
+ videoContainer = createVideoContainer();
+
+ /*
+ * Our user interface hierarchy has been initialized so we are ready to
+ * begin receiving events warranting updates of this view from its
+ * model.
+ */
+ uiVideoHandler.addObserver(uiVideoHandlerObserver);
+
+ /*
+ * Notify the super that this instance has completed its initialization
+ * and the view that it implements is ready to be updated from the
+ * model.
+ */
+ initializeComplete();
+ }
+
+ private void addConferenceMemberContainers(
+ ConferenceParticipantContainer cpc)
+ {
+ List<ConferenceParticipantContainer> cmcs
+ = cpc.conferenceMemberContainers;
+
+ if ((cmcs != null) && !cmcs.isEmpty())
+ {
+ for (ConferenceParticipantContainer cmc : cmcs)
+ {
+ if (!cmc.toBeRemoved)
+ {
+ videoContainer.add(
+ cmc.getComponent(),
+ VideoLayout.CENTER_REMOTE);
+ }
+ }
+ }
+ }
+
+ private Component createDefaultPhotoPanel(Call call)
+ {
+ OperationSetServerStoredAccountInfo accountInfo
+ = call.getProtocolProvider().getOperationSet(
+ OperationSetServerStoredAccountInfo.class);
+ ImageIcon photoLabelIcon = null;
+
+ if (accountInfo != null)
+ {
+ byte[] accountImage = AccountInfoUtils.getImage(accountInfo);
+
+ // do not set empty images
+ if ((accountImage != null) && (accountImage.length > 0))
+ photoLabelIcon = new ImageIcon(accountImage);
+ }
+ if (photoLabelIcon == null)
+ {
+ photoLabelIcon
+ = new ImageIcon(
+ ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO));
+ }
+
+ return createDefaultPhotoPanel(photoLabelIcon);
+ }
+
+ private Component createDefaultPhotoPanel(CallPeer callPeer)
+ {
+ byte[] peerImage = CallManager.getPeerImage(callPeer);
+ ImageIcon photoLabelIcon
+ = (peerImage == null)
+ ? new ImageIcon(
+ ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO))
+ : new ImageIcon(peerImage);
+
+ return createDefaultPhotoPanel(photoLabelIcon);
+ }
+
+ private Component createDefaultPhotoPanel(ConferenceMember conferenceMember)
+ {
+ return
+ createDefaultPhotoPanel(
+ new ImageIcon(
+ ImageLoader.getImage(
+ ImageLoader.DEFAULT_USER_PHOTO)));
+ }
+
+ /**
+ * Creates a new <tt>Component</tt> which is to display a specific
+ * <tt>ImageIcon</tt> representing the photo of a participant in a call.
+ *
+ * @param photoLabelIcon the <tt>ImageIcon</tt> which represents the photo
+ * of a participant in a call and which is to be displayed by the new
+ * <tt>Component</tt>
+ * @return a new <tt>Component</tt> which displays the specified
+ * <tt>photoLabelIcon</tt>
+ */
+ private Component createDefaultPhotoPanel(ImageIcon photoLabelIcon)
+ {
+ JLabel photoLabel = new JLabel();
+
+ photoLabel.setIcon(photoLabelIcon);
+
+ @SuppressWarnings("serial")
+ JPanel photoPanel
+ = new TransparentPanel(new GridBagLayout())
+ {
+ /**
+ * @{inheritDoc}
+ */
+ @Override
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+
+ g = g.create();
+ try
+ {
+ AntialiasingManager.activateAntialiasing(g);
+
+ g.setColor(Color.GRAY);
+ g.fillRoundRect(
+ 0, 0, this.getWidth(), this.getHeight(),
+ 6, 6);
+ }
+ finally
+ {
+ g.dispose();
+ }
+ }
+ };
+
+ photoPanel.setPreferredSize(new Dimension(320, 240));
+
+ GridBagConstraints photoPanelConstraints = new GridBagConstraints();
+
+ photoPanelConstraints.anchor = GridBagConstraints.CENTER;
+ photoPanelConstraints.fill = GridBagConstraints.NONE;
+ photoPanel.add(photoLabel, photoPanelConstraints);
+
+ return photoPanel;
+ }
+
+ /**
+ * Initializes a new <tt>VideoContainer</tt> instance which is to contain
+ * the visual/video <tt>Component</tt>s of the telephony conference depicted
+ * by this instance.
+ */
+ private VideoContainer createVideoContainer()
+ {
+ VideoContainer videoContainer = new VideoContainer(null, true);
+
+ thumbnailPanel.setBackground(Color.DARK_GRAY);
+ thumbnailPanel.add(thumbnailContainer, BorderLayout.NORTH);
+
+ add(thumbnailPanel, BorderLayout.EAST);
+ add(videoContainer, BorderLayout.CENTER);
+
+ return videoContainer;
+ }
+
+ /**
+ * Shows/hides the participants thumbnails list.
+ *
+ * @param show <tt>true</tt> to show the participants list, <tt>false</tt>
+ * to hide it
+ */
+ public void showThumbnailsList(boolean show)
+ {
+ thumbnailPanel.setVisible(show);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dispose()
+ {
+ try
+ {
+ uiVideoHandler.deleteObserver(uiVideoHandlerObserver);
+ }
+ finally
+ {
+ super.dispose();
+ }
+ }
+
+ /**
+ * Determines whether a specific <tt>ConferenceMember</tt> represents the
+ * same conference participant as a specific <tt>CallPeer</tt>. If the
+ * specified <tt>conferenceMember</tt> is <tt>null</tt>, returns
+ * <tt>true</tt>. Otherwise, determines whether the addresses of the
+ * specified <tt>conferenceMember</tt> and the specified <tt>callPeer</tt>
+ * identify one and the same entity.
+ *
+ * @param conferenceMember the <tt>ConferenceMember</tt> to be checked
+ * whether is represents the same conference participant as the specified
+ * <tt>callPeer</tt>. If it is <tt>null</tt>, <tt>true</tt> is returned.
+ * @param callPeer the <tt>CallPeer</tt> to be checked whether it represents
+ * the same conference participant as the specified
+ * <tt>conferenceMember</tt>
+ * @return <tt>true</tt> if the specified <tt>conferenceMember</tt> and the
+ * specified <tt>callPeer</tt> represent the same conference participant or
+ * the specified <tt>conferenceMember</tt> is <tt>null</tt>; otherwise,
+ * <tt>false</tt>
+ */
+ private boolean isConferenceMemberCallPeer(
+ ConferenceMember conferenceMember,
+ CallPeer callPeer)
+ {
+ return
+ (conferenceMember == null)
+ ? true
+ : CallManager.addressesAreEqual(
+ conferenceMember.getAddress(),
+ callPeer.getAddress());
+ }
+
+ /**
+ * Determines whether a specific <tt>ConferenceMember</tt> represents the
+ * local peer/user. Since this instance depicts a whole telephony
+ * conference, the local peer/user may be participating with multiple
+ * <tt>Call</tt>s in it. The <tt>Call</tt>s may be through different
+ * (local) accounts. That's why the implementation determines whether the
+ * address of the specified <tt>conferenceMember</tt> identifies the address
+ * of a (local) accounts involved in the telephony conference depicted by
+ * this instance.
+ *
+ * @param conferenceMember the <tt>ConferenceMember</tt> to be checked
+ * whether it represents the local peer/user
+ * @return <tt>true</tt> if the specified <tt>conferenceMember</tt>
+ * represents the local peer/user; otherwise, <tt>false</tt>
+ */
+ private boolean isConferenceMemberLocalUser(
+ ConferenceMember conferenceMember)
+ {
+ String address = conferenceMember.getAddress();
+
+ for (Call call : callConference.getCalls())
+ {
+ if (CallManager.addressesAreEqual(
+ address,
+ call.getProtocolProvider().getAccountID()
+ .getAccountAddress()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void removeConferenceMemberContainers(
+ ConferenceParticipantContainer cpc,
+ boolean all)
+ {
+ List<ConferenceParticipantContainer> cmcs
+ = cpc.conferenceMemberContainers;
+
+ if ((cmcs != null) && !cmcs.isEmpty())
+ {
+ Iterator<ConferenceParticipantContainer> i = cmcs.iterator();
+
+ while (i.hasNext())
+ {
+ ConferenceParticipantContainer cmc = i.next();
+
+ if (all || cmc.toBeRemoved)
+ {
+ i.remove();
+
+ videoContainer.remove(cmc.getComponent());
+ cmc.dispose();
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates the <tt>ConferenceParticipantContainer</tt>s which depict the
+ * <tt>ConferenceMember</tt>s of the <tt>CallPeer</tt> depicted by a
+ * specific <tt>ConferenceParticipantContainer</tt>.
+ *
+ * @param cpc the <tt>ConferenceParticipantContainer</tt> which depicts the
+ * <tt>CallPeer</tt> whose <tt>ConferenceMember</tt>s are to be depicted
+ * @param videos the visual <tt>Component</tt>s displaying video streaming
+ * from the remote peer (represented by <tt>cpc</tt>) to the local peer/user
+ * @param videoTelephony the <tt>OperationSetVideoTelephony</tt> which
+ * retrieved the specified <tt>videos</tt> from the <tt>CallPeer</tt>
+ * depicted by <tt>cpc</tt>. While the <tt>CallPeer</tt> could be queried
+ * for it, such a query would waste more resources at run time given that
+ * the invoker has it already.
+ */
+ private void updateConferenceMemberContainers(
+ ConferenceParticipantContainer cpc,
+ List<Component> videos,
+ OperationSetVideoTelephony videoTelephony)
+ {
+ CallPeer callPeer = (CallPeer) cpc.getParticipant();
+ List<ConferenceParticipantContainer> cmcs
+ = cpc.conferenceMemberContainers;
+
+ /*
+ * Invalidate all conferenceMemberContainers. Then validate which of
+ * them are to remain and which of them are to really be removed
+ * later on.
+ */
+ if (cmcs != null)
+ {
+ for (ConferenceParticipantContainer cmc : cmcs)
+ cmc.toBeRemoved = true;
+ }
+
+ /*
+ * Depict the remote videos. They may or may not be associated with
+ * ConferenceMembers so the ConferenceMembers which have no
+ * associated videos will be depicted afterwards.
+ */
+ if (videos != null)
+ {
+ Component video = cpc.getVideo();
+
+ for (Component conferenceMemberVideo : videos)
+ {
+ /*
+ * One of the remote videos is already used to depict the
+ * callPeer.
+ */
+ if (conferenceMemberVideo == video)
+ continue;
+
+ boolean addNewConferenceParticipantContainer = true;
+ ConferenceMember conferenceMember
+ = videoTelephony.getConferenceMember(
+ callPeer,
+ conferenceMemberVideo);
+
+ if (cmcs == null)
+ {
+ cmcs = new LinkedList<ConferenceParticipantContainer>();
+ cpc.conferenceMemberContainers = cmcs;
+ }
+ else
+ {
+ for (ConferenceParticipantContainer cmc : cmcs)
+ {
+ Object cmcParticipant = cmc.getParticipant();
+
+ if (conferenceMember == null)
+ {
+ if (cmcParticipant == callPeer)
+ {
+ Component cmcVideo = cmc.getVideo();
+
+ if (cmcVideo == null)
+ {
+ cmc.setVideo(conferenceMemberVideo);
+ cmc.toBeRemoved = false;
+ addNewConferenceParticipantContainer
+ = false;
+ break;
+ }
+ else if (cmcVideo == conferenceMemberVideo)
+ {
+ cmc.toBeRemoved = false;
+ addNewConferenceParticipantContainer
+ = false;
+ break;
+ }
+ }
+ }
+ else if (cmcParticipant == conferenceMember)
+ {
+ cmc.setVideo(conferenceMemberVideo);
+ cmc.toBeRemoved = false;
+ addNewConferenceParticipantContainer = false;
+ break;
+ }
+ }
+ }
+
+ if (addNewConferenceParticipantContainer)
+ {
+ ConferenceParticipantContainer cmc
+ = (conferenceMember == null)
+ ? new ConferenceParticipantContainer(
+ callPeer,
+ conferenceMemberVideo)
+ : new ConferenceParticipantContainer(
+ conferenceMember,
+ conferenceMemberVideo);
+
+ cmcs.add(cmc);
+ }
+ }
+ }
+
+ /*
+ * Depict the ConferenceMembers which have not been depicted yet.
+ * They have no associated videos.
+ */
+ List<ConferenceMember> conferenceMembers
+ = callPeer.getConferenceMembers();
+
+ if (!conferenceMembers.isEmpty())
+ {
+ if (cmcs == null)
+ {
+ cmcs = new LinkedList<ConferenceParticipantContainer>();
+ cpc.conferenceMemberContainers = cmcs;
+ }
+ for (ConferenceMember conferenceMember : conferenceMembers)
+ {
+ /*
+ * If the callPeer reports itself as a ConferenceMember, then
+ * we've already depicted it with cpc.
+ */
+ if (isConferenceMemberCallPeer(conferenceMember, callPeer))
+ continue;
+ /*
+ * If the callPeer reports the local peer/user as a
+ * ConferenceMember, then we've already depicted it.
+ */
+ if (isConferenceMemberLocalUser(conferenceMember))
+ continue;
+
+ boolean addNewConferenceParticipantContainer = true;
+
+ for (ConferenceParticipantContainer cmc : cmcs)
+ {
+ if (cmc.getParticipant() == conferenceMember)
+ {
+ /*
+ * It is possible to have a ConferenceMember who is
+ * sending video but we just do not have the SSRC of
+ * that video to associate the video with the
+ * ConferenceMember. In such a case, we may be depicting
+ * the ConferenceMember twice: once with video without a
+ * ConferenceMember and once with a ConferenceMember
+ * without video. This will surely be the case at the
+ * time of this writing with non-focus participants in a
+ * telephony conference hosted on a Jitsi Videobridge.
+ * Such a display is undesirable. If the
+ * conferenceMember is known to send video, we will not
+ * display it until we associated it with a video. This
+ * way, if a ConferenceMember is not sending video, we
+ * will depict it and we can be sure that no video
+ * without a ConferenceMember association will be
+ * depicting it a second time.
+ */
+ if (cmc.toBeRemoved
+ && !conferenceMember
+ .getVideoStatus()
+ .allowsSending())
+ {
+ cmc.setVideo(null);
+ cmc.toBeRemoved = false;
+ }
+ addNewConferenceParticipantContainer = false;
+ break;
+ }
+ }
+
+ if (addNewConferenceParticipantContainer)
+ {
+ ConferenceParticipantContainer cmc
+ = new ConferenceParticipantContainer(
+ conferenceMember,
+ null);
+
+ cmcs.add(cmc);
+ }
+ }
+ }
+
+ if ((cmcs != null) && !cmcs.isEmpty())
+ {
+ removeConferenceMemberContainers(cpc, false);
+ /*
+ * If cpc is already added to the user interface hierarchy of this
+ * instance, then it was there before the update procedure and it
+ * was determined to be appropriate to continue to depict its model.
+ * Consequently, its Component will be neither added to (because it
+ * was already added) nor removed from the user interface hierarchy
+ * of this instance. That's why we have make sure that the
+ * Components of its conferenceMemberContainers are also added to
+ * the user interface.
+ */
+ if (UIVideoHandler2.isAncestor(this, cpc.getComponent()))
+ addConferenceMemberContainers(cpc);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected ConferenceCallPeerRenderer updateViewFromModel(
+ ConferenceCallPeerRenderer callPeerPanel,
+ CallPeer callPeer)
+ {
+ if (callPeer == null)
+ {
+ /*
+ * The local peer/user will be represented by a Call which has a
+ * CallPeer who provides local video. However, if the user has
+ * selected to hide the local video, the local peer/user will not be
+ * represented at all.
+ */
+ Component video = null;
+
+ if (uiVideoHandler.isLocalVideoVisible())
+ {
+ for (Call aCall : callConference.getCalls())
+ {
+ Iterator<? extends CallPeer> callPeerIter
+ = aCall.getCallPeers();
+ OperationSetVideoTelephony videoTelephony
+ = aCall.getProtocolProvider().getOperationSet(
+ OperationSetVideoTelephony.class);
+
+ while (callPeerIter.hasNext())
+ {
+ callPeer = callPeerIter.next();
+
+ if (videoTelephony != null)
+ {
+ try
+ {
+ video
+ = videoTelephony.getLocalVisualComponent(
+ callPeer);
+ }
+ catch (OperationFailedException ofe)
+ {
+ logger.error(
+ "Failed to retrieve the local video"
+ + " for display",
+ ofe);
+ }
+ if (video != null)
+ break;
+ }
+ }
+ if (video != null)
+ break;
+ }
+ }
+
+ if (callPeer == null)
+ callPeerPanel = null;
+ else
+ {
+ Call call = callPeer.getCall();
+
+ if (callPeerPanel instanceof ConferenceParticipantContainer)
+ {
+ ConferenceParticipantContainer cpc
+ = (ConferenceParticipantContainer) callPeerPanel;
+
+ if (cpc.getParticipant() == call)
+ cpc.setVideo(video);
+ else
+ callPeerPanel = null;
+ }
+ else
+ callPeerPanel = null;
+ if (callPeerPanel == null)
+ {
+ callPeerPanel
+ = new ConferenceParticipantContainer(call, video);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * The specified callPeer will be represented by one of its remote
+ * videos which is not associated with a ConferenceMember or is
+ * associated with a ConferenceMember representing the callPeer
+ * itself.
+ */
+ OperationSetVideoTelephony videoTelephony
+ = callPeer.getProtocolProvider().getOperationSet(
+ OperationSetVideoTelephony.class);
+ List<Component> videos = null;
+ Component video = null;
+
+ if (videoTelephony != null)
+ {
+ videos = videoTelephony.getVisualComponents(callPeer);
+ if ((videos != null) && !videos.isEmpty())
+ {
+ for (Component aVideo : videos)
+ {
+ ConferenceMember conferenceMember
+ = videoTelephony.getConferenceMember(
+ callPeer,
+ aVideo);
+
+ if (isConferenceMemberCallPeer(
+ conferenceMember,
+ callPeer))
+ {
+ video = aVideo;
+ break;
+ }
+ }
+ }
+ }
+
+ ConferenceParticipantContainer cpc = null;
+
+ if (callPeerPanel instanceof ConferenceParticipantContainer)
+ {
+ cpc = (ConferenceParticipantContainer) callPeerPanel;
+ if (cpc.getParticipant() == callPeer)
+ cpc.setVideo(video);
+ else
+ cpc = null;
+ }
+ if (cpc == null)
+ cpc = new ConferenceParticipantContainer(callPeer, video);
+ callPeerPanel = cpc;
+
+ // Update the conferenceMemberContainers of the cpc.
+ updateConferenceMemberContainers(cpc, videos, videoTelephony);
+ }
+ return callPeerPanel;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If {@link #SHOW_TOOLBARS} is <tt>false</tt>, disables the use of
+ * <tt>ConferenceParticipantContainer</tt>. A reason for such a value of
+ * <tt>SHOW_TOOLBARS</tt> may be that the functionality implemented in the
+ * model may not fully support mapping of visual <tt>Component</tt>s
+ * displaying video to telephony conference participants (e.g. in telephony
+ * conferences utilizing the Jitsi Videobridge server-side technology). In
+ * such a case displays the videos only, does not map videos to participants
+ * and does not display participants who do not have videos.
+ */
+ @Override
+ protected void updateViewFromModelInEventDispatchThread()
+ {
+ if (SHOW_TOOLBARS)
+ {
+ super.updateViewFromModelInEventDispatchThread();
+ return;
+ }
+
+ /*
+ * Determine the set of visual Components displaying video streaming
+ * between the local peer/user and the remote peers which are to be
+ * depicted by this instance.
+ */
+ Component localVideo = null;
+ Set<Component> videos = new HashSet<Component>();
+
+ for (Call call : callConference.getCalls())
+ {
+ OperationSetVideoTelephony videoTelephony
+ = call.getProtocolProvider().getOperationSet(
+ OperationSetVideoTelephony.class);
+
+ if (videoTelephony == null)
+ continue;
+
+ Iterator<? extends CallPeer> callPeerIter = call.getCallPeers();
+
+ while (callPeerIter.hasNext())
+ {
+ CallPeer callPeer = callPeerIter.next();
+
+ /*
+ * TODO VideoConferenceCallPanel respects
+ * UIVideoHandler2.isLocalVideoVisible() in order to react to
+ * the associated button at the bottom of the CallPanel.
+ * However, it does not add a close button on top of the local
+ * video in contrast to OneToOneCallPeerPanel. Overall, the
+ * result is questionable.
+ */
+ if (uiVideoHandler.isLocalVideoVisible()
+ && (localVideo == null))
+ {
+ try
+ {
+ localVideo
+ = videoTelephony.getLocalVisualComponent(callPeer);
+ }
+ catch (OperationFailedException ofe)
+ {
+ /*
+ * We'll just try to get the local video through another
+ * CallPeer then.
+ */
+ }
+ if (localVideo != null)
+ videos.add(localVideo);
+ }
+
+ List<Component> callPeerRemoteVideos
+ = videoTelephony.getVisualComponents(callPeer);
+
+ videos.addAll(callPeerRemoteVideos);
+ }
+ }
+
+ /*
+ * Remove the Components of this view which are no longer present in the
+ * model.
+ */
+ Iterator<Component> thisVideoIter = this.videos.iterator();
+
+ while (thisVideoIter.hasNext())
+ {
+ Component thisVideo = thisVideoIter.next();
+
+ if (!videos.contains(thisVideo))
+ {
+ thisVideoIter.remove();
+ videoContainer.remove(thisVideo);
+ }
+
+ /*
+ * If a video is known to be depicted by this view and is still
+ * present in the model, then we could remove it from the set of
+ * videos present in the model in order to prevent going through the
+ * procedure of adding it to this view. However, we choose to play
+ * on the safe side.
+ */
+ }
+
+ /*
+ * Add the Components of the model which are not depicted by this view.
+ */
+ for (Component video : videos)
+ {
+ if (!UIVideoHandler2.isAncestor(videoContainer, video))
+ {
+ this.videos.add(video);
+ videoContainer.add(
+ video,
+ (video == localVideo)
+ ? VideoLayout.LOCAL
+ : VideoLayout.CENTER_REMOTE);
+ }
+ }
+ }
+
+ @Override
+ protected void viewForModelAdded(
+ ConferenceCallPeerRenderer callPeerPanel,
+ CallPeer callPeer)
+ {
+ videoContainer.add(
+ callPeerPanel.getComponent(),
+ VideoLayout.CENTER_REMOTE);
+ if ((callPeer != null)
+ && (callPeerPanel instanceof ConferenceParticipantContainer))
+ {
+ addConferenceMemberContainers(
+ (ConferenceParticipantContainer) callPeerPanel);
+ }
+ }
+
+ @Override
+ protected void viewForModelRemoved(
+ ConferenceCallPeerRenderer callPeerPanel,
+ CallPeer callPeer)
+ {
+ videoContainer.remove(callPeerPanel.getComponent());
+ if ((callPeer != null)
+ && (callPeerPanel instanceof ConferenceParticipantContainer))
+ {
+ removeConferenceMemberContainers(
+ (ConferenceParticipantContainer) callPeerPanel,
+ true);
+ }
+ }
+
+ /**
+ * Implements an AWT <tt>Component</tt> which contains the user interface
+ * elements depicting a specific participant in the telephony conference
+ * depicted by a <tt>VideoConferenceCallPanel</tt>.
+ */
+ private class ConferenceParticipantContainer
+ extends TransparentPanel
+ implements ConferenceCallPeerRenderer
+ {
+ /**
+ * The list of <tt>ConferenceParticipantContainer</tt>s which represent
+ * the <tt>ConferenceMember</tt>s of the participant represented by this
+ * instance. Since a <tt>CallPeer</tt> may send the local peer/user
+ * multiple videos without providing a way to associate a
+ * ConferenceMember with each one of them, the list may contain
+ * <tt>ConferenceParticipantContainer</tt>s which do not represent a
+ * specific <tt>ConferenceMember</tt> instance but rather a video sent
+ * by a <tt>CallPeer</tt> to the local peer/user which looks like (in
+ * the terms of <tt>VideoConferenceCallPanel) a member of a conference
+ * organized by the <tt>CallPeer</tt> in question.
+ * <p>
+ * Implements a state which is private to
+ * <tt>VideoConferenceCallPanel</tt> and is of no concern to
+ * <tt>ConferenceParticipantContainer</tt>.
+ * </p>
+ */
+ List<ConferenceParticipantContainer> conferenceMemberContainers;
+
+ /**
+ * The indicator which determines whether this instance is to be removed
+ * because it has become out-of-date, obsolete, unnecessary.
+ * <p>
+ * Implements a state which is private to
+ * <tt>VideoConferenceCallPanel</tt> and is of no concern to
+ * <tt>ConferenceParticipantContainer</tt>.
+ * </p>
+ */
+ boolean toBeRemoved;
+
+ /**
+ * The <tt>BasicConferenceParticipantPanel</tt> which is displayed at
+ * the bottom of this instance, bellow the {@link #video} (i.e.
+ * {@link #videoContainer}) and is referred to as the tool bar.
+ */
+ private final BasicConferenceParticipantPanel<?> toolBar;
+
+ /**
+ * The visual <tt>Component</tt>, if any, displaying video which is
+ * depicted by this instance.
+ */
+ private Component video;
+
+ /**
+ * The <tt>VideoContainer</tt> which lays out the video depicted by this
+ * instance i.e. {@link #video}.
+ */
+ private final VideoContainer videoContainer;
+
+ /**
+ * The <tt>CallPeer</tt> associated with this container, if it has been
+ * created to represent a <tt>CallPeer</tt>.
+ */
+ private CallPeer callPeer;
+
+ /**
+ * The <tt>conferenceMember</tt> associated with this container, if it
+ * has been created to represent a <tt>conferenceMember</tt>.
+ */
+ private ConferenceMember conferenceMember;
+
+ /**
+ * Indicates that this container contains information for the local
+ * user.
+ */
+ private boolean isLocalUser;
+
+ /**
+ * Initializes a new <tt>ConferenceParticipantContainer</tt> instance
+ * which is to depict the local peer/user.
+ *
+ * @param call a <tt>Call</tt> which is to provide information about the
+ * local peer/user
+ * @param video the visual <tt>Component</tt>, if any, displaying the
+ * video streaming from the local peer/user to the remote peer(s)
+ */
+ public ConferenceParticipantContainer(Call call, Component video)
+ {
+ this(
+ createDefaultPhotoPanel(call),
+ video,
+ new ConferencePeerPanel(
+ VideoConferenceCallPanel.this,
+ call,
+ true),
+ null, null, true);
+ }
+
+ public ConferenceParticipantContainer(
+ CallPeer callPeer,
+ Component video)
+ {
+ this( createDefaultPhotoPanel(callPeer),
+ video,
+ new ConferencePeerPanel(
+ VideoConferenceCallPanel.this,
+ callPeer,
+ true),
+ callPeer, null, false);
+ }
+
+ private ConferenceParticipantContainer(
+ Component noVideo,
+ Component video,
+ BasicConferenceParticipantPanel<?> toolBar,
+ CallPeer callPeer,
+ ConferenceMember conferenceMember,
+ boolean isLocalUser)
+ {
+ super(new BorderLayout());
+
+ this.callPeer = callPeer;
+ this.conferenceMember = conferenceMember;
+ this.isLocalUser = isLocalUser;
+
+ videoContainer = new VideoContainer(noVideo, false);
+ add(videoContainer, BorderLayout.CENTER);
+
+ this.toolBar = toolBar;
+ if (this.toolBar != null)
+ add(this.toolBar, BorderLayout.SOUTH);
+
+ if (video != null)
+ {
+ setVideo(video);
+ }
+ else
+ setVisible(false);
+ }
+
+ public ConferenceParticipantContainer(
+ ConferenceMember conferenceMember,
+ Component video)
+ {
+ this(
+ createDefaultPhotoPanel(conferenceMember),
+ video,
+ new ConferenceMemberPanel(
+ VideoConferenceCallPanel.this,
+ conferenceMember,
+ true),
+ null, conferenceMember, false);
+ }
+
+ public void dispose()
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.dispose();
+
+ // Dispose of the conferenceMemberContainers if any.
+ /*
+ * XXX The field conferenceMemberContainers implements a state
+ * private to VideoConferenceCallPanel which the latter makes sure
+ * to access on the AWT event dispatching thread only. Since we are
+ * going out of our way here to help VideoConferenceCallPanel,
+ * ensure that the mentioned synchronization rule is not violated.
+ */
+ CallManager.assertIsEventDispatchingThread();
+ if (conferenceMemberContainers != null)
+ {
+ for (ConferenceParticipantContainer cmc
+ : conferenceMemberContainers)
+ {
+ cmc.dispose();
+ }
+ }
+ }
+
+ public CallPanel getCallPanel()
+ {
+ return getCallRenderer().getCallContainer();
+ }
+
+ public SwingCallRenderer getCallRenderer()
+ {
+ return VideoConferenceCallPanel.this;
+ }
+
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ private ConferenceCallPeerRenderer
+ getConferenceCallPeerRendererDelegate()
+ {
+ return
+ (toolBar instanceof ConferenceCallPeerRenderer)
+ ? (ConferenceCallPeerRenderer) toolBar
+ : null;
+ }
+
+ /**
+ * Gets the conference participant depicted by this instance.
+ *
+ * @return the conference participant depicted by this instance
+ */
+ public Object getParticipant()
+ {
+ return (toolBar == null) ? null : toolBar.getParticipant();
+ }
+
+ public Component getVideo()
+ {
+ return video;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only. Otherwise, returns <tt>false</tt>.
+ */
+ public boolean isLocalVideoVisible()
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ return (delegate == null) ? false : delegate.isLocalVideoVisible();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void printDTMFTone(char dtmfChar)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.printDTMFTone(dtmfChar);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void securityNegotiationStarted(
+ CallPeerSecurityNegotiationStartedEvent ev)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.securityNegotiationStarted(ev);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void securityOff(CallPeerSecurityOffEvent ev)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.securityOff(ev);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void securityOn(CallPeerSecurityOnEvent ev)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.securityOn(ev);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void securityPending()
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.securityPending();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void securityTimeout(CallPeerSecurityTimeoutEvent ev)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.securityTimeout(ev);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void setErrorReason(String reason)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.setErrorReason(reason);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void setLocalVideoVisible(boolean visible)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.setLocalVideoVisible(visible);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void setMute(boolean mute)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.setMute(mute);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void setOnHold(boolean onHold)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.setOnHold(onHold);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void setPeerImage(byte[] image)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.setPeerImage(image);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void setPeerName(String name)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.setPeerName(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void setPeerState(
+ CallPeerState oldState,
+ CallPeerState newState,
+ String stateString)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.setPeerState(oldState, newState, stateString);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Delegates to the <tt>toolBar</tt>, if the latter implements
+ * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
+ * container only.
+ */
+ public void setSecurityPanelVisible(boolean visible)
+ {
+ ConferenceCallPeerRenderer delegate
+ = getConferenceCallPeerRendererDelegate();
+
+ if (delegate != null)
+ delegate.setSecurityPanelVisible(visible);
+ }
+
+ /**
+ * Sets the visual <tt>Component</tt> displaying the video associated
+ * with the participant depicted by this instance.
+ *
+ * @param video the visual <tt>Component</tt> displaying video which is
+ * to be associated with the participant depicted by this instance
+ */
+ void setVideo(Component video)
+ {
+ if (this.video != video)
+ {
+ if (this.video != null)
+ videoContainer.remove(this.video);
+
+ this.video = video;
+
+ if (this.video != null)
+ {
+ setVisible(true);
+ videoContainer.add(this.video, VideoLayout.CENTER_REMOTE);
+ }
+ else
+ setVisible(false);
+
+ // Update thumbnails container according to video status.
+ if (thumbnailContainer != null)
+ {
+ if (conferenceMember != null)
+ thumbnailContainer
+ .updateThumbnail(conferenceMember, (video != null));
+ else if (callPeer != null)
+ thumbnailContainer
+ .updateThumbnail(callPeer, (video != null));
+ else if (isLocalUser)
+ thumbnailContainer
+ .updateThumbnail((video != null));
+ }
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java b/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java
index 127e28b..0283e25 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/ChatConversationPanel.java
@@ -790,8 +790,6 @@ public class ChatConversationPanel
*/
public void correctMessage(final ChatMessage chatMessage)
{
- lastMessageUID = chatMessage.getMessageUID();
-
if (!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
@@ -805,6 +803,11 @@ public class ChatConversationPanel
}
String correctedUID = chatMessage.getCorrectedMessageUID();
+ if (correctedUID != null && correctedUID.equals(lastMessageUID))
+ {
+ lastMessageUID = chatMessage.getMessageUID();
+ }
+
Element root = document.getDefaultRootElement();
Element correctedMsgElement
= document.getElement(root,
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ChatSessionChangeListener.java b/src/net/java/sip/communicator/impl/gui/main/chat/ChatSessionChangeListener.java
index 0b96069..1e6127e 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chat/ChatSessionChangeListener.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/ChatSessionChangeListener.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,32 +15,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.chat;
-
-/**
- * Listens for changes in {@link ChatSession}.
- * @author George Politis
- */
-public interface ChatSessionChangeListener
-{
- /**
- * The icon representing the ChatTransport has changed.
- */
- public static final int ICON_UPDATED = 1;
-
- /**
- * Called when the current {@link ChatTransport} has
- * changed.
- *
- * @param chatSession the {@link ChatSession} it's current
- * {@link ChatTransport} has changed
- */
- public void currentChatTransportChanged(ChatSession chatSession);
-
- /**
- * When a property of the chatTransport has changed.
- * @param eventID the event id representing the property of the transport
- * that has changed.
- */
- public void currentChatTransportUpdated(int eventID);
-}
+package net.java.sip.communicator.impl.gui.main.chat;
+
+/**
+ * Listens for changes in {@link ChatSession}.
+ * @author George Politis
+ */
+public interface ChatSessionChangeListener
+{
+ /**
+ * The icon representing the ChatTransport has changed.
+ */
+ public static final int ICON_UPDATED = 1;
+
+ /**
+ * Called when the current {@link ChatTransport} has
+ * changed.
+ *
+ * @param chatSession the {@link ChatSession} it's current
+ * {@link ChatTransport} has changed
+ */
+ public void currentChatTransportChanged(ChatSession chatSession);
+
+ /**
+ * When a property of the chatTransport has changed.
+ * @param eventID the event id representing the property of the transport
+ * that has changed.
+ */
+ public void currentChatTransportUpdated(int eventID);
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/conference/ChatContactListModel.java b/src/net/java/sip/communicator/impl/gui/main/chat/conference/ChatContactListModel.java
index d42ecfb..acf36bf 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chat/conference/ChatContactListModel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/conference/ChatContactListModel.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,235 +15,235 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.chat.conference;
-
-import java.util.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.main.chat.*;
-import net.java.sip.communicator.service.muc.*;
-import net.java.sip.communicator.service.protocol.event.*;
-
-/**
- * Implements an <tt>AbstractListModel</tt> which represents a member list of
- * <tt>ChatContact</tt>s. The primary purpose of the implementation is to sort
- * the <tt>ChatContact</tt>s according to their member roles and in alphabetical
- * order according to their names.
- *
- * @author Lyubomir Marinov
- */
-public class ChatContactListModel
- extends AbstractListModel
- implements ChatRoomMemberPropertyChangeListener
-{
-
- /**
- * The backing store of this <tt>AbstractListModel</tt> listing the
- * <tt>ChatContact</tt>s.
- */
- private final List<ChatContact<?>> chatContacts
- = new ArrayList<ChatContact<?>>();
-
- /**
- * Current chat session.
- */
- private ChatSession chatSession;
-
- /**
- * The implementation of the sorting rules - the <tt>ChatContact</tt>s are
- * first sorted according to their roles in decreasing order of their
- * privileges and then they are sorted according to their names in
- * alphabetical order.
- */
- private final Comparator<ChatContact<?>> sorter
- = new Comparator<ChatContact<?>>()
- {
- public int compare(ChatContact<?> chatContact0, ChatContact<?> chatContact1)
- {
- /*
- * Place ChatMembers with more privileges at the beginning of
- * the list.
- */
- if (chatContact0 instanceof ConferenceChatContact)
- {
- if (chatContact1 instanceof ConferenceChatContact)
- {
- int role0
- = ((ConferenceChatContact) chatContact0).getRole()
- .getRoleIndex();
- int role1
- = ((ConferenceChatContact) chatContact1).getRole()
- .getRoleIndex();
-
- if (role0 > role1)
- return -1;
- else if (role0 < role1)
- return 1;
- }
- else
- return -1;
- }
- else if (chatContact1 instanceof ConferenceChatContact)
- return 1;
-
- /* By default, sort the ChatContacts in alphabetical order. */
- return
- chatContact0.getName().compareToIgnoreCase(
- chatContact1.getName());
- }
- };
-
- /**
- * Creates the model.
- * @param chatSession The current model chat session.
- */
- public ChatContactListModel(ChatSession chatSession)
- {
- this.chatSession = chatSession;
-
- // when something like rename on a member change update the UI to
- // reflect it
- Object descriptor = chatSession.getDescriptor();
-
- if(descriptor instanceof ChatRoomWrapper)
- {
- ((ChatRoomWrapper) descriptor)
- .getChatRoom().addMemberPropertyChangeListener(this);
- }
- }
-
- /**
- * Listens for property change in chat room members.
- * @param ev the event
- */
- public void chatRoomPropertyChanged(ChatRoomMemberPropertyChangeEvent ev)
- {
- // Translate into
- // ListDataListener.contentsChanged.
- int chatContactCount = chatContacts.size();
-
- for (int i = 0; i < chatContactCount; i++)
- {
- ChatContact<?> chatContact = chatContacts.get(i);
-
- if(chatContact.getDescriptor().equals(ev.getSourceChatRoomMember()))
- {
- fireContentsChanged(chatContact, i, i);
- /*
- * TODO Can ev.sourceChatRoomMember
- * equal more than one chatContacts
- * element? If it cannot, it will be
- * more efficient to break here.
- */
- }
- }
- }
-
- /**
- * Adds a specific <tt>ChatContact</tt> to this <tt>AbstractListModel</tt>
- * implementation and preserves the sorting it applies.
- *
- * @param chatContact a <tt>ChatContact</tt> to be added to this
- * <tt>AbstractListModel</tt>
- */
- public void addElement(ChatContact<?> chatContact)
- {
- if (chatContact == null)
- throw new IllegalArgumentException("chatContact");
-
- int index = -1;
-
- synchronized(chatContacts)
- {
- int chatContactCount = chatContacts.size();
-
- for (int i = 0; i < chatContactCount; i++)
- {
- ChatContact<?> containedChatContact = chatContacts.get(i);
-
- // We don't want duplicates.
- if (chatContact.equals(containedChatContact))
- return;
- if ((index == -1)
- && (sorter.compare(containedChatContact, chatContact) > 0))
- {
- index = i;
- // Continue in order to prevent duplicates.
- }
- }
- if (index == -1)
- index = chatContactCount;
-
- chatContacts.add(index, chatContact);
- }
- fireIntervalAdded(this, index, index);
- }
-
- /* Implements ListModel#getElementAt(int). */
- public ChatContact<?> getElementAt(int index)
- {
- synchronized(chatContacts)
- {
- return chatContacts.get(index);
- }
- }
-
- /* Implements ListModel#getSize(). */
- public int getSize()
- {
- synchronized(chatContacts)
- {
- return chatContacts.size();
- }
- }
-
- /**
- * Removes a specific <tt>ChatContact</tt> from this
- * <tt>AbstractListModel</tt> implementation.
- *
- * @param chatContact a <tt>ChatContact</tt> to be removed from this
- * <tt>AbstractListModel</tt> if it's already contained
- */
- public void removeElement(ChatContact<?> chatContact)
- {
- synchronized(chatContacts)
- {
- int index = chatContacts.indexOf(chatContact);
-
- if ((index >= 0) && chatContacts.remove(chatContact))
- fireIntervalRemoved(this, index, index);
- }
- }
-
- /**
- * Removes all the elements from this model.
- */
- public void removeAllElements()
- {
- if (chatContacts == null || chatContacts.size() <= 0)
- return;
-
- synchronized(chatContacts)
- {
- int contactsSize = chatContacts.size();
- chatContacts.clear();
-
- fireIntervalRemoved(this, 0, contactsSize - 1);
- }
- }
-
- /**
- * Runs clean-up.
- */
- public void dispose()
- {
- Object descriptor = chatSession.getDescriptor();
-
- if(descriptor instanceof ChatRoomWrapper)
- {
- ((ChatRoomWrapper) descriptor)
- .getChatRoom().removeMemberPropertyChangeListener(this);
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.chat.conference;
+
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.main.chat.*;
+import net.java.sip.communicator.service.muc.*;
+import net.java.sip.communicator.service.protocol.event.*;
+
+/**
+ * Implements an <tt>AbstractListModel</tt> which represents a member list of
+ * <tt>ChatContact</tt>s. The primary purpose of the implementation is to sort
+ * the <tt>ChatContact</tt>s according to their member roles and in alphabetical
+ * order according to their names.
+ *
+ * @author Lyubomir Marinov
+ */
+public class ChatContactListModel
+ extends AbstractListModel
+ implements ChatRoomMemberPropertyChangeListener
+{
+
+ /**
+ * The backing store of this <tt>AbstractListModel</tt> listing the
+ * <tt>ChatContact</tt>s.
+ */
+ private final List<ChatContact<?>> chatContacts
+ = new ArrayList<ChatContact<?>>();
+
+ /**
+ * Current chat session.
+ */
+ private ChatSession chatSession;
+
+ /**
+ * The implementation of the sorting rules - the <tt>ChatContact</tt>s are
+ * first sorted according to their roles in decreasing order of their
+ * privileges and then they are sorted according to their names in
+ * alphabetical order.
+ */
+ private final Comparator<ChatContact<?>> sorter
+ = new Comparator<ChatContact<?>>()
+ {
+ public int compare(ChatContact<?> chatContact0, ChatContact<?> chatContact1)
+ {
+ /*
+ * Place ChatMembers with more privileges at the beginning of
+ * the list.
+ */
+ if (chatContact0 instanceof ConferenceChatContact)
+ {
+ if (chatContact1 instanceof ConferenceChatContact)
+ {
+ int role0
+ = ((ConferenceChatContact) chatContact0).getRole()
+ .getRoleIndex();
+ int role1
+ = ((ConferenceChatContact) chatContact1).getRole()
+ .getRoleIndex();
+
+ if (role0 > role1)
+ return -1;
+ else if (role0 < role1)
+ return 1;
+ }
+ else
+ return -1;
+ }
+ else if (chatContact1 instanceof ConferenceChatContact)
+ return 1;
+
+ /* By default, sort the ChatContacts in alphabetical order. */
+ return
+ chatContact0.getName().compareToIgnoreCase(
+ chatContact1.getName());
+ }
+ };
+
+ /**
+ * Creates the model.
+ * @param chatSession The current model chat session.
+ */
+ public ChatContactListModel(ChatSession chatSession)
+ {
+ this.chatSession = chatSession;
+
+ // when something like rename on a member change update the UI to
+ // reflect it
+ Object descriptor = chatSession.getDescriptor();
+
+ if(descriptor instanceof ChatRoomWrapper)
+ {
+ ((ChatRoomWrapper) descriptor)
+ .getChatRoom().addMemberPropertyChangeListener(this);
+ }
+ }
+
+ /**
+ * Listens for property change in chat room members.
+ * @param ev the event
+ */
+ public void chatRoomPropertyChanged(ChatRoomMemberPropertyChangeEvent ev)
+ {
+ // Translate into
+ // ListDataListener.contentsChanged.
+ int chatContactCount = chatContacts.size();
+
+ for (int i = 0; i < chatContactCount; i++)
+ {
+ ChatContact<?> chatContact = chatContacts.get(i);
+
+ if(chatContact.getDescriptor().equals(ev.getSourceChatRoomMember()))
+ {
+ fireContentsChanged(chatContact, i, i);
+ /*
+ * TODO Can ev.sourceChatRoomMember
+ * equal more than one chatContacts
+ * element? If it cannot, it will be
+ * more efficient to break here.
+ */
+ }
+ }
+ }
+
+ /**
+ * Adds a specific <tt>ChatContact</tt> to this <tt>AbstractListModel</tt>
+ * implementation and preserves the sorting it applies.
+ *
+ * @param chatContact a <tt>ChatContact</tt> to be added to this
+ * <tt>AbstractListModel</tt>
+ */
+ public void addElement(ChatContact<?> chatContact)
+ {
+ if (chatContact == null)
+ throw new IllegalArgumentException("chatContact");
+
+ int index = -1;
+
+ synchronized(chatContacts)
+ {
+ int chatContactCount = chatContacts.size();
+
+ for (int i = 0; i < chatContactCount; i++)
+ {
+ ChatContact<?> containedChatContact = chatContacts.get(i);
+
+ // We don't want duplicates.
+ if (chatContact.equals(containedChatContact))
+ return;
+ if ((index == -1)
+ && (sorter.compare(containedChatContact, chatContact) > 0))
+ {
+ index = i;
+ // Continue in order to prevent duplicates.
+ }
+ }
+ if (index == -1)
+ index = chatContactCount;
+
+ chatContacts.add(index, chatContact);
+ }
+ fireIntervalAdded(this, index, index);
+ }
+
+ /* Implements ListModel#getElementAt(int). */
+ public ChatContact<?> getElementAt(int index)
+ {
+ synchronized(chatContacts)
+ {
+ return chatContacts.get(index);
+ }
+ }
+
+ /* Implements ListModel#getSize(). */
+ public int getSize()
+ {
+ synchronized(chatContacts)
+ {
+ return chatContacts.size();
+ }
+ }
+
+ /**
+ * Removes a specific <tt>ChatContact</tt> from this
+ * <tt>AbstractListModel</tt> implementation.
+ *
+ * @param chatContact a <tt>ChatContact</tt> to be removed from this
+ * <tt>AbstractListModel</tt> if it's already contained
+ */
+ public void removeElement(ChatContact<?> chatContact)
+ {
+ synchronized(chatContacts)
+ {
+ int index = chatContacts.indexOf(chatContact);
+
+ if ((index >= 0) && chatContacts.remove(chatContact))
+ fireIntervalRemoved(this, index, index);
+ }
+ }
+
+ /**
+ * Removes all the elements from this model.
+ */
+ public void removeAllElements()
+ {
+ if (chatContacts == null || chatContacts.size() <= 0)
+ return;
+
+ synchronized(chatContacts)
+ {
+ int contactsSize = chatContacts.size();
+ chatContacts.clear();
+
+ fireIntervalRemoved(this, 0, contactsSize - 1);
+ }
+ }
+
+ /**
+ * Runs clean-up.
+ */
+ public void dispose()
+ {
+ Object descriptor = chatSession.getDescriptor();
+
+ if(descriptor instanceof ChatRoomWrapper)
+ {
+ ((ChatRoomWrapper) descriptor)
+ .getChatRoom().removeMemberPropertyChangeListener(this);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatManager.java b/src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatManager.java
index 58a1db4..89ceb48 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatManager.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatManager.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,1390 +15,1390 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.chat.conference;
-
-import java.util.*;
-import java.util.concurrent.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.chat.*;
-import net.java.sip.communicator.impl.gui.main.chat.history.*;
-import net.java.sip.communicator.impl.gui.main.chatroomslist.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.muc.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.service.protocol.globalstatus.*;
-import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.util.Logger;
-
-import org.jdesktop.swingworker.SwingWorker;
-import org.jitsi.util.*;
-import org.osgi.framework.*;
-
-/**
- * The <tt>ConferenceChatManager</tt> is the one that manages both chat room and
- * ad-hoc chat rooms invitations.
- *
- * @author Yana Stamcheva
- * @author Lubomir Marinov
- * @author Valentin Martinet
- * @author Hristo Terezov
- */
-public class ConferenceChatManager
- implements ChatRoomMessageListener,
- ChatRoomInvitationListener,
- ChatRoomInvitationRejectionListener,
- AdHocChatRoomMessageListener,
- AdHocChatRoomInvitationListener,
- AdHocChatRoomInvitationRejectionListener,
- LocalUserChatRoomPresenceListener,
- LocalUserAdHocChatRoomPresenceListener,
- ServiceListener, ChatRoomLocalUserRoleListener
-{
- /**
- * The object used for logging.
- */
- private static final Logger logger
- = Logger.getLogger(ConferenceChatManager.class);
-
- /**
- * Maps each history window to a <tt>ChatRoomWrapper</tt>.
- */
- private final Hashtable<ChatRoomWrapper, HistoryWindow> chatRoomHistory =
- new Hashtable<ChatRoomWrapper, HistoryWindow>();
-
- /**
- * The list of ad-hoc chat rooms.
- */
- private final AdHocChatRoomList adHocChatRoomList = new AdHocChatRoomList();
-
- /**
- * A list of all <tt>AdHocChatRoomListChangeListener</tt>-s.
- */
- private final Vector<AdHocChatRoomListChangeListener>
- adHoclistChangeListeners = new Vector<AdHocChatRoomListChangeListener>();
-
- /**
- * Creates an instance of <tt>ConferenceChatManager</tt>.
- */
- public ConferenceChatManager()
- {
- // Loads the chat rooms list in a separate thread.
- new Thread()
- {
- @Override
- public void run()
- {
- adHocChatRoomList.loadList();
- }
- }.start();
-
- GuiActivator.bundleContext.addServiceListener(this);
-
- }
-
- /**
- * Returns all chat room providers currently contained in the ad-hoc chat
- * room list.
- *
- * @return all chat room providers currently contained in the ad-hoc chat
- * room list.
- */
- public AdHocChatRoomList getAdHocChatRoomList()
- {
- return adHocChatRoomList;
- }
-
- /**
- * Handles <tt>ChatRoomInvitationReceivedEvent</tt>-s.
- */
- public void invitationReceived(ChatRoomInvitationReceivedEvent evt)
- {
- InvitationReceivedDialog dialog
- = new InvitationReceivedDialog(
- this,
- evt.getSourceOperationSet(),
- evt.getInvitation());
-
- dialog.setVisible(true);
- }
-
- public void invitationRejected(ChatRoomInvitationRejectedEvent evt) {}
-
- /**
- * Implements the <tt>ChatRoomMessageListener.messageDelivered</tt> method.
- * <br>
- * Shows the message in the conversation area and clears the write message
- * area.
- * @param evt the <tt>ChatRoomMessageDeliveredEvent</tt> that notified us
- * that the message was delivered to its destination
- */
- public void messageDelivered(ChatRoomMessageDeliveredEvent evt)
- {
- ChatRoom sourceChatRoom = (ChatRoom) evt.getSource();
-
- if (logger.isTraceEnabled())
- logger.trace(
- "MESSAGE DELIVERED to chat room: " + sourceChatRoom.getName());
-
- ChatPanel chatPanel = GuiActivator.getUIService().getChatWindowManager()
- .getMultiChat(sourceChatRoom, false);
-
- if(chatPanel != null)
- {
- String messageType;
-
- switch (evt.getEventType())
- {
- case ChatRoomMessageDeliveredEvent.CONVERSATION_MESSAGE_DELIVERED:
- messageType = Chat.OUTGOING_MESSAGE;
- break;
- case ChatRoomMessageDeliveredEvent.ACTION_MESSAGE_DELIVERED:
- messageType = Chat.ACTION_MESSAGE;
- break;
- default:
- messageType = null;
- break;
- }
-
- Message msg = evt.getMessage();
-
- chatPanel.addMessage(
- sourceChatRoom.getUserNickname(),
- null,
- evt.getTimestamp(),
- messageType,
- msg.getContent(),
- msg.getContentType(),
- msg.getMessageUID(),
- null);
- }
- }
-
- /**
- * Implements the <tt>ChatRoomMessageListener.messageReceived</tt> method.
- * <br>
- * Obtains the corresponding <tt>ChatPanel</tt> and process the message
- * there.
- * @param evt the <tt>ChatRoomMessageReceivedEvent</tt> that notified us
- * that a message has been received
- */
- public void messageReceived(ChatRoomMessageReceivedEvent evt)
- {
- ChatRoom sourceChatRoom = evt.getSourceChatRoom();
- ChatRoomMember sourceMember = evt.getSourceChatRoomMember();
-
- String messageType = null;
-
- switch (evt.getEventType())
- {
- case ChatRoomMessageReceivedEvent.CONVERSATION_MESSAGE_RECEIVED:
- messageType = Chat.INCOMING_MESSAGE;
- break;
- case ChatRoomMessageReceivedEvent.SYSTEM_MESSAGE_RECEIVED:
- messageType = Chat.SYSTEM_MESSAGE;
- break;
- case ChatRoomMessageReceivedEvent.ACTION_MESSAGE_RECEIVED:
- messageType = Chat.ACTION_MESSAGE;
- break;
- }
-
- if (logger.isTraceEnabled())
- logger.trace("MESSAGE RECEIVED from contact: "
- + sourceMember.getContactAddress());
-
- Message message = evt.getMessage();
-
- ChatPanel chatPanel = null;
-
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
-
- boolean createWindow = false;
- String autoOpenConfig
- = MUCService.getChatRoomAutoOpenOption(
- sourceChatRoom.getParentProvider(),
- sourceChatRoom.getIdentifier());
- if(autoOpenConfig == null)
- autoOpenConfig = MUCService.DEFAULT_AUTO_OPEN_BEHAVIOUR;
-
- if(autoOpenConfig.equals(MUCService.OPEN_ON_ACTIVITY)
- || (autoOpenConfig.equals(MUCService.OPEN_ON_MESSAGE)
- && !evt.isHistoryMessage())
- || evt.isImportantMessage())
- createWindow = true;
-
- if(sourceChatRoom.isSystem())
- {
- ChatRoomProviderWrapper serverWrapper
- = GuiActivator.getMUCService().findServerWrapperFromProvider(
- sourceChatRoom.getParentProvider());
-
- chatPanel = chatWindowManager.getMultiChat(
- serverWrapper.getSystemRoomWrapper(), createWindow);
- }
- else
- {
- chatPanel = chatWindowManager.getMultiChat(
- sourceChatRoom, createWindow, message.getMessageUID());
- }
-
- if(chatPanel == null)
- return;
-
- String messageContent = message.getContent();
-
- if (evt.isHistoryMessage())
- {
- Date timeStamp = chatPanel.getChatConversationPanel()
- .getLastIncomingMsgTimestamp();
- Collection<Object> c =
- chatPanel.getChatSession().getHistoryBeforeDate(
- new Date(
- timeStamp.equals(new Date(0))
- ? System.currentTimeMillis() - 10000
- : timeStamp.getTime()
- ), 20);
- if (c.size() > 0)
- {
- boolean isPresent = false;
- for (Object o : c)
- {
- if (o instanceof ChatRoomMessageDeliveredEvent)
- {
- ChatRoomMessageDeliveredEvent ev =
- (ChatRoomMessageDeliveredEvent) o;
- if (evt.getTimestamp() != null
- && evt.getTimestamp().equals(ev.getTimestamp()))
- {
- isPresent = true;
- break;
- }
- }
- else if(o instanceof ChatRoomMessageReceivedEvent)
- {
- ChatRoomMessageReceivedEvent ev =
- (ChatRoomMessageReceivedEvent) o;
- if (evt.getTimestamp() != null
- && evt.getTimestamp().equals(ev.getTimestamp()))
- {
- isPresent = true;
- break;
- }
- }
-
- Message m2 = evt.getMessage();
-
- if(m2 != null
- && m2.getContent().equals(messageContent))
- {
- isPresent = true;
- break;
- }
- }
-
- if (isPresent)
- return;
- }
- }
-
- chatPanel.addMessage(
- sourceMember.getName(),
- null,
- evt.getTimestamp(),
- messageType,
- messageContent,
- message.getContentType(),
- message.getMessageUID(),
- null);
-
- if(createWindow)
- chatWindowManager.openChat(chatPanel, false);
- }
-
- /**
- * Implements the <tt>ChatRoomMessageListener.messageDeliveryFailed</tt>
- * method.
- * <br>
- * In the conversation area shows an error message, explaining the problem.
- * @param evt the <tt>ChatRoomMessageDeliveryFailedEvent</tt> that notified
- * us of a delivery failure
- */
- public void messageDeliveryFailed(ChatRoomMessageDeliveryFailedEvent evt)
- {
- ChatRoom sourceChatRoom = evt.getSourceChatRoom();
-
- String errorMsg = null;
-
- /*
- * FIXME ChatRoomMessageDeliveryFailedEvent#getSource() is not a Message
- * instance at the time of this writing and the attempt "(Message)
- * evt.getSource()" seems to be to get the message which failed to be
- * delivered. I'm not sure it's
- * ChatRoomMessageDeliveryFailedEvent#getMessage() but since it's the
- * only message I can get out of ChatRoomMessageDeliveryFailedEvent, I'm
- * using it.
- */
- Message sourceMessage = evt.getMessage();
-
- ChatRoomMember destMember = evt.getDestinationChatRoomMember();
-
- if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED)
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_DELIVERY_NOT_SUPPORTED",
- new String[]{destMember.getName()});
- }
- else if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.NETWORK_FAILURE)
- {
- errorMsg = GuiActivator.getResources()
- .getI18NString("service.gui.MSG_NOT_DELIVERED");
- }
- else if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.PROVIDER_NOT_REGISTERED)
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_SEND_CONNECTION_PROBLEM");
- }
- else if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.INTERNAL_ERROR)
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_DELIVERY_INTERNAL_ERROR");
- }
- else if (evt.getErrorCode()
- == ChatRoomMessageDeliveryFailedEvent.FORBIDDEN)
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_SEND_MSG_FORBIDDEN");
- }
- else if (evt.getErrorCode()
- == ChatRoomMessageDeliveryFailedEvent.UNSUPPORTED_OPERATION)
- {
- errorMsg =
- GuiActivator.getResources().getI18NString(
- "service.gui.MSG_DELIVERY_UNSUPPORTED_OPERATION");
- }
- else
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_DELIVERY_UNKNOWN_ERROR");
- }
-
- String reason = evt.getReason();
- if (reason != null)
- errorMsg += " " + GuiActivator.getResources().getI18NString(
- "service.gui.ERROR_WAS",
- new String[]{reason});
-
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
- ChatPanel chatPanel
- = chatWindowManager.getMultiChat(sourceChatRoom, true);
-
- chatPanel.addMessage(
- destMember != null ? destMember.getName()
- : sourceChatRoom.getName(),
- new Date(),
- Chat.OUTGOING_MESSAGE,
- sourceMessage.getContent(),
- sourceMessage.getContentType());
-
- chatPanel.addErrorMessage(
- destMember != null ? destMember.getName()
- : sourceChatRoom.getName(),
- errorMsg);
-
- chatWindowManager.openChat(chatPanel, false);
- }
-
- /**
- * Implements the
- * <tt>LocalUserAdHocChatRoomPresenceListener.localUserPresenceChanged</tt>
- * method
- *
- * @param evt the <tt>LocalUserAdHocChatRoomPresenceChangeEvent</tt> that
- * notified us of a presence change
- */
- public void localUserAdHocPresenceChanged(
- LocalUserAdHocChatRoomPresenceChangeEvent evt)
- {
- AdHocChatRoom sourceAdHocChatRoom = evt.getAdHocChatRoom();
- AdHocChatRoomWrapper adHocChatRoomWrapper
- = adHocChatRoomList
- .findChatRoomWrapperFromAdHocChatRoom(sourceAdHocChatRoom);
-
- String eventType = evt.getEventType();
-
- if (LocalUserAdHocChatRoomPresenceChangeEvent
- .LOCAL_USER_JOINED.equals(eventType))
- {
- if(adHocChatRoomWrapper != null)
- {
- this.fireAdHocChatRoomListChangedEvent(
- adHocChatRoomWrapper,
- AdHocChatRoomListChangeEvent.AD_HOC_CHAT_ROOM_CHANGED);
-
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
- ChatPanel chatPanel
- = chatWindowManager
- .getMultiChat(adHocChatRoomWrapper, true);
-
- // Check if we have already opened a chat window for this chat
- // wrapper and load the real chat room corresponding to the
- // wrapper.
- if(chatPanel.isShown())
- ((AdHocConferenceChatSession) chatPanel.getChatSession())
- .loadChatRoom(sourceAdHocChatRoom);
- else
- chatWindowManager.openChat(chatPanel, true);
- }
-
- sourceAdHocChatRoom.addMessageListener(this);
- }
- else if (evt.getEventType().equals(
- LocalUserAdHocChatRoomPresenceChangeEvent.LOCAL_USER_JOIN_FAILED))
- {
- GuiActivator.getAlertUIService().showAlertPopup(
- GuiActivator.getResources().getI18NString("service.gui.ERROR"),
- GuiActivator.getResources().getI18NString(
- "service.gui.FAILED_TO_JOIN_CHAT_ROOM",
- new String[]{sourceAdHocChatRoom.getName()})
- + evt.getReason());
- }
- else if (LocalUserAdHocChatRoomPresenceChangeEvent
- .LOCAL_USER_LEFT.equals(eventType)
- || LocalUserAdHocChatRoomPresenceChangeEvent
- .LOCAL_USER_DROPPED.equals(eventType))
- {
- this.closeAdHocChatRoom(adHocChatRoomWrapper);
-
- // Need to refresh the chat room's list in order to change
- // the state of the chat room to offline.
- fireAdHocChatRoomListChangedEvent(
- adHocChatRoomWrapper,
- AdHocChatRoomListChangeEvent.AD_HOC_CHAT_ROOM_CHANGED);
-
- sourceAdHocChatRoom.removeMessageListener(this);
- }
- }
-
- /**
- * Implements the
- * <tt>LocalUserChatRoomPresenceListener.localUserPresenceChanged</tt>
- * method.
- * @param evt the <tt>LocalUserChatRoomPresenceChangeEvent</tt> that
- * notified us
- */
- public void localUserPresenceChanged(
- final LocalUserChatRoomPresenceChangeEvent evt)
- {
- if(!SwingUtilities.isEventDispatchThread())
- {
- SwingUtilities.invokeLater(new Runnable()
- {
- @Override
- public void run()
- {
- localUserPresenceChanged(evt);
- }
- });
- return;
- }
-
- ChatRoom sourceChatRoom = evt.getChatRoom();
- ChatRoomWrapper chatRoomWrapper
- = GuiActivator.getMUCService().findChatRoomWrapperFromChatRoom(
- sourceChatRoom);
-
- String eventType = evt.getEventType();
-
- if (LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_JOINED.equals(eventType))
- {
- if(chatRoomWrapper != null)
- {
- GuiActivator.getMUCService().fireChatRoomListChangedEvent(
- chatRoomWrapper,
- ChatRoomListChangeEvent.CHAT_ROOM_CHANGED);
-
- boolean createWindow = false;
-
- String autoOpenConfig
- = MUCService.getChatRoomAutoOpenOption(
- sourceChatRoom.getParentProvider(),
- sourceChatRoom.getIdentifier());
-
- if(autoOpenConfig != null
- && autoOpenConfig.equals(MUCService.OPEN_ON_ACTIVITY))
- createWindow = true;
-
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
- ChatPanel chatPanel
- = chatWindowManager.getMultiChat(
- chatRoomWrapper, createWindow);
-
- if(chatPanel != null)
- {
- chatPanel.setChatIcon(
- chatPanel.getChatSession().getChatStatusIcon());
-
- // Check if we have already opened a chat window for this chat
- // wrapper and load the real chat room corresponding to the
- // wrapper.
- if(chatPanel.isShown())
- {
- ((ConferenceChatSession) chatPanel.getChatSession())
- .loadChatRoom(sourceChatRoom);
- }
- else
- {
- chatWindowManager.openChat(chatPanel, true);
- }
- }
- }
-
- if (sourceChatRoom.isSystem())
- {
- ChatRoomProviderWrapper serverWrapper
- = GuiActivator.getMUCService()
- .findServerWrapperFromProvider(
- sourceChatRoom.getParentProvider());
-
- serverWrapper.setSystemRoom(sourceChatRoom);
- }
-
- sourceChatRoom.addMessageListener(this);
- sourceChatRoom.addLocalUserRoleListener(this);
- }
- else if (LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_JOIN_FAILED.equals(eventType))
- {
- GuiActivator.getAlertUIService().showAlertPopup(
- GuiActivator.getResources().getI18NString("service.gui.ERROR"),
- GuiActivator.getResources().getI18NString(
- "service.gui.FAILED_TO_JOIN_CHAT_ROOM",
- new String[]{sourceChatRoom.getName()})
- + evt.getReason());
- }
- else if (LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_LEFT.equals(eventType)
- || LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_KICKED.equals(eventType)
- || LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_DROPPED.equals(eventType))
- {
- if(chatRoomWrapper != null)
- {
- if(StringUtils.isNullOrEmpty(evt.getReason()))
- {
- GuiActivator.getUIService()
- .closeChatRoomWindow(chatRoomWrapper);
- }
- else
- {
- // send some system messages informing for the
- // reason of leaving
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
-
- ChatPanel chatPanel = chatWindowManager.getMultiChat(
- sourceChatRoom, false);
-
- if(chatPanel != null)
- {
- chatPanel.addMessage(
- sourceChatRoom.getName(),
- null,
- new Date(),
- Chat.SYSTEM_MESSAGE,
- evt.getReason(),
- OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE,
- null,
- null);
-
- // print and the alternate address
- if(!StringUtils.isNullOrEmpty(
- evt.getAlternateAddress()))
- {
- chatPanel.addMessage(
- sourceChatRoom.getName(),
- null,
- new Date(),
- Chat.SYSTEM_MESSAGE,
- GuiActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_ALTERNATE_ADDRESS",
- new String[]{evt.getAlternateAddress()}),
- OperationSetBasicInstantMessaging
- .DEFAULT_MIME_TYPE,
- null,
- null);
- }
- }
- }
-
- // Need to refresh the chat room's list in order to change
- // the state of the chat room to offline.
-
- GuiActivator.getMUCService().fireChatRoomListChangedEvent(
- chatRoomWrapper,
- ChatRoomListChangeEvent.CHAT_ROOM_CHANGED);
- }
-
- sourceChatRoom.removeMessageListener(this);
- sourceChatRoom.removelocalUserRoleListener(this);
- }
- }
-
-
- /**
- * Called to accept an incoming invitation. Adds the invitation chat room
- * to the list of chat rooms and joins it.
- *
- * @param invitation the invitation to accept
- * @param multiUserChatOpSet the operation set for chat conferencing
- * @throws OperationFailedException if the accept fails
- */
- public void acceptInvitation(
- AdHocChatRoomInvitation invitation,
- OperationSetAdHocMultiUserChat multiUserChatOpSet)
- throws OperationFailedException
- {
- AdHocChatRoom chatRoom = invitation.getTargetAdHocChatRoom();
-
- chatRoom.join();
- }
-
- /**
- * Rejects the given invitation with the specified reason.
- *
- * @param multiUserChatAdHocOpSet the operation set to use for rejecting the
- * invitation
- * @param invitation the invitation to reject
- * @param reason the reason for the rejection
- */
- public void rejectInvitation(
- OperationSetAdHocMultiUserChat multiUserChatAdHocOpSet,
- AdHocChatRoomInvitation invitation,
- String reason)
- {
- multiUserChatAdHocOpSet.rejectInvitation(invitation, reason);
- }
-
- /**
- * Creates an ad-hoc chat room, by specifying the ad-hoc chat room name, the
- * parent protocol provider and eventually, the contacts invited to
- * participate in this ad-hoc chat room.
- *
- * @param protocolProvider the parent protocol provider.
- * @param contacts the contacts invited when creating the chat room.
- * @param reason the reason for this invitation
- * @return the <tt>AdHocChatRoomWrapper</tt> corresponding to the created
- * ad hoc chat room
- */
- public AdHocChatRoomWrapper createAdHocChatRoom(
- ProtocolProviderService protocolProvider,
- Collection<String> contacts,
- String reason)
- {
- AdHocChatRoomWrapper chatRoomWrapper = null;
-
- OperationSetAdHocMultiUserChat groupChatOpSet
- = protocolProvider
- .getOperationSet(OperationSetAdHocMultiUserChat.class);
-
- // If there's no group chat operation set we have nothing to do here.
- if (groupChatOpSet == null)
- return null;
-
- AdHocChatRoom chatRoom = null;
-
- try
- {
- java.util.List<String> members = new LinkedList<String>();
-
- for(String address : contacts)
- members.add(address);
-
- chatRoom = groupChatOpSet.createAdHocChatRoom(
- "chatroom-" + new Date().getTime(), members, reason);
- }
- catch (OperationFailedException ex)
- {
- new ErrorDialog(
- GuiActivator.getUIService().getMainFrame(),
- GuiActivator.getResources().getI18NString("service.gui.ERROR"),
- GuiActivator.getResources().getI18NString(
- "service.gui.CREATE_CHAT_ROOM_ERROR",
- new String[]{protocolProvider.getProtocolDisplayName()}),
- ex)
- .showDialog();
- }
- catch (OperationNotSupportedException ex)
- {
- new ErrorDialog(
- GuiActivator.getUIService().getMainFrame(),
- GuiActivator.getResources().getI18NString("service.gui.ERROR"),
- GuiActivator.getResources().getI18NString(
- "service.gui.CREATE_CHAT_ROOM_ERROR",
- new String[]{protocolProvider.getProtocolDisplayName()}),
- ex)
- .showDialog();
- }
-
- if(chatRoom != null)
- {
- AdHocChatRoomProviderWrapper parentProvider
- = adHocChatRoomList.findServerWrapperFromProvider(
- protocolProvider);
-
- chatRoomWrapper = new AdHocChatRoomWrapper(
- parentProvider, chatRoom);
- parentProvider.addAdHocChatRoom(chatRoomWrapper);
- adHocChatRoomList.addAdHocChatRoom(chatRoomWrapper);
-
- fireAdHocChatRoomListChangedEvent(
- chatRoomWrapper,
- AdHocChatRoomListChangeEvent.AD_HOC_CHAT_ROOM_ADDED);
- }
-
- return chatRoomWrapper;
- }
-
- /**
- * Joins the given ad-hoc chat room
- *
- * @param chatRoomWrapper
- */
- public void joinChatRoom(AdHocChatRoomWrapper chatRoomWrapper)
- {
- AdHocChatRoom chatRoom = chatRoomWrapper.getAdHocChatRoom();
-
- if(chatRoom == null)
- {
- new ErrorDialog(
- GuiActivator.getUIService().getMainFrame(),
- GuiActivator.getResources().getI18NString("service.gui.WARNING"),
- GuiActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_NOT_CONNECTED",
- new String[]{chatRoomWrapper.getAdHocChatRoomName()}))
- .showDialog();
-
- return;
- }
-
- new JoinAdHocChatRoomTask(chatRoomWrapper).execute();
- }
-
- /**
- * Removes the given chat room from the UI.
- *
- * @param chatRoomWrapper the chat room to remove.
- */
- public void removeChatRoom(ChatRoomWrapper chatRoomWrapper)
- {
- ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
-
- if (chatRoom != null)
- leaveChatRoom(chatRoomWrapper);
-
- GuiActivator.getUIService().closeChatRoomWindow(chatRoomWrapper);
-
- GuiActivator.getMUCService().removeChatRoom(chatRoomWrapper);
-
- }
-
- /**
- * Joins the given chat room and manages all the exceptions that could
- * occur during the join process.
- *
- * @param chatRoom the chat room to join
- */
- public void joinChatRoom(AdHocChatRoom chatRoom)
- {
- AdHocChatRoomWrapper chatRoomWrapper
- = adHocChatRoomList.findChatRoomWrapperFromAdHocChatRoom(chatRoom);
-
- if(chatRoomWrapper == null)
- {
- AdHocChatRoomProviderWrapper parentProvider
- = adHocChatRoomList.findServerWrapperFromProvider(
- chatRoom.getParentProvider());
-
- chatRoomWrapper =
- new AdHocChatRoomWrapper(parentProvider, chatRoom);
-
- adHocChatRoomList.addAdHocChatRoom(chatRoomWrapper);
-
- fireAdHocChatRoomListChangedEvent(
- chatRoomWrapper,
- AdHocChatRoomListChangeEvent.AD_HOC_CHAT_ROOM_ADDED);
- }
-
- this.joinChatRoom(chatRoomWrapper);
-
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
-
- chatWindowManager
- .openChat(
- chatWindowManager.getMultiChat(chatRoomWrapper, true),
- true);
- }
-
- /**
- * Leaves the given <tt>ChatRoom</tt>.
- *
- * @param chatRoomWrapper the <tt>ChatRoom</tt> to leave.
- */
- public void leaveChatRoom(ChatRoomWrapper chatRoomWrapper)
- {
- ChatRoomWrapper leavedRoomWrapped
- = GuiActivator.getMUCService().leaveChatRoom(chatRoomWrapper);
- if(leavedRoomWrapped != null)
- GuiActivator.getUIService().closeChatRoomWindow(leavedRoomWrapped);
- }
-
- /**
- * Leaves the given <tt>ChatRoom</tt>.
- *
- * @param chatRoomWrapper the <tt>ChatRoom</tt> to leave.
- */
- public void leaveChatRoom(AdHocChatRoomWrapper chatRoomWrapper)
- {
- AdHocChatRoom chatRoom = chatRoomWrapper.getAdHocChatRoom();
-
- if (chatRoom != null)
- {
- chatRoom.leave();
- }
- else
- {
- new ErrorDialog(
- GuiActivator.getUIService().getMainFrame(),
- GuiActivator.getResources().getI18NString("service.gui.WARNING"),
- GuiActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_LEAVE_NOT_CONNECTED"))
- .showDialog();
- }
- }
-
- /**
- * Checks if there's an open history window for the given chat room.
- *
- * @param chatRoomWrapper the chat room wrapper to check for
- * @return TRUE if there's an opened history window for the given chat room,
- * FALSE otherwise.
- */
- public boolean containsHistoryWindowForChatRoom(
- ChatRoomWrapper chatRoomWrapper)
- {
- return chatRoomHistory.containsKey(chatRoomWrapper);
- }
-
- /**
- * Returns the history window for the given chat room.
- *
- * @param chatRoomWrapper the chat room wrapper to search for
- * @return the history window for the given chat room
- */
- public HistoryWindow getHistoryWindowForChatRoom(
- ChatRoomWrapper chatRoomWrapper)
- {
- return chatRoomHistory.get(chatRoomWrapper);
- }
-
- /**
- * Adds a history window for a given chat room in the table of opened
- * history windows.
- *
- * @param chatRoomWrapper the chat room wrapper to add
- * @param historyWindow the history window to add
- */
- public void addHistoryWindowForChatRoom(ChatRoomWrapper chatRoomWrapper,
- HistoryWindow historyWindow)
- {
- chatRoomHistory.put(chatRoomWrapper, historyWindow);
- }
-
- /**
- * Removes the history window for the given chat room.
- *
- * @param chatRoomWrapper the chat room wrapper to remove the history window
- */
- public void removeHistoryWindowForChatRoom(ChatRoomWrapper chatRoomWrapper)
- {
- chatRoomHistory.remove(chatRoomWrapper);
- }
-
- /**
- * Adds the given <tt>AdHocChatRoomListChangeListener</tt> that will listen
- * for all changes of the chat room list data model.
- *
- * @param l the listener to add.
- */
- public void addAdHocChatRoomListChangeListener(
- AdHocChatRoomListChangeListener l)
- {
- synchronized (adHoclistChangeListeners)
- {
- adHoclistChangeListeners.add(l);
- }
- }
-
- /**
- * Removes the given <tt>AdHocChatRoomListChangeListener</tt>.
- *
- * @param l the listener to remove.
- */
- public void removeAdHocChatRoomListChangeListener(
- AdHocChatRoomListChangeListener l)
- {
- synchronized (adHoclistChangeListeners)
- {
- adHoclistChangeListeners.remove(l);
- }
- }
-
- /**
- * Notifies all interested listeners that a change in the chat room list
- * model has occurred.
- * @param adHocChatRoomWrapper the chat room wrapper that identifies the
- * chat room
- * @param eventID the identifier of the event
- */
- private void fireAdHocChatRoomListChangedEvent(
- AdHocChatRoomWrapper adHocChatRoomWrapper,
- int eventID)
- {
- AdHocChatRoomListChangeEvent evt
- = new AdHocChatRoomListChangeEvent(adHocChatRoomWrapper, eventID);
-
- for (AdHocChatRoomListChangeListener l : adHoclistChangeListeners)
- {
- l.contentChanged(evt);
- }
- }
-
-
- /**
- * Closes the chat corresponding to the given ad-hoc chat room wrapper, if
- * such exists.
- *
- * @param chatRoomWrapper the ad-hoc chat room wrapper for which we search a
- * chat to close.
- */
- private void closeAdHocChatRoom(AdHocChatRoomWrapper chatRoomWrapper)
- {
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
- ChatPanel chatPanel
- = chatWindowManager.getMultiChat(chatRoomWrapper, false);
-
- if (chatPanel != null)
- chatWindowManager.closeChat(chatPanel);
- }
-
- /**
- * Handles <tt>ServiceEvent</tt>s triggered by adding or removing a
- * ProtocolProviderService. Updates the list of available chat rooms and
- * chat room servers.
- *
- * @param event The event to handle.
- */
- public void serviceChanged(ServiceEvent event)
- {
- // if the event is caused by a bundle being stopped, we don't want to
- // know
- if (event.getServiceReference().getBundle().getState()
- == Bundle.STOPPING)
- return;
-
- Object service = GuiActivator.bundleContext.getService(event
- .getServiceReference());
-
- // we don't care if the source service is not a protocol provider
- if (!(service instanceof ProtocolProviderService))
- return;
-
- ProtocolProviderService protocolProvider
- = (ProtocolProviderService) service;
-
-
- Object multiUserChatAdHocOpSet
- = protocolProvider
- .getOperationSet(OperationSetAdHocMultiUserChat.class);
-
- if (multiUserChatAdHocOpSet != null)
- {
- if (event.getType() == ServiceEvent.REGISTERED)
- {
- adHocChatRoomList.addChatProvider(protocolProvider);
- }
- else if (event.getType() == ServiceEvent.UNREGISTERING)
- {
- adHocChatRoomList.removeChatProvider(protocolProvider);
- }
- }
- }
-
- /**
- * Joins an ad-hoc chat room in an asynchronous way.
- */
- private static class JoinAdHocChatRoomTask
- extends SwingWorker<String, Object>
- {
- private static final String SUCCESS = "Success";
-
- private static final String AUTHENTICATION_FAILED
- = "AuthenticationFailed";
-
- private static final String REGISTRATION_REQUIRED
- = "RegistrationRequired";
-
- private static final String PROVIDER_NOT_REGISTERED
- = "ProviderNotRegistered";
-
- private static final String SUBSCRIPTION_ALREADY_EXISTS
- = "SubscriptionAlreadyExists";
-
- private static final String UNKNOWN_ERROR
- = "UnknownError";
-
- private final AdHocChatRoomWrapper adHocChatRoomWrapper;
-
- JoinAdHocChatRoomTask(AdHocChatRoomWrapper chatRoomWrapper)
- {
- this.adHocChatRoomWrapper = chatRoomWrapper;
- }
-
- /**
- * @override {@link SwingWorker}{@link #doInBackground()} to perform
- * all asynchronous tasks.
- * @return SUCCESS if success, otherwise the error code
- */
- @Override
- public String doInBackground()
- {
- AdHocChatRoom chatRoom = adHocChatRoomWrapper.getAdHocChatRoom();
-
- try
- {
- chatRoom.join();
-
- return SUCCESS;
- }
- catch (OperationFailedException e)
- {
- if (logger.isTraceEnabled())
- logger.trace("Failed to join ad-hoc chat room: "
- + chatRoom.getName(), e);
-
- switch (e.getErrorCode())
- {
- case OperationFailedException.AUTHENTICATION_FAILED:
- return AUTHENTICATION_FAILED;
- case OperationFailedException.REGISTRATION_REQUIRED:
- return REGISTRATION_REQUIRED;
- case OperationFailedException.PROVIDER_NOT_REGISTERED:
- return PROVIDER_NOT_REGISTERED;
- case OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS:
- return SUBSCRIPTION_ALREADY_EXISTS;
- default:
- return UNKNOWN_ERROR;
- }
- }
- }
-
- /**
- * @override {@link SwingWorker}{@link #done()} to perform UI changes
- * after the ad-hoc chat room join task has finished.
- */
- @Override
- protected void done()
- {
- String returnCode = null;
- try
- {
- returnCode = get();
- }
- catch (InterruptedException ignore)
- {}
- catch (ExecutionException ignore)
- {}
-
- ConfigurationUtils.updateChatRoomStatus(
- adHocChatRoomWrapper.getParentProvider().getProtocolProvider(),
- adHocChatRoomWrapper.getAdHocChatRoomID(),
- GlobalStatusEnum.ONLINE_STATUS);
-
- String errorMessage = null;
- if(PROVIDER_NOT_REGISTERED.equals(returnCode))
- {
- errorMessage
- = GuiActivator.getResources()
- .getI18NString("service.gui.CHAT_ROOM_NOT_CONNECTED",
- new String[]{
- adHocChatRoomWrapper.getAdHocChatRoomName()});
- }
- else if(SUBSCRIPTION_ALREADY_EXISTS.equals(returnCode))
- {
- errorMessage
- = GuiActivator.getResources()
- .getI18NString("service.gui.CHAT_ROOM_ALREADY_JOINED",
- new String[]{
- adHocChatRoomWrapper.getAdHocChatRoomName()});
- }
- else
- {
- errorMessage
- = GuiActivator.getResources()
- .getI18NString("service.gui.FAILED_TO_JOIN_CHAT_ROOM",
- new String[]{
- adHocChatRoomWrapper.getAdHocChatRoomName()});
- }
-
- if (!SUCCESS.equals(returnCode)
- && !AUTHENTICATION_FAILED.equals(returnCode))
- {
- GuiActivator.getAlertUIService().showAlertPopup(
- GuiActivator.getResources().getI18NString(
- "service.gui.ERROR"), errorMessage);
- }
- }
- }
-
-
- /**
- * Indicates that an invitation has been received and opens the invitation
- * dialog to notify the user.
- * @param evt the <tt>AdHocChatRoomInvitationReceivedEvent</tt> that
- * notified us
- */
- public void invitationReceived(AdHocChatRoomInvitationReceivedEvent evt)
- {
- if (logger.isInfoEnabled())
- logger.info("Invitation received: "+evt.toString());
- OperationSetAdHocMultiUserChat multiUserChatOpSet
- = evt.getSourceOperationSet();
-
- InvitationReceivedDialog dialog = new InvitationReceivedDialog(
- this, multiUserChatOpSet, evt.getInvitation());
-
- dialog.setVisible(true);
- }
-
- /**
- * Implements the <tt>AdHocChatRoomMessageListener.messageDelivered</tt>
- * method.
- * <br>
- * Shows the message in the conversation area and clears the write message
- * area.
- * @param evt the <tt>AdHocChatRoomMessageDeliveredEvent</tt> that notified
- * us
- */
- public void messageDelivered(AdHocChatRoomMessageDeliveredEvent evt)
- {
- AdHocChatRoom sourceChatRoom = (AdHocChatRoom) evt.getSource();
-
- if (logger.isInfoEnabled())
- logger.info("MESSAGE DELIVERED to ad-hoc chat room: "
- + sourceChatRoom.getName());
-
- ChatPanel chatPanel
- = GuiActivator
- .getUIService()
- .getChatWindowManager()
- .getMultiChat(sourceChatRoom, false);
-
- if(chatPanel != null)
- {
- String messageType;
- switch (evt.getEventType())
- {
- case AdHocChatRoomMessageDeliveredEvent
- .CONVERSATION_MESSAGE_DELIVERED:
- messageType = Chat.OUTGOING_MESSAGE;
- break;
- case AdHocChatRoomMessageDeliveredEvent.ACTION_MESSAGE_DELIVERED:
- messageType = Chat.ACTION_MESSAGE;
- break;
- default:
- messageType = null;
- }
-
- Message msg = evt.getMessage();
-
- chatPanel
- .addMessage(
- sourceChatRoom
- .getParentProvider().getAccountID().getUserID(),
- null,
- evt.getTimestamp(),
- messageType,
- msg.getContent(),
- msg.getContentType(),
- msg.getMessageUID(),
- null);
- }
- else
- {
- logger.error("chat panel is null, message NOT DELIVERED !");
- }
- }
-
- /**
- * Implements <tt>AdHocChatRoomMessageListener.messageDeliveryFailed</tt>
- * method.
- * <br>
- * In the conversation area shows an error message, explaining the problem.
- * @param evt the <tt>AdHocChatRoomMessageDeliveryFailedEvent</tt> that
- * notified us
- */
- public void messageDeliveryFailed(
- AdHocChatRoomMessageDeliveryFailedEvent evt)
- {
- AdHocChatRoom sourceChatRoom = evt.getSourceChatRoom();
- Message sourceMessage = evt.getMessage();
- Contact destParticipant = evt.getDestinationParticipant();
-
- String errorMsg = null;
- if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED)
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_DELIVERY_NOT_SUPPORTED",
- new String[]{destParticipant.getDisplayName()});
- }
- else if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.NETWORK_FAILURE)
- {
- errorMsg = GuiActivator.getResources()
- .getI18NString("service.gui.MSG_NOT_DELIVERED");
- }
- else if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.PROVIDER_NOT_REGISTERED)
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_SEND_CONNECTION_PROBLEM");
- }
- else if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.INTERNAL_ERROR)
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_DELIVERY_INTERNAL_ERROR");
- }
- else if (evt.getErrorCode()
- == MessageDeliveryFailedEvent.UNSUPPORTED_OPERATION)
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_DELIVERY_UNSUPPORTED_OPERATION");
- }
- else
- {
- errorMsg = GuiActivator.getResources().getI18NString(
- "service.gui.MSG_DELIVERY_UNKNOWN_ERROR");
- }
-
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
- ChatPanel chatPanel
- = chatWindowManager.getMultiChat(sourceChatRoom, true);
-
- chatPanel.addMessage(
- destParticipant.getDisplayName(),
- new Date(),
- Chat.OUTGOING_MESSAGE,
- sourceMessage.getContent(),
- sourceMessage.getContentType());
-
- chatPanel.addErrorMessage(
- destParticipant.getDisplayName(),
- errorMsg);
-
- chatWindowManager.openChat(chatPanel, false);
- }
-
- /**
- * Implements the <tt>AdHocChatRoomMessageListener.messageReceived</tt>
- * method.
- * <br>
- * Obtains the corresponding <tt>ChatPanel</tt> and process the message
- * there.
- * @param evt the <tt>AdHocChatRoomMessageReceivedEvent</tt> that notified
- * us
- */
- public void messageReceived(AdHocChatRoomMessageReceivedEvent evt)
- {
- AdHocChatRoom sourceChatRoom = evt.getSourceChatRoom();
- Contact sourceParticipant = evt.getSourceChatRoomParticipant();
-
- String messageType = null;
-
- switch (evt.getEventType())
- {
- case AdHocChatRoomMessageReceivedEvent.CONVERSATION_MESSAGE_RECEIVED:
- messageType = Chat.INCOMING_MESSAGE;
- break;
- case AdHocChatRoomMessageReceivedEvent.SYSTEM_MESSAGE_RECEIVED:
- messageType = Chat.SYSTEM_MESSAGE;
- break;
- case AdHocChatRoomMessageReceivedEvent.ACTION_MESSAGE_RECEIVED:
- messageType = Chat.ACTION_MESSAGE;
- break;
- }
-
- if (logger.isInfoEnabled())
- logger.info("MESSAGE RECEIVED from contact: "
- + sourceParticipant.getAddress());
-
- Message message = evt.getMessage();
-
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
- ChatPanel chatPanel
- = chatWindowManager
- .getMultiChat(sourceChatRoom, true, message.getMessageUID());
-
- String messageContent = message.getContent();
-
- chatPanel.addMessage(
- sourceParticipant.getDisplayName(),
- null,
- evt.getTimestamp(),
- messageType,
- messageContent,
- message.getContentType(),
- message.getMessageUID(),
- null);
-
- chatWindowManager.openChat(chatPanel, false);
- }
-
- public void invitationRejected(AdHocChatRoomInvitationRejectedEvent evt) {}
-
- @Override
- public void localUserRoleChanged(ChatRoomLocalUserRoleChangeEvent evt)
- {
- if(evt.isInitial())
- return;
- ChatRoom sourceChatRoom = evt.getSourceChatRoom();
- ChatRoomWrapper chatRoomWrapper
- = GuiActivator.getMUCService().findChatRoomWrapperFromChatRoom(
- sourceChatRoom);
- ChatWindowManager chatWindowManager
- = GuiActivator.getUIService().getChatWindowManager();
- ChatPanel chatPanel
- = chatWindowManager.getMultiChat(chatRoomWrapper, true);
- chatWindowManager.openChat(chatPanel, true);
- }
-
-}
+package net.java.sip.communicator.impl.gui.main.chat.conference;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import javax.swing.*;
+import javax.swing.SwingWorker;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.chat.*;
+import net.java.sip.communicator.impl.gui.main.chat.history.*;
+import net.java.sip.communicator.impl.gui.main.chatroomslist.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.muc.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.globalstatus.*;
+import net.java.sip.communicator.util.*;
+import net.java.sip.communicator.util.Logger;
+
+import org.jitsi.util.*;
+import org.osgi.framework.*;
+
+/**
+ * The <tt>ConferenceChatManager</tt> is the one that manages both chat room and
+ * ad-hoc chat rooms invitations.
+ *
+ * @author Yana Stamcheva
+ * @author Lubomir Marinov
+ * @author Valentin Martinet
+ * @author Hristo Terezov
+ */
+public class ConferenceChatManager
+ implements ChatRoomMessageListener,
+ ChatRoomInvitationListener,
+ ChatRoomInvitationRejectionListener,
+ AdHocChatRoomMessageListener,
+ AdHocChatRoomInvitationListener,
+ AdHocChatRoomInvitationRejectionListener,
+ LocalUserChatRoomPresenceListener,
+ LocalUserAdHocChatRoomPresenceListener,
+ ServiceListener, ChatRoomLocalUserRoleListener
+{
+ /**
+ * The object used for logging.
+ */
+ private static final Logger logger
+ = Logger.getLogger(ConferenceChatManager.class);
+
+ /**
+ * Maps each history window to a <tt>ChatRoomWrapper</tt>.
+ */
+ private final Hashtable<ChatRoomWrapper, HistoryWindow> chatRoomHistory =
+ new Hashtable<ChatRoomWrapper, HistoryWindow>();
+
+ /**
+ * The list of ad-hoc chat rooms.
+ */
+ private final AdHocChatRoomList adHocChatRoomList = new AdHocChatRoomList();
+
+ /**
+ * A list of all <tt>AdHocChatRoomListChangeListener</tt>-s.
+ */
+ private final Vector<AdHocChatRoomListChangeListener>
+ adHoclistChangeListeners = new Vector<AdHocChatRoomListChangeListener>();
+
+ /**
+ * Creates an instance of <tt>ConferenceChatManager</tt>.
+ */
+ public ConferenceChatManager()
+ {
+ // Loads the chat rooms list in a separate thread.
+ new Thread()
+ {
+ @Override
+ public void run()
+ {
+ adHocChatRoomList.loadList();
+ }
+ }.start();
+
+ GuiActivator.bundleContext.addServiceListener(this);
+
+ }
+
+ /**
+ * Returns all chat room providers currently contained in the ad-hoc chat
+ * room list.
+ *
+ * @return all chat room providers currently contained in the ad-hoc chat
+ * room list.
+ */
+ public AdHocChatRoomList getAdHocChatRoomList()
+ {
+ return adHocChatRoomList;
+ }
+
+ /**
+ * Handles <tt>ChatRoomInvitationReceivedEvent</tt>-s.
+ */
+ public void invitationReceived(ChatRoomInvitationReceivedEvent evt)
+ {
+ InvitationReceivedDialog dialog
+ = new InvitationReceivedDialog(
+ this,
+ evt.getSourceOperationSet(),
+ evt.getInvitation());
+
+ dialog.setVisible(true);
+ }
+
+ public void invitationRejected(ChatRoomInvitationRejectedEvent evt) {}
+
+ /**
+ * Implements the <tt>ChatRoomMessageListener.messageDelivered</tt> method.
+ * <br>
+ * Shows the message in the conversation area and clears the write message
+ * area.
+ * @param evt the <tt>ChatRoomMessageDeliveredEvent</tt> that notified us
+ * that the message was delivered to its destination
+ */
+ public void messageDelivered(ChatRoomMessageDeliveredEvent evt)
+ {
+ ChatRoom sourceChatRoom = (ChatRoom) evt.getSource();
+
+ if (logger.isTraceEnabled())
+ logger.trace(
+ "MESSAGE DELIVERED to chat room: " + sourceChatRoom.getName());
+
+ ChatPanel chatPanel = GuiActivator.getUIService().getChatWindowManager()
+ .getMultiChat(sourceChatRoom, false);
+
+ if(chatPanel != null)
+ {
+ String messageType;
+
+ switch (evt.getEventType())
+ {
+ case ChatRoomMessageDeliveredEvent.CONVERSATION_MESSAGE_DELIVERED:
+ messageType = Chat.OUTGOING_MESSAGE;
+ break;
+ case ChatRoomMessageDeliveredEvent.ACTION_MESSAGE_DELIVERED:
+ messageType = Chat.ACTION_MESSAGE;
+ break;
+ default:
+ messageType = null;
+ break;
+ }
+
+ Message msg = evt.getMessage();
+
+ chatPanel.addMessage(
+ sourceChatRoom.getUserNickname(),
+ null,
+ evt.getTimestamp(),
+ messageType,
+ msg.getContent(),
+ msg.getContentType(),
+ msg.getMessageUID(),
+ null);
+ }
+ }
+
+ /**
+ * Implements the <tt>ChatRoomMessageListener.messageReceived</tt> method.
+ * <br>
+ * Obtains the corresponding <tt>ChatPanel</tt> and process the message
+ * there.
+ * @param evt the <tt>ChatRoomMessageReceivedEvent</tt> that notified us
+ * that a message has been received
+ */
+ public void messageReceived(ChatRoomMessageReceivedEvent evt)
+ {
+ ChatRoom sourceChatRoom = evt.getSourceChatRoom();
+ ChatRoomMember sourceMember = evt.getSourceChatRoomMember();
+
+ String messageType = null;
+
+ switch (evt.getEventType())
+ {
+ case ChatRoomMessageReceivedEvent.CONVERSATION_MESSAGE_RECEIVED:
+ messageType = Chat.INCOMING_MESSAGE;
+ break;
+ case ChatRoomMessageReceivedEvent.SYSTEM_MESSAGE_RECEIVED:
+ messageType = Chat.SYSTEM_MESSAGE;
+ break;
+ case ChatRoomMessageReceivedEvent.ACTION_MESSAGE_RECEIVED:
+ messageType = Chat.ACTION_MESSAGE;
+ break;
+ }
+
+ if (logger.isTraceEnabled())
+ logger.trace("MESSAGE RECEIVED from contact: "
+ + sourceMember.getContactAddress());
+
+ Message message = evt.getMessage();
+
+ ChatPanel chatPanel = null;
+
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+
+ boolean createWindow = false;
+ String autoOpenConfig
+ = MUCService.getChatRoomAutoOpenOption(
+ sourceChatRoom.getParentProvider(),
+ sourceChatRoom.getIdentifier());
+ if(autoOpenConfig == null)
+ autoOpenConfig = MUCService.DEFAULT_AUTO_OPEN_BEHAVIOUR;
+
+ if(autoOpenConfig.equals(MUCService.OPEN_ON_ACTIVITY)
+ || (autoOpenConfig.equals(MUCService.OPEN_ON_MESSAGE)
+ && !evt.isHistoryMessage())
+ || evt.isImportantMessage())
+ createWindow = true;
+
+ if(sourceChatRoom.isSystem())
+ {
+ ChatRoomProviderWrapper serverWrapper
+ = GuiActivator.getMUCService().findServerWrapperFromProvider(
+ sourceChatRoom.getParentProvider());
+
+ chatPanel = chatWindowManager.getMultiChat(
+ serverWrapper.getSystemRoomWrapper(), createWindow);
+ }
+ else
+ {
+ chatPanel = chatWindowManager.getMultiChat(
+ sourceChatRoom, createWindow, message.getMessageUID());
+ }
+
+ if(chatPanel == null)
+ return;
+
+ String messageContent = message.getContent();
+
+ if (evt.isHistoryMessage())
+ {
+ Date timeStamp = chatPanel.getChatConversationPanel()
+ .getLastIncomingMsgTimestamp();
+ Collection<Object> c =
+ chatPanel.getChatSession().getHistoryBeforeDate(
+ new Date(
+ timeStamp.equals(new Date(0))
+ ? System.currentTimeMillis() - 10000
+ : timeStamp.getTime()
+ ), 20);
+ if (c.size() > 0)
+ {
+ boolean isPresent = false;
+ for (Object o : c)
+ {
+ if (o instanceof ChatRoomMessageDeliveredEvent)
+ {
+ ChatRoomMessageDeliveredEvent ev =
+ (ChatRoomMessageDeliveredEvent) o;
+ if (evt.getTimestamp() != null
+ && evt.getTimestamp().equals(ev.getTimestamp()))
+ {
+ isPresent = true;
+ break;
+ }
+ }
+ else if(o instanceof ChatRoomMessageReceivedEvent)
+ {
+ ChatRoomMessageReceivedEvent ev =
+ (ChatRoomMessageReceivedEvent) o;
+ if (evt.getTimestamp() != null
+ && evt.getTimestamp().equals(ev.getTimestamp()))
+ {
+ isPresent = true;
+ break;
+ }
+ }
+
+ Message m2 = evt.getMessage();
+
+ if(m2 != null
+ && m2.getContent().equals(messageContent))
+ {
+ isPresent = true;
+ break;
+ }
+ }
+
+ if (isPresent)
+ return;
+ }
+ }
+
+ chatPanel.addMessage(
+ sourceMember.getName(),
+ null,
+ evt.getTimestamp(),
+ messageType,
+ messageContent,
+ message.getContentType(),
+ message.getMessageUID(),
+ null);
+
+ if(createWindow)
+ chatWindowManager.openChat(chatPanel, false);
+ }
+
+ /**
+ * Implements the <tt>ChatRoomMessageListener.messageDeliveryFailed</tt>
+ * method.
+ * <br>
+ * In the conversation area shows an error message, explaining the problem.
+ * @param evt the <tt>ChatRoomMessageDeliveryFailedEvent</tt> that notified
+ * us of a delivery failure
+ */
+ public void messageDeliveryFailed(ChatRoomMessageDeliveryFailedEvent evt)
+ {
+ ChatRoom sourceChatRoom = evt.getSourceChatRoom();
+
+ String errorMsg = null;
+
+ /*
+ * FIXME ChatRoomMessageDeliveryFailedEvent#getSource() is not a Message
+ * instance at the time of this writing and the attempt "(Message)
+ * evt.getSource()" seems to be to get the message which failed to be
+ * delivered. I'm not sure it's
+ * ChatRoomMessageDeliveryFailedEvent#getMessage() but since it's the
+ * only message I can get out of ChatRoomMessageDeliveryFailedEvent, I'm
+ * using it.
+ */
+ Message sourceMessage = evt.getMessage();
+
+ ChatRoomMember destMember = evt.getDestinationChatRoomMember();
+
+ if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED)
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_DELIVERY_NOT_SUPPORTED",
+ new String[]{destMember.getName()});
+ }
+ else if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.NETWORK_FAILURE)
+ {
+ errorMsg = GuiActivator.getResources()
+ .getI18NString("service.gui.MSG_NOT_DELIVERED");
+ }
+ else if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.PROVIDER_NOT_REGISTERED)
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_SEND_CONNECTION_PROBLEM");
+ }
+ else if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.INTERNAL_ERROR)
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_DELIVERY_INTERNAL_ERROR");
+ }
+ else if (evt.getErrorCode()
+ == ChatRoomMessageDeliveryFailedEvent.FORBIDDEN)
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_SEND_MSG_FORBIDDEN");
+ }
+ else if (evt.getErrorCode()
+ == ChatRoomMessageDeliveryFailedEvent.UNSUPPORTED_OPERATION)
+ {
+ errorMsg =
+ GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_DELIVERY_UNSUPPORTED_OPERATION");
+ }
+ else
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_DELIVERY_UNKNOWN_ERROR");
+ }
+
+ String reason = evt.getReason();
+ if (reason != null)
+ errorMsg += " " + GuiActivator.getResources().getI18NString(
+ "service.gui.ERROR_WAS",
+ new String[]{reason});
+
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+ ChatPanel chatPanel
+ = chatWindowManager.getMultiChat(sourceChatRoom, true);
+
+ chatPanel.addMessage(
+ destMember != null ? destMember.getName()
+ : sourceChatRoom.getName(),
+ new Date(),
+ Chat.OUTGOING_MESSAGE,
+ sourceMessage.getContent(),
+ sourceMessage.getContentType());
+
+ chatPanel.addErrorMessage(
+ destMember != null ? destMember.getName()
+ : sourceChatRoom.getName(),
+ errorMsg);
+
+ chatWindowManager.openChat(chatPanel, false);
+ }
+
+ /**
+ * Implements the
+ * <tt>LocalUserAdHocChatRoomPresenceListener.localUserPresenceChanged</tt>
+ * method
+ *
+ * @param evt the <tt>LocalUserAdHocChatRoomPresenceChangeEvent</tt> that
+ * notified us of a presence change
+ */
+ public void localUserAdHocPresenceChanged(
+ LocalUserAdHocChatRoomPresenceChangeEvent evt)
+ {
+ AdHocChatRoom sourceAdHocChatRoom = evt.getAdHocChatRoom();
+ AdHocChatRoomWrapper adHocChatRoomWrapper
+ = adHocChatRoomList
+ .findChatRoomWrapperFromAdHocChatRoom(sourceAdHocChatRoom);
+
+ String eventType = evt.getEventType();
+
+ if (LocalUserAdHocChatRoomPresenceChangeEvent
+ .LOCAL_USER_JOINED.equals(eventType))
+ {
+ if(adHocChatRoomWrapper != null)
+ {
+ this.fireAdHocChatRoomListChangedEvent(
+ adHocChatRoomWrapper,
+ AdHocChatRoomListChangeEvent.AD_HOC_CHAT_ROOM_CHANGED);
+
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+ ChatPanel chatPanel
+ = chatWindowManager
+ .getMultiChat(adHocChatRoomWrapper, true);
+
+ // Check if we have already opened a chat window for this chat
+ // wrapper and load the real chat room corresponding to the
+ // wrapper.
+ if(chatPanel.isShown())
+ ((AdHocConferenceChatSession) chatPanel.getChatSession())
+ .loadChatRoom(sourceAdHocChatRoom);
+ else
+ chatWindowManager.openChat(chatPanel, true);
+ }
+
+ sourceAdHocChatRoom.addMessageListener(this);
+ }
+ else if (evt.getEventType().equals(
+ LocalUserAdHocChatRoomPresenceChangeEvent.LOCAL_USER_JOIN_FAILED))
+ {
+ GuiActivator.getAlertUIService().showAlertPopup(
+ GuiActivator.getResources().getI18NString("service.gui.ERROR"),
+ GuiActivator.getResources().getI18NString(
+ "service.gui.FAILED_TO_JOIN_CHAT_ROOM",
+ new String[]{sourceAdHocChatRoom.getName()})
+ + evt.getReason());
+ }
+ else if (LocalUserAdHocChatRoomPresenceChangeEvent
+ .LOCAL_USER_LEFT.equals(eventType)
+ || LocalUserAdHocChatRoomPresenceChangeEvent
+ .LOCAL_USER_DROPPED.equals(eventType))
+ {
+ this.closeAdHocChatRoom(adHocChatRoomWrapper);
+
+ // Need to refresh the chat room's list in order to change
+ // the state of the chat room to offline.
+ fireAdHocChatRoomListChangedEvent(
+ adHocChatRoomWrapper,
+ AdHocChatRoomListChangeEvent.AD_HOC_CHAT_ROOM_CHANGED);
+
+ sourceAdHocChatRoom.removeMessageListener(this);
+ }
+ }
+
+ /**
+ * Implements the
+ * <tt>LocalUserChatRoomPresenceListener.localUserPresenceChanged</tt>
+ * method.
+ * @param evt the <tt>LocalUserChatRoomPresenceChangeEvent</tt> that
+ * notified us
+ */
+ public void localUserPresenceChanged(
+ final LocalUserChatRoomPresenceChangeEvent evt)
+ {
+ if(!SwingUtilities.isEventDispatchThread())
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ localUserPresenceChanged(evt);
+ }
+ });
+ return;
+ }
+
+ ChatRoom sourceChatRoom = evt.getChatRoom();
+ ChatRoomWrapper chatRoomWrapper
+ = GuiActivator.getMUCService().findChatRoomWrapperFromChatRoom(
+ sourceChatRoom);
+
+ String eventType = evt.getEventType();
+
+ if (LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_JOINED.equals(eventType))
+ {
+ if(chatRoomWrapper != null)
+ {
+ GuiActivator.getMUCService().fireChatRoomListChangedEvent(
+ chatRoomWrapper,
+ ChatRoomListChangeEvent.CHAT_ROOM_CHANGED);
+
+ boolean createWindow = false;
+
+ String autoOpenConfig
+ = MUCService.getChatRoomAutoOpenOption(
+ sourceChatRoom.getParentProvider(),
+ sourceChatRoom.getIdentifier());
+
+ if(autoOpenConfig != null
+ && autoOpenConfig.equals(MUCService.OPEN_ON_ACTIVITY))
+ createWindow = true;
+
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+ ChatPanel chatPanel
+ = chatWindowManager.getMultiChat(
+ chatRoomWrapper, createWindow);
+
+ if(chatPanel != null)
+ {
+ chatPanel.setChatIcon(
+ chatPanel.getChatSession().getChatStatusIcon());
+
+ // Check if we have already opened a chat window for this chat
+ // wrapper and load the real chat room corresponding to the
+ // wrapper.
+ if(chatPanel.isShown())
+ {
+ ((ConferenceChatSession) chatPanel.getChatSession())
+ .loadChatRoom(sourceChatRoom);
+ }
+ else
+ {
+ chatWindowManager.openChat(chatPanel, true);
+ }
+ }
+ }
+
+ if (sourceChatRoom.isSystem())
+ {
+ ChatRoomProviderWrapper serverWrapper
+ = GuiActivator.getMUCService()
+ .findServerWrapperFromProvider(
+ sourceChatRoom.getParentProvider());
+
+ serverWrapper.setSystemRoom(sourceChatRoom);
+ }
+
+ sourceChatRoom.addMessageListener(this);
+ sourceChatRoom.addLocalUserRoleListener(this);
+ }
+ else if (LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_JOIN_FAILED.equals(eventType))
+ {
+ GuiActivator.getAlertUIService().showAlertPopup(
+ GuiActivator.getResources().getI18NString("service.gui.ERROR"),
+ GuiActivator.getResources().getI18NString(
+ "service.gui.FAILED_TO_JOIN_CHAT_ROOM",
+ new String[]{sourceChatRoom.getName()})
+ + evt.getReason());
+ }
+ else if (LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_LEFT.equals(eventType)
+ || LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_KICKED.equals(eventType)
+ || LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_DROPPED.equals(eventType))
+ {
+ if(chatRoomWrapper != null)
+ {
+ if(StringUtils.isNullOrEmpty(evt.getReason()))
+ {
+ GuiActivator.getUIService()
+ .closeChatRoomWindow(chatRoomWrapper);
+ }
+ else
+ {
+ // send some system messages informing for the
+ // reason of leaving
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+
+ ChatPanel chatPanel = chatWindowManager.getMultiChat(
+ sourceChatRoom, false);
+
+ if(chatPanel != null)
+ {
+ chatPanel.addMessage(
+ sourceChatRoom.getName(),
+ null,
+ new Date(),
+ Chat.SYSTEM_MESSAGE,
+ evt.getReason(),
+ OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE,
+ null,
+ null);
+
+ // print and the alternate address
+ if(!StringUtils.isNullOrEmpty(
+ evt.getAlternateAddress()))
+ {
+ chatPanel.addMessage(
+ sourceChatRoom.getName(),
+ null,
+ new Date(),
+ Chat.SYSTEM_MESSAGE,
+ GuiActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_ALTERNATE_ADDRESS",
+ new String[]{evt.getAlternateAddress()}),
+ OperationSetBasicInstantMessaging
+ .DEFAULT_MIME_TYPE,
+ null,
+ null);
+ }
+ }
+ }
+
+ // Need to refresh the chat room's list in order to change
+ // the state of the chat room to offline.
+
+ GuiActivator.getMUCService().fireChatRoomListChangedEvent(
+ chatRoomWrapper,
+ ChatRoomListChangeEvent.CHAT_ROOM_CHANGED);
+ }
+
+ sourceChatRoom.removeMessageListener(this);
+ sourceChatRoom.removelocalUserRoleListener(this);
+ }
+ }
+
+
+ /**
+ * Called to accept an incoming invitation. Adds the invitation chat room
+ * to the list of chat rooms and joins it.
+ *
+ * @param invitation the invitation to accept
+ * @param multiUserChatOpSet the operation set for chat conferencing
+ * @throws OperationFailedException if the accept fails
+ */
+ public void acceptInvitation(
+ AdHocChatRoomInvitation invitation,
+ OperationSetAdHocMultiUserChat multiUserChatOpSet)
+ throws OperationFailedException
+ {
+ AdHocChatRoom chatRoom = invitation.getTargetAdHocChatRoom();
+
+ chatRoom.join();
+ }
+
+ /**
+ * Rejects the given invitation with the specified reason.
+ *
+ * @param multiUserChatAdHocOpSet the operation set to use for rejecting the
+ * invitation
+ * @param invitation the invitation to reject
+ * @param reason the reason for the rejection
+ */
+ public void rejectInvitation(
+ OperationSetAdHocMultiUserChat multiUserChatAdHocOpSet,
+ AdHocChatRoomInvitation invitation,
+ String reason)
+ {
+ multiUserChatAdHocOpSet.rejectInvitation(invitation, reason);
+ }
+
+ /**
+ * Creates an ad-hoc chat room, by specifying the ad-hoc chat room name, the
+ * parent protocol provider and eventually, the contacts invited to
+ * participate in this ad-hoc chat room.
+ *
+ * @param protocolProvider the parent protocol provider.
+ * @param contacts the contacts invited when creating the chat room.
+ * @param reason the reason for this invitation
+ * @return the <tt>AdHocChatRoomWrapper</tt> corresponding to the created
+ * ad hoc chat room
+ */
+ public AdHocChatRoomWrapper createAdHocChatRoom(
+ ProtocolProviderService protocolProvider,
+ Collection<String> contacts,
+ String reason)
+ {
+ AdHocChatRoomWrapper chatRoomWrapper = null;
+
+ OperationSetAdHocMultiUserChat groupChatOpSet
+ = protocolProvider
+ .getOperationSet(OperationSetAdHocMultiUserChat.class);
+
+ // If there's no group chat operation set we have nothing to do here.
+ if (groupChatOpSet == null)
+ return null;
+
+ AdHocChatRoom chatRoom = null;
+
+ try
+ {
+ java.util.List<String> members = new LinkedList<String>();
+
+ for(String address : contacts)
+ members.add(address);
+
+ chatRoom = groupChatOpSet.createAdHocChatRoom(
+ "chatroom-" + new Date().getTime(), members, reason);
+ }
+ catch (OperationFailedException ex)
+ {
+ new ErrorDialog(
+ GuiActivator.getUIService().getMainFrame(),
+ GuiActivator.getResources().getI18NString("service.gui.ERROR"),
+ GuiActivator.getResources().getI18NString(
+ "service.gui.CREATE_CHAT_ROOM_ERROR",
+ new String[]{protocolProvider.getProtocolDisplayName()}),
+ ex)
+ .showDialog();
+ }
+ catch (OperationNotSupportedException ex)
+ {
+ new ErrorDialog(
+ GuiActivator.getUIService().getMainFrame(),
+ GuiActivator.getResources().getI18NString("service.gui.ERROR"),
+ GuiActivator.getResources().getI18NString(
+ "service.gui.CREATE_CHAT_ROOM_ERROR",
+ new String[]{protocolProvider.getProtocolDisplayName()}),
+ ex)
+ .showDialog();
+ }
+
+ if(chatRoom != null)
+ {
+ AdHocChatRoomProviderWrapper parentProvider
+ = adHocChatRoomList.findServerWrapperFromProvider(
+ protocolProvider);
+
+ chatRoomWrapper = new AdHocChatRoomWrapper(
+ parentProvider, chatRoom);
+ parentProvider.addAdHocChatRoom(chatRoomWrapper);
+ adHocChatRoomList.addAdHocChatRoom(chatRoomWrapper);
+
+ fireAdHocChatRoomListChangedEvent(
+ chatRoomWrapper,
+ AdHocChatRoomListChangeEvent.AD_HOC_CHAT_ROOM_ADDED);
+ }
+
+ return chatRoomWrapper;
+ }
+
+ /**
+ * Joins the given ad-hoc chat room
+ *
+ * @param chatRoomWrapper
+ */
+ public void joinChatRoom(AdHocChatRoomWrapper chatRoomWrapper)
+ {
+ AdHocChatRoom chatRoom = chatRoomWrapper.getAdHocChatRoom();
+
+ if(chatRoom == null)
+ {
+ new ErrorDialog(
+ GuiActivator.getUIService().getMainFrame(),
+ GuiActivator.getResources().getI18NString("service.gui.WARNING"),
+ GuiActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_NOT_CONNECTED",
+ new String[]{chatRoomWrapper.getAdHocChatRoomName()}))
+ .showDialog();
+
+ return;
+ }
+
+ new JoinAdHocChatRoomTask(chatRoomWrapper).execute();
+ }
+
+ /**
+ * Removes the given chat room from the UI.
+ *
+ * @param chatRoomWrapper the chat room to remove.
+ */
+ public void removeChatRoom(ChatRoomWrapper chatRoomWrapper)
+ {
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+
+ if (chatRoom != null)
+ leaveChatRoom(chatRoomWrapper);
+
+ GuiActivator.getUIService().closeChatRoomWindow(chatRoomWrapper);
+
+ GuiActivator.getMUCService().removeChatRoom(chatRoomWrapper);
+
+ }
+
+ /**
+ * Joins the given chat room and manages all the exceptions that could
+ * occur during the join process.
+ *
+ * @param chatRoom the chat room to join
+ */
+ public void joinChatRoom(AdHocChatRoom chatRoom)
+ {
+ AdHocChatRoomWrapper chatRoomWrapper
+ = adHocChatRoomList.findChatRoomWrapperFromAdHocChatRoom(chatRoom);
+
+ if(chatRoomWrapper == null)
+ {
+ AdHocChatRoomProviderWrapper parentProvider
+ = adHocChatRoomList.findServerWrapperFromProvider(
+ chatRoom.getParentProvider());
+
+ chatRoomWrapper =
+ new AdHocChatRoomWrapper(parentProvider, chatRoom);
+
+ adHocChatRoomList.addAdHocChatRoom(chatRoomWrapper);
+
+ fireAdHocChatRoomListChangedEvent(
+ chatRoomWrapper,
+ AdHocChatRoomListChangeEvent.AD_HOC_CHAT_ROOM_ADDED);
+ }
+
+ this.joinChatRoom(chatRoomWrapper);
+
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+
+ chatWindowManager
+ .openChat(
+ chatWindowManager.getMultiChat(chatRoomWrapper, true),
+ true);
+ }
+
+ /**
+ * Leaves the given <tt>ChatRoom</tt>.
+ *
+ * @param chatRoomWrapper the <tt>ChatRoom</tt> to leave.
+ */
+ public void leaveChatRoom(ChatRoomWrapper chatRoomWrapper)
+ {
+ ChatRoomWrapper leavedRoomWrapped
+ = GuiActivator.getMUCService().leaveChatRoom(chatRoomWrapper);
+ if(leavedRoomWrapped != null)
+ GuiActivator.getUIService().closeChatRoomWindow(leavedRoomWrapped);
+ }
+
+ /**
+ * Leaves the given <tt>ChatRoom</tt>.
+ *
+ * @param chatRoomWrapper the <tt>ChatRoom</tt> to leave.
+ */
+ public void leaveChatRoom(AdHocChatRoomWrapper chatRoomWrapper)
+ {
+ AdHocChatRoom chatRoom = chatRoomWrapper.getAdHocChatRoom();
+
+ if (chatRoom != null)
+ {
+ chatRoom.leave();
+ }
+ else
+ {
+ new ErrorDialog(
+ GuiActivator.getUIService().getMainFrame(),
+ GuiActivator.getResources().getI18NString("service.gui.WARNING"),
+ GuiActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_LEAVE_NOT_CONNECTED"))
+ .showDialog();
+ }
+ }
+
+ /**
+ * Checks if there's an open history window for the given chat room.
+ *
+ * @param chatRoomWrapper the chat room wrapper to check for
+ * @return TRUE if there's an opened history window for the given chat room,
+ * FALSE otherwise.
+ */
+ public boolean containsHistoryWindowForChatRoom(
+ ChatRoomWrapper chatRoomWrapper)
+ {
+ return chatRoomHistory.containsKey(chatRoomWrapper);
+ }
+
+ /**
+ * Returns the history window for the given chat room.
+ *
+ * @param chatRoomWrapper the chat room wrapper to search for
+ * @return the history window for the given chat room
+ */
+ public HistoryWindow getHistoryWindowForChatRoom(
+ ChatRoomWrapper chatRoomWrapper)
+ {
+ return chatRoomHistory.get(chatRoomWrapper);
+ }
+
+ /**
+ * Adds a history window for a given chat room in the table of opened
+ * history windows.
+ *
+ * @param chatRoomWrapper the chat room wrapper to add
+ * @param historyWindow the history window to add
+ */
+ public void addHistoryWindowForChatRoom(ChatRoomWrapper chatRoomWrapper,
+ HistoryWindow historyWindow)
+ {
+ chatRoomHistory.put(chatRoomWrapper, historyWindow);
+ }
+
+ /**
+ * Removes the history window for the given chat room.
+ *
+ * @param chatRoomWrapper the chat room wrapper to remove the history window
+ */
+ public void removeHistoryWindowForChatRoom(ChatRoomWrapper chatRoomWrapper)
+ {
+ chatRoomHistory.remove(chatRoomWrapper);
+ }
+
+ /**
+ * Adds the given <tt>AdHocChatRoomListChangeListener</tt> that will listen
+ * for all changes of the chat room list data model.
+ *
+ * @param l the listener to add.
+ */
+ public void addAdHocChatRoomListChangeListener(
+ AdHocChatRoomListChangeListener l)
+ {
+ synchronized (adHoclistChangeListeners)
+ {
+ adHoclistChangeListeners.add(l);
+ }
+ }
+
+ /**
+ * Removes the given <tt>AdHocChatRoomListChangeListener</tt>.
+ *
+ * @param l the listener to remove.
+ */
+ public void removeAdHocChatRoomListChangeListener(
+ AdHocChatRoomListChangeListener l)
+ {
+ synchronized (adHoclistChangeListeners)
+ {
+ adHoclistChangeListeners.remove(l);
+ }
+ }
+
+ /**
+ * Notifies all interested listeners that a change in the chat room list
+ * model has occurred.
+ * @param adHocChatRoomWrapper the chat room wrapper that identifies the
+ * chat room
+ * @param eventID the identifier of the event
+ */
+ private void fireAdHocChatRoomListChangedEvent(
+ AdHocChatRoomWrapper adHocChatRoomWrapper,
+ int eventID)
+ {
+ AdHocChatRoomListChangeEvent evt
+ = new AdHocChatRoomListChangeEvent(adHocChatRoomWrapper, eventID);
+
+ for (AdHocChatRoomListChangeListener l : adHoclistChangeListeners)
+ {
+ l.contentChanged(evt);
+ }
+ }
+
+
+ /**
+ * Closes the chat corresponding to the given ad-hoc chat room wrapper, if
+ * such exists.
+ *
+ * @param chatRoomWrapper the ad-hoc chat room wrapper for which we search a
+ * chat to close.
+ */
+ private void closeAdHocChatRoom(AdHocChatRoomWrapper chatRoomWrapper)
+ {
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+ ChatPanel chatPanel
+ = chatWindowManager.getMultiChat(chatRoomWrapper, false);
+
+ if (chatPanel != null)
+ chatWindowManager.closeChat(chatPanel);
+ }
+
+ /**
+ * Handles <tt>ServiceEvent</tt>s triggered by adding or removing a
+ * ProtocolProviderService. Updates the list of available chat rooms and
+ * chat room servers.
+ *
+ * @param event The event to handle.
+ */
+ public void serviceChanged(ServiceEvent event)
+ {
+ // if the event is caused by a bundle being stopped, we don't want to
+ // know
+ if (event.getServiceReference().getBundle().getState()
+ == Bundle.STOPPING)
+ return;
+
+ Object service = GuiActivator.bundleContext.getService(event
+ .getServiceReference());
+
+ // we don't care if the source service is not a protocol provider
+ if (!(service instanceof ProtocolProviderService))
+ return;
+
+ ProtocolProviderService protocolProvider
+ = (ProtocolProviderService) service;
+
+
+ Object multiUserChatAdHocOpSet
+ = protocolProvider
+ .getOperationSet(OperationSetAdHocMultiUserChat.class);
+
+ if (multiUserChatAdHocOpSet != null)
+ {
+ if (event.getType() == ServiceEvent.REGISTERED)
+ {
+ adHocChatRoomList.addChatProvider(protocolProvider);
+ }
+ else if (event.getType() == ServiceEvent.UNREGISTERING)
+ {
+ adHocChatRoomList.removeChatProvider(protocolProvider);
+ }
+ }
+ }
+
+ /**
+ * Joins an ad-hoc chat room in an asynchronous way.
+ */
+ private static class JoinAdHocChatRoomTask
+ extends SwingWorker<String, Object>
+ {
+ private static final String SUCCESS = "Success";
+
+ private static final String AUTHENTICATION_FAILED
+ = "AuthenticationFailed";
+
+ private static final String REGISTRATION_REQUIRED
+ = "RegistrationRequired";
+
+ private static final String PROVIDER_NOT_REGISTERED
+ = "ProviderNotRegistered";
+
+ private static final String SUBSCRIPTION_ALREADY_EXISTS
+ = "SubscriptionAlreadyExists";
+
+ private static final String UNKNOWN_ERROR
+ = "UnknownError";
+
+ private final AdHocChatRoomWrapper adHocChatRoomWrapper;
+
+ JoinAdHocChatRoomTask(AdHocChatRoomWrapper chatRoomWrapper)
+ {
+ this.adHocChatRoomWrapper = chatRoomWrapper;
+ }
+
+ /**
+ * @override {@link SwingWorker}{@link #doInBackground()} to perform
+ * all asynchronous tasks.
+ * @return SUCCESS if success, otherwise the error code
+ */
+ @Override
+ public String doInBackground()
+ {
+ AdHocChatRoom chatRoom = adHocChatRoomWrapper.getAdHocChatRoom();
+
+ try
+ {
+ chatRoom.join();
+
+ return SUCCESS;
+ }
+ catch (OperationFailedException e)
+ {
+ if (logger.isTraceEnabled())
+ logger.trace("Failed to join ad-hoc chat room: "
+ + chatRoom.getName(), e);
+
+ switch (e.getErrorCode())
+ {
+ case OperationFailedException.AUTHENTICATION_FAILED:
+ return AUTHENTICATION_FAILED;
+ case OperationFailedException.REGISTRATION_REQUIRED:
+ return REGISTRATION_REQUIRED;
+ case OperationFailedException.PROVIDER_NOT_REGISTERED:
+ return PROVIDER_NOT_REGISTERED;
+ case OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS:
+ return SUBSCRIPTION_ALREADY_EXISTS;
+ default:
+ return UNKNOWN_ERROR;
+ }
+ }
+ }
+
+ /**
+ * @override {@link SwingWorker}{@link #done()} to perform UI changes
+ * after the ad-hoc chat room join task has finished.
+ */
+ @Override
+ protected void done()
+ {
+ String returnCode = null;
+ try
+ {
+ returnCode = get();
+ }
+ catch (InterruptedException ignore)
+ {}
+ catch (ExecutionException ignore)
+ {}
+
+ ConfigurationUtils.updateChatRoomStatus(
+ adHocChatRoomWrapper.getParentProvider().getProtocolProvider(),
+ adHocChatRoomWrapper.getAdHocChatRoomID(),
+ GlobalStatusEnum.ONLINE_STATUS);
+
+ String errorMessage = null;
+ if(PROVIDER_NOT_REGISTERED.equals(returnCode))
+ {
+ errorMessage
+ = GuiActivator.getResources()
+ .getI18NString("service.gui.CHAT_ROOM_NOT_CONNECTED",
+ new String[]{
+ adHocChatRoomWrapper.getAdHocChatRoomName()});
+ }
+ else if(SUBSCRIPTION_ALREADY_EXISTS.equals(returnCode))
+ {
+ errorMessage
+ = GuiActivator.getResources()
+ .getI18NString("service.gui.CHAT_ROOM_ALREADY_JOINED",
+ new String[]{
+ adHocChatRoomWrapper.getAdHocChatRoomName()});
+ }
+ else
+ {
+ errorMessage
+ = GuiActivator.getResources()
+ .getI18NString("service.gui.FAILED_TO_JOIN_CHAT_ROOM",
+ new String[]{
+ adHocChatRoomWrapper.getAdHocChatRoomName()});
+ }
+
+ if (!SUCCESS.equals(returnCode)
+ && !AUTHENTICATION_FAILED.equals(returnCode))
+ {
+ GuiActivator.getAlertUIService().showAlertPopup(
+ GuiActivator.getResources().getI18NString(
+ "service.gui.ERROR"), errorMessage);
+ }
+ }
+ }
+
+
+ /**
+ * Indicates that an invitation has been received and opens the invitation
+ * dialog to notify the user.
+ * @param evt the <tt>AdHocChatRoomInvitationReceivedEvent</tt> that
+ * notified us
+ */
+ public void invitationReceived(AdHocChatRoomInvitationReceivedEvent evt)
+ {
+ if (logger.isInfoEnabled())
+ logger.info("Invitation received: "+evt.toString());
+ OperationSetAdHocMultiUserChat multiUserChatOpSet
+ = evt.getSourceOperationSet();
+
+ InvitationReceivedDialog dialog = new InvitationReceivedDialog(
+ this, multiUserChatOpSet, evt.getInvitation());
+
+ dialog.setVisible(true);
+ }
+
+ /**
+ * Implements the <tt>AdHocChatRoomMessageListener.messageDelivered</tt>
+ * method.
+ * <br>
+ * Shows the message in the conversation area and clears the write message
+ * area.
+ * @param evt the <tt>AdHocChatRoomMessageDeliveredEvent</tt> that notified
+ * us
+ */
+ public void messageDelivered(AdHocChatRoomMessageDeliveredEvent evt)
+ {
+ AdHocChatRoom sourceChatRoom = (AdHocChatRoom) evt.getSource();
+
+ if (logger.isInfoEnabled())
+ logger.info("MESSAGE DELIVERED to ad-hoc chat room: "
+ + sourceChatRoom.getName());
+
+ ChatPanel chatPanel
+ = GuiActivator
+ .getUIService()
+ .getChatWindowManager()
+ .getMultiChat(sourceChatRoom, false);
+
+ if(chatPanel != null)
+ {
+ String messageType;
+ switch (evt.getEventType())
+ {
+ case AdHocChatRoomMessageDeliveredEvent
+ .CONVERSATION_MESSAGE_DELIVERED:
+ messageType = Chat.OUTGOING_MESSAGE;
+ break;
+ case AdHocChatRoomMessageDeliveredEvent.ACTION_MESSAGE_DELIVERED:
+ messageType = Chat.ACTION_MESSAGE;
+ break;
+ default:
+ messageType = null;
+ }
+
+ Message msg = evt.getMessage();
+
+ chatPanel
+ .addMessage(
+ sourceChatRoom
+ .getParentProvider().getAccountID().getUserID(),
+ null,
+ evt.getTimestamp(),
+ messageType,
+ msg.getContent(),
+ msg.getContentType(),
+ msg.getMessageUID(),
+ null);
+ }
+ else
+ {
+ logger.error("chat panel is null, message NOT DELIVERED !");
+ }
+ }
+
+ /**
+ * Implements <tt>AdHocChatRoomMessageListener.messageDeliveryFailed</tt>
+ * method.
+ * <br>
+ * In the conversation area shows an error message, explaining the problem.
+ * @param evt the <tt>AdHocChatRoomMessageDeliveryFailedEvent</tt> that
+ * notified us
+ */
+ public void messageDeliveryFailed(
+ AdHocChatRoomMessageDeliveryFailedEvent evt)
+ {
+ AdHocChatRoom sourceChatRoom = evt.getSourceChatRoom();
+ Message sourceMessage = evt.getMessage();
+ Contact destParticipant = evt.getDestinationParticipant();
+
+ String errorMsg = null;
+ if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED)
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_DELIVERY_NOT_SUPPORTED",
+ new String[]{destParticipant.getDisplayName()});
+ }
+ else if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.NETWORK_FAILURE)
+ {
+ errorMsg = GuiActivator.getResources()
+ .getI18NString("service.gui.MSG_NOT_DELIVERED");
+ }
+ else if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.PROVIDER_NOT_REGISTERED)
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_SEND_CONNECTION_PROBLEM");
+ }
+ else if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.INTERNAL_ERROR)
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_DELIVERY_INTERNAL_ERROR");
+ }
+ else if (evt.getErrorCode()
+ == MessageDeliveryFailedEvent.UNSUPPORTED_OPERATION)
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_DELIVERY_UNSUPPORTED_OPERATION");
+ }
+ else
+ {
+ errorMsg = GuiActivator.getResources().getI18NString(
+ "service.gui.MSG_DELIVERY_UNKNOWN_ERROR");
+ }
+
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+ ChatPanel chatPanel
+ = chatWindowManager.getMultiChat(sourceChatRoom, true);
+
+ chatPanel.addMessage(
+ destParticipant.getDisplayName(),
+ new Date(),
+ Chat.OUTGOING_MESSAGE,
+ sourceMessage.getContent(),
+ sourceMessage.getContentType());
+
+ chatPanel.addErrorMessage(
+ destParticipant.getDisplayName(),
+ errorMsg);
+
+ chatWindowManager.openChat(chatPanel, false);
+ }
+
+ /**
+ * Implements the <tt>AdHocChatRoomMessageListener.messageReceived</tt>
+ * method.
+ * <br>
+ * Obtains the corresponding <tt>ChatPanel</tt> and process the message
+ * there.
+ * @param evt the <tt>AdHocChatRoomMessageReceivedEvent</tt> that notified
+ * us
+ */
+ public void messageReceived(AdHocChatRoomMessageReceivedEvent evt)
+ {
+ AdHocChatRoom sourceChatRoom = evt.getSourceChatRoom();
+ Contact sourceParticipant = evt.getSourceChatRoomParticipant();
+
+ String messageType = null;
+
+ switch (evt.getEventType())
+ {
+ case AdHocChatRoomMessageReceivedEvent.CONVERSATION_MESSAGE_RECEIVED:
+ messageType = Chat.INCOMING_MESSAGE;
+ break;
+ case AdHocChatRoomMessageReceivedEvent.SYSTEM_MESSAGE_RECEIVED:
+ messageType = Chat.SYSTEM_MESSAGE;
+ break;
+ case AdHocChatRoomMessageReceivedEvent.ACTION_MESSAGE_RECEIVED:
+ messageType = Chat.ACTION_MESSAGE;
+ break;
+ }
+
+ if (logger.isInfoEnabled())
+ logger.info("MESSAGE RECEIVED from contact: "
+ + sourceParticipant.getAddress());
+
+ Message message = evt.getMessage();
+
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+ ChatPanel chatPanel
+ = chatWindowManager
+ .getMultiChat(sourceChatRoom, true, message.getMessageUID());
+
+ String messageContent = message.getContent();
+
+ chatPanel.addMessage(
+ sourceParticipant.getDisplayName(),
+ null,
+ evt.getTimestamp(),
+ messageType,
+ messageContent,
+ message.getContentType(),
+ message.getMessageUID(),
+ null);
+
+ chatWindowManager.openChat(chatPanel, false);
+ }
+
+ public void invitationRejected(AdHocChatRoomInvitationRejectedEvent evt) {}
+
+ @Override
+ public void localUserRoleChanged(ChatRoomLocalUserRoleChangeEvent evt)
+ {
+ if(evt.isInitial())
+ return;
+ ChatRoom sourceChatRoom = evt.getSourceChatRoom();
+ ChatRoomWrapper chatRoomWrapper
+ = GuiActivator.getMUCService().findChatRoomWrapperFromChatRoom(
+ sourceChatRoom);
+ ChatWindowManager chatWindowManager
+ = GuiActivator.getUIService().getChatWindowManager();
+ ChatPanel chatPanel
+ = chatWindowManager.getMultiChat(chatRoomWrapper, true);
+ chatWindowManager.openChat(chatPanel, true);
+ }
+
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/filetransfer/ReceiveFileConversationComponent.java b/src/net/java/sip/communicator/impl/gui/main/chat/filetransfer/ReceiveFileConversationComponent.java
index ef72a89..f5a698b 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chat/filetransfer/ReceiveFileConversationComponent.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/filetransfer/ReceiveFileConversationComponent.java
@@ -164,6 +164,10 @@ public class ReceiveFileConversationComponent
File downloadDir = null;
String incomingFileName = fileTransferRequest.getFileName();
+ // strip characters that are invalid on Windows and maybe other
+ // platforms too
+ incomingFileName = incomingFileName
+ .replaceAll("[\\\\/:*?\"<>|]", "_");
try
{
downloadDir = GuiActivator.getFileAccessService()
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java b/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java
index 6905df9..7080d13 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java
@@ -715,14 +715,19 @@ public class MainToolBar
m.put(opSetClass, ct.getProtocolProvider());
UIContactDetailImpl d = new UIContactDetailImpl(
- ct.getName(),
+ ct.getName() + (ct.getResourceName() == null
+ ? ""
+ : "/" + ct.getResourceName()),
ct.getDisplayName(),
null,
- null,
+ (ct.getResourceName() == null
+ ? Arrays.asList(GuiActivator.getResources().getI18NString("service.gui.VIA") + ": "
+ + ct.getProtocolProvider().getAccountID().getAccountAddress())
+ : null),
null,
m,
null,
- ct.getName());
+ ct);
PresenceStatus status = ct.getStatus();
byte[] statusIconBytes = status.getStatusIcon();
@@ -736,7 +741,7 @@ public class MainToolBar
res.add(d);
}
-
+
Point location = new Point(callButton.getX(),
callButton.getY() + callButton.getHeight());
diff --git a/src/net/java/sip/communicator/impl/gui/main/chatroomslist/ChatRoomTableDialog.java b/src/net/java/sip/communicator/impl/gui/main/chatroomslist/ChatRoomTableDialog.java
index 0efb860..28f3e08 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chatroomslist/ChatRoomTableDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chatroomslist/ChatRoomTableDialog.java
@@ -55,6 +55,13 @@ public class ChatRoomTableDialog
"REMOVE_ROOM_ON_FIRST_JOIN_FAILED";
/**
+ * Whether we should make rooms autojoin by default.
+ */
+ private static final String ENABLE_ROOM_AUTO_JOIN_ON_CREATION
+ = "net.java.sip.communicator.impl.gui.main.chatroomslist." +
+ "ENABLE_ROOM_AUTO_JOIN_ON_CREATION";
+
+ /**
* The global/shared <code>ChatRoomTableDialog</code> currently showing.
*/
private static ChatRoomTableDialog chatRoomTableDialog;
@@ -425,6 +432,12 @@ public class ChatRoomTableDialog
chatRoomWrapper.getChatRoomID(),
chatRoomWrapper.getChatRoomID(),
chatRoomWrapper.getChatRoomName());
+
+ if(GuiActivator.getConfigurationService()
+ .getBoolean(ENABLE_ROOM_AUTO_JOIN_ON_CREATION, false))
+ {
+ chatRoomWrapper.setAutoJoin(true);
+ }
}
String nickName = nicknameField.getText().trim();
diff --git a/src/net/java/sip/communicator/impl/gui/main/chatroomslist/ServerChatRoomsChoiceDialog.java b/src/net/java/sip/communicator/impl/gui/main/chatroomslist/ServerChatRoomsChoiceDialog.java
index 3eeb783..4725a08 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chatroomslist/ServerChatRoomsChoiceDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chatroomslist/ServerChatRoomsChoiceDialog.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,97 +15,97 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.chatroomslist;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.contactlist.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.muc.*;
-
-/**
- * A dialog that lists the existing chat rooms on the server.
- *
- * @author Hristo Terezov
- */
-public class ServerChatRoomsChoiceDialog
- extends OneChoiceInviteDialog
-{
-
- /**
- * Generated serial id.
- */
- private static final long serialVersionUID = 428358553225114162L;
-
- /**
- * The contact source that generates the list of chat rooms.
- */
- private ContactSourceService contactSource;
-
- /**
- * Creates new instance of <tt>ServerChatRoomsChoiceDialog</tt>.
- *
- * @param title the title of the window.
- * @param pps the protocol provider service associated with the list of chat
- * rooms.
- */
- public ServerChatRoomsChoiceDialog(String title,
- ChatRoomProviderWrapper pps)
- {
- super(title);
- contactList.setDefaultFilter(new SearchFilter(contactList));
- contactList.removeAllContactSources();
- contactSource = GuiActivator.getMUCService()
- .getServerChatRoomsContactSourceForProvider(pps);
- contactList.addContactSource(
- contactSource);
-
- setInfoText(GuiActivator.getResources().getI18NString(
- "service.gui.SERVER_CHAT_ROOMS_DIALOG_TEXT"));
-
- contactList.applyDefaultFilter();
- this.setMinimumSize(new Dimension(300, 300));
- addOkButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- UIContact uiContact = getSelectedContact();
-
- if (uiContact != null)
- {
- ChatRoomTableDialog.setChatRoomField(
- uiContact.getDisplayName());
- }
-
- setVisible(false);
- dispose();
- }
- });
- addCancelButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- setVisible(false);
- dispose();
- }
- });
- }
-
- /**
- * Handles provider change.
- *
- * @param provider the provider.
- */
- public void changeProtocolProvider(ChatRoomProviderWrapper provider)
- {
- contactList.removeContactSource(contactSource);
- contactSource = GuiActivator.getMUCService()
- .getServerChatRoomsContactSourceForProvider(provider);
- contactList.addContactSource(contactSource);
- contactList.applyDefaultFilter();
- }
-}
+package net.java.sip.communicator.impl.gui.main.chatroomslist;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.muc.*;
+
+/**
+ * A dialog that lists the existing chat rooms on the server.
+ *
+ * @author Hristo Terezov
+ */
+public class ServerChatRoomsChoiceDialog
+ extends OneChoiceInviteDialog
+{
+
+ /**
+ * Generated serial id.
+ */
+ private static final long serialVersionUID = 428358553225114162L;
+
+ /**
+ * The contact source that generates the list of chat rooms.
+ */
+ private ContactSourceService contactSource;
+
+ /**
+ * Creates new instance of <tt>ServerChatRoomsChoiceDialog</tt>.
+ *
+ * @param title the title of the window.
+ * @param pps the protocol provider service associated with the list of chat
+ * rooms.
+ */
+ public ServerChatRoomsChoiceDialog(String title,
+ ChatRoomProviderWrapper pps)
+ {
+ super(title);
+ contactList.setDefaultFilter(new SearchFilter(contactList));
+ contactList.removeAllContactSources();
+ contactSource = GuiActivator.getMUCService()
+ .getServerChatRoomsContactSourceForProvider(pps);
+ contactList.addContactSource(
+ contactSource);
+
+ setInfoText(GuiActivator.getResources().getI18NString(
+ "service.gui.SERVER_CHAT_ROOMS_DIALOG_TEXT"));
+
+ contactList.applyDefaultFilter();
+ this.setMinimumSize(new Dimension(300, 300));
+ addOkButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ UIContact uiContact = getSelectedContact();
+
+ if (uiContact != null)
+ {
+ ChatRoomTableDialog.setChatRoomField(
+ uiContact.getDisplayName());
+ }
+
+ setVisible(false);
+ dispose();
+ }
+ });
+ addCancelButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ setVisible(false);
+ dispose();
+ }
+ });
+ }
+
+ /**
+ * Handles provider change.
+ *
+ * @param provider the provider.
+ */
+ public void changeProtocolProvider(ChatRoomProviderWrapper provider)
+ {
+ contactList.removeContactSource(contactSource);
+ contactSource = GuiActivator.getMUCService()
+ .getServerChatRoomsContactSourceForProvider(provider);
+ contactList.addContactSource(contactSource);
+ contactList.applyDefaultFilter();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/AddContactDialog.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/AddContactDialog.java
index 856ae5b..8d43c7f 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/AddContactDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/AddContactDialog.java
@@ -21,9 +21,14 @@ import java.awt.*;
import java.awt.Container;
import java.awt.event.*;
import java.util.*;
+import java.util.List;
import javax.swing.*;
+import javax.swing.border.*;
import javax.swing.event.*;
+import javax.swing.text.*;
+
+import org.jitsi.util.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.addgroup.*;
@@ -163,31 +168,100 @@ public class AddContactDialog
}
/**
+ * Adds a faint gray prompt to the provided text field that
+ * will vanish as soon as text is entered into the field.
+ */
+ private void addPrompt(JTextField field, String text)
+ {
+ final JLabel prompt = new JLabel(text);
+
+ // Give prompt a foreground color like the original
+ // text field, but with half transparency.
+ final Color fg = field.getForeground();
+ final Color color = new Color(
+ fg.getRed(), fg.getGreen(), fg.getBlue(), 128);
+
+ // Mimic properties of given text field
+ prompt.setFont(field.getFont());
+ prompt.setForeground(color);
+ prompt.setBorder(new EmptyBorder(field.getInsets()));
+ prompt.setHorizontalAlignment(JLabel.LEADING);
+
+ // Add handler to hide prompt when text is entered
+ final Document doc = field.getDocument();
+ doc.addDocumentListener( new DocumentListener() {
+ public void insertUpdate(DocumentEvent e)
+ {
+ prompt.setVisible(doc.getLength() == 0);
+ }
+
+ public void removeUpdate(DocumentEvent e)
+ {
+ prompt.setVisible(doc.getLength() == 0);
+ }
+
+ public void changedUpdate(DocumentEvent e) {}
+ });
+
+ // Add prompt to text field
+ field.setLayout( new BorderLayout() );
+ field.add(prompt);
+ }
+
+ /**
* Initializes the dialog.
*/
private void init()
{
+ // Get tool tip text for primary controls
+ final String displayNameInfo =
+ GuiActivator.getResources().getI18NString(
+ "service.gui.DISPLAY_NAME_INFO");
+ final String contactInfo =
+ GuiActivator.getResources().getI18NString(
+ "service.gui.CONTACT_NAME_INFO");
+ final String accountInfo =
+ GuiActivator.getResources().getI18NString(
+ "service.gui.SELECT_ACCOUNT_INFO");
+ final String groupInfo =
+ GuiActivator.getResources().getI18NString(
+ "service.gui.SELECT_GROUP_INFO");
+
+ // Initialize controls
this.accountLabel = new JLabel(
GuiActivator.getResources().getI18NString(
"service.gui.SELECT_ACCOUNT") + ": ");
+ this.accountLabel.setToolTipText(accountInfo);
this.accountCombo = new JComboBox();
-
- this.groupLabel = new JLabel(
- GuiActivator.getResources().getI18NString(
- "service.gui.SELECT_GROUP") + ": ");
+ this.accountCombo.setToolTipText(accountInfo);
this.contactAddressLabel = new JLabel(
GuiActivator.getResources().getI18NString(
"service.gui.CONTACT_NAME") + ": ");
+ this.contactAddressLabel.setToolTipText(contactInfo);
this.displayNameLabel = new JLabel(
GuiActivator.getResources().getI18NString(
"service.gui.DISPLAY_NAME") + ": ");
+ this.displayNameLabel.setToolTipText(displayNameInfo);
this.contactAddressField = new JTextField();
+ this.contactAddressField.setToolTipText(contactInfo);
+ addPrompt(this.contactAddressField,
+ GuiActivator.getResources().getI18NString(
+ "service.gui.CONTACT_NAME_PROMPT"));
this.displayNameField = new JTextField();
+ this.displayNameField.setToolTipText(displayNameInfo);
+ addPrompt(this.displayNameField,
+ GuiActivator.getResources().getI18NString(
+ "service.gui.DISPLAY_NAME_PROMPT"));
+
+ this.groupLabel = new JLabel(
+ GuiActivator.getResources().getI18NString(
+ "service.gui.SELECT_GROUP") + ": ");
+ this.groupLabel.setToolTipText(groupInfo);
this.addButton = new JButton(
GuiActivator.getResources().getI18NString("service.gui.ADD"));
@@ -198,6 +272,7 @@ public class AddContactDialog
this.imageLabel = new JLabel();
this.groupCombo = createGroupCombo(this);
+ this.groupCombo.setToolTipText(groupInfo);
if(metaContact != null)
{
@@ -225,15 +300,15 @@ public class AddContactDialog
fieldsPanel.add(accountCombo);
}
- labelsPanel.add(groupLabel);
- fieldsPanel.add(groupCombo);
-
labelsPanel.add(contactAddressLabel);
fieldsPanel.add(contactAddressField);
labelsPanel.add(displayNameLabel);
fieldsPanel.add(displayNameField);
+ labelsPanel.add(groupLabel);
+ fieldsPanel.add(groupCombo);
+
contactAddressField.getDocument().addDocumentListener(
new DocumentListener()
{
@@ -499,6 +574,26 @@ public class AddContactDialog
final String contactAddress = contactAddressField.getText().trim();
final String displayName = displayNameField.getText();
+ List<String> validationResult = new ArrayList<>(2);
+ if (!protocolProvider.validateContactAddress(contactAddress,
+ validationResult))
+ {
+ new ErrorDialog(GuiActivator.getUIService().getMainFrame(),
+ GuiActivator.getResources()
+ .getI18NString("service.gui.ADD_CONTACT_ERROR_TITLE"),
+ validationResult.get(0), ErrorDialog.WARNING).showDialog();
+ if (validationResult.size() >= 2)
+ {
+ contactAddressField.setText(validationResult.get(1));
+ if (StringUtils.isNullOrEmpty(displayName, true))
+ {
+ displayNameField.setText(contactAddress);
+ }
+ }
+
+ return;
+ }
+
if (!protocolProvider.isRegistered())
{
new ErrorDialog(
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactInfoDialog.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactInfoDialog.java
deleted file mode 100644
index 325be90..0000000
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactInfoDialog.java
+++ /dev/null
@@ -1,131 +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.gui.main.contactlist;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.customcontrols.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.contactlist.*;
-
-/**
- * The <tt>ContactInfoPanel</tt> is a popup dialog containing the contact
- * detailed info.
- *
- * @author Yana Stamcheva
- */
-public class ContactInfoDialog
- extends SIPCommDialog
- implements WindowFocusListener
-{
-
- private JPanel protocolsPanel = new TransparentPanel(new GridLayout(0, 1));
-
- private TransparentBackground bg;
-
- /**
- * Creates an instance of the <tt>ContactInfoPanel</tt>.
- *
- * @param owner The frame owner of this dialog.
- * @param contactItem The <tt>MetaContact</tt> for the info.
- */
- public ContactInfoDialog(Frame owner, MetaContact contactItem)
- {
- super(owner);
-
- this.setUndecorated(true);
-
- this.setModal(true);
-
- // Create the transparent background component
- this.bg = new TransparentBackground(this);
-
- this.bg.setLayout(new BorderLayout());
-
- this.bg.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
-
- this.getContentPane().setLayout(new BorderLayout());
-
- this.init();
-
- this.getContentPane().add(bg, BorderLayout.CENTER);
-
- this.pack();
-
- this.setSize(140, 50);
-
- this.addWindowFocusListener(this);
- }
-
- /**
- * Initializes the <tt>ContactInfoPanel</tt>.
- */
- private void init()
- {
- /*
- * String[] protocolList = this.contactItem.getC();
- *
- * if(protocolsPanel.getComponentCount() == 0){ for(int i = 0; i <
- * protocolList.length; i ++){
- *
- * JLabel protocolLabel = new JLabel(protocolList[i], new
- * ImageIcon(Constants.getProtocolIcon(protocolList[i])), JLabel.LEFT);
- *
- * this.protocolsPanel.add(protocolLabel); } }
- *
- * this.bg.add(protocolsPanel, BorderLayout.CENTER);
- */
- }
-
- /**
- * Returns the panel containing all contact protocols' information.
- *
- * @return the panel containing all contact protocols' information.
- */
- public JPanel getProtocolsPanel()
- {
- return protocolsPanel;
- }
-
- public void windowGainedFocus(WindowEvent e)
- {
-
- }
-
- public void windowLostFocus(WindowEvent e)
- {
- close(false);
- }
-
- public void setPopupLocation(int x, int y)
- {
- this.setLocation(x, y);
-
- this.bg.updateBackground(x, y);
- }
-
- @Override
- protected void close(boolean isEscaped)
- {
- this.setVisible(false);
- this.dispose();
- }
-}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java
index bfe2fd3..ae0e3b0 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java
@@ -1078,7 +1078,7 @@ public class ContactListTreeCellRenderer
UIContactDetail desktopContact
= uiContact.getDefaultContactDetail(
- OperationSetDesktopStreaming.class);
+ OperationSetDesktopSharingServer.class);
if (desktopContact != null
|| (contactPhoneUtil != null
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java
index cf7108f..011b00a 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,353 +15,353 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.contactlist;
-
-import java.util.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
-import net.java.sip.communicator.service.contactlist.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.gui.event.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * The <tt>PresenceFilter</tt> is used to filter offline contacts from the
- * contact list.
- *
- * @author Yana Stamcheva
- */
-public class PresenceFilter
- implements ContactListFilter
-{
- /**
- * The <tt>Logger</tt> used by the <tt>PresenceFilter</tt> class and its
- * instances to print debugging information.
- */
- private static final Logger logger = Logger.getLogger(PresenceFilter.class);
-
- /**
- * Indicates if this presence filter shows or hides the offline contacts.
- */
- private boolean isShowOffline;
-
- /**
- * The initial result count below which we insert all filter results
- * directly to the contact list without firing events.
- */
- private static final int INITIAL_CONTACT_COUNT = 30;
-
- /**
- * Creates an instance of <tt>PresenceFilter</tt>.
- */
- public PresenceFilter()
- {
- isShowOffline = ConfigurationUtils.isShowOffline();
- }
-
- /**
- * Applies this filter. This filter is applied over the
- * <tt>MetaContactListService</tt>.
- *
- * @param filterQuery the query which keeps track of the filtering results
- */
- public void applyFilter(FilterQuery filterQuery)
- {
- // Create the query that will track filtering.
- MetaContactQuery query = new MetaContactQuery();
-
- // Add this query to the filterQuery.
- filterQuery.addContactQuery(query);
-
- TreeContactList contactList = GuiActivator.getContactList();
-
- Collection<UIContactSource> uiContactSourceCollection
- = contactList.getContactSources(
- ContactSourceService.CONTACT_LIST_TYPE);
-
- Iterator<UIContactSource> filterSources
- = uiContactSourceCollection.iterator();
- int maxIndex = 0;
- while (filterSources.hasNext())
- {
- UIContactSource filterSource = filterSources.next();
- int currIx = filterSource.getContactSourceService().getIndex();
- if(maxIndex < currIx)
- maxIndex = currIx;
- }
-
- contactList.getMetaContactListSource().setIndex(maxIndex + 1);
-
- filterSources = uiContactSourceCollection.iterator();
- while (filterSources.hasNext())
- {
- UIContactSource filterSource = filterSources.next();
-
- filterSource.setContactSourceIndex(
- filterSource.getContactSourceService().getIndex());
-
- ContactSourceService sourceService
- = filterSource.getContactSourceService();
-
- ContactQuery contactQuery
- = sourceService.createContactQuery(null);
-
- if(contactQuery == null)
- continue;
-
- // Add this query to the filterQuery.
- filterQuery.addContactQuery(contactQuery);
-
- contactQuery.addContactQueryListener(contactList);
-
- contactQuery.start();
- }
-
- // Closes this filter to indicate that we finished adding queries to it.
- filterQuery.close();
-
- query.addContactQueryListener(GuiActivator.getContactList());
- int resultCount = 0;
-
- addMatching(GuiActivator.getContactListService().getRoot(),
- query,
- resultCount);
-
- query.fireQueryEvent(
- query.isCanceled()
- ? MetaContactQueryStatusEvent.QUERY_CANCELED
- : MetaContactQueryStatusEvent.QUERY_COMPLETED);
- }
-
- /**
- * Indicates if the given <tt>uiContact</tt> is matching this filter.
- *
- * @param uiContact the <tt>UIContact</tt> to check
- * @return <tt>true</tt> if the given <tt>uiContact</tt> is matching
- * this filter, otherwise returns <tt>false</tt>
- */
- public boolean isMatching(UIContact uiContact)
- {
- Object descriptor = uiContact.getDescriptor();
-
- if (descriptor instanceof MetaContact)
- return isMatching((MetaContact) descriptor);
- else if (descriptor instanceof SourceContact)
- return isMatching((SourceContact)descriptor);
- else
- return false;
- }
-
- /**
- * Indicates if the given <tt>uiGroup</tt> is matching this filter.
- *
- * @param uiGroup the <tt>UIGroup</tt> to check
- * @return <tt>true</tt> if the given <tt>uiGroup</tt> is matching
- * this filter, otherwise returns <tt>false</tt>
- */
- public boolean isMatching(UIGroup uiGroup)
- {
- Object descriptor = uiGroup.getDescriptor();
-
- if (descriptor instanceof MetaContactGroup)
- return isMatching((MetaContactGroup) descriptor);
- else
- return false;
- }
-
- /**
- * Sets the show offline property.
- *
- * @param isShowOffline indicates if offline contacts are shown
- */
- public void setShowOffline(boolean isShowOffline)
- {
- this.isShowOffline = isShowOffline;
-
- ConfigurationUtils.setShowOffline(isShowOffline);
- }
-
- /**
- * Returns <tt>true</tt> if offline contacts are shown, otherwise returns
- * <tt>false</tt>.
- *
- * @return <tt>true</tt> if offline contacts are shown, otherwise returns
- * <tt>false</tt>
- */
- public boolean isShowOffline()
- {
- return isShowOffline;
- }
-
- /**
- * Returns <tt>true</tt> if offline contacts are shown or if the given
- * <tt>MetaContact</tt> is online, otherwise returns false.
- *
- * @param metaContact the <tt>MetaContact</tt> to check
- * @return <tt>true</tt> if the given <tt>MetaContact</tt> is matching this
- * filter
- */
- public boolean isMatching(MetaContact metaContact)
- {
- return isShowOffline || isContactOnline(metaContact);
- }
-
- /**
- * Returns <tt>true</tt> if offline contacts are shown or if the given
- * <tt>MetaContact</tt> is online, otherwise returns false.
- *
- * @param contact the <tt>MetaContact</tt> to check
- * @return <tt>true</tt> if the given <tt>MetaContact</tt> is matching this
- * filter
- */
- public boolean isMatching(SourceContact contact)
- {
- // make sure we always show chat rooms and recent messages
- return
- isShowOffline
- || contact.getPresenceStatus().isOnline()
- || contact.getContactSource().getType()
- == ContactSourceService.CONTACT_LIST_TYPE;
- }
-
- /**
- * Returns <tt>true</tt> if offline contacts are shown or if the given
- * <tt>MetaContactGroup</tt> contains online contacts.
- *
- * @param metaGroup the <tt>MetaContactGroup</tt> to check
- * @return <tt>true</tt> if the given <tt>MetaContactGroup</tt> is matching
- * this filter
- */
- private boolean isMatching(MetaContactGroup metaGroup)
- {
- return
- isShowOffline
- || (metaGroup.countOnlineChildContacts() > 0)
- || MetaContactListSource.isNewGroup(metaGroup);
- }
-
- /**
- * Returns <tt>true</tt> if the given meta contact is online, <tt>false</tt>
- * otherwise.
- *
- * @param contact the meta contact
- * @return <tt>true</tt> if the given meta contact is online, <tt>false</tt>
- * otherwise
- */
- private boolean isContactOnline(MetaContact contact)
- {
- // If for some reason the default contact is null we return false.
- Contact defaultContact = contact.getDefaultContact();
- if(defaultContact == null)
- return false;
-
- // Lays on the fact that the default contact is the most connected.
- return defaultContact.getPresenceStatus().getStatus()
- >= PresenceStatus.ONLINE_THRESHOLD;
- }
-
- /**
- * 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);
- }
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.contactlist;
+
+import java.util.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.service.contactlist.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.gui.event.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * The <tt>PresenceFilter</tt> is used to filter offline contacts from the
+ * contact list.
+ *
+ * @author Yana Stamcheva
+ */
+public class PresenceFilter
+ implements ContactListFilter
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>PresenceFilter</tt> class and its
+ * instances to print debugging information.
+ */
+ private static final Logger logger = Logger.getLogger(PresenceFilter.class);
+
+ /**
+ * Indicates if this presence filter shows or hides the offline contacts.
+ */
+ private boolean isShowOffline;
+
+ /**
+ * The initial result count below which we insert all filter results
+ * directly to the contact list without firing events.
+ */
+ private static final int INITIAL_CONTACT_COUNT = 30;
+
+ /**
+ * Creates an instance of <tt>PresenceFilter</tt>.
+ */
+ public PresenceFilter()
+ {
+ isShowOffline = ConfigurationUtils.isShowOffline();
+ }
+
+ /**
+ * Applies this filter. This filter is applied over the
+ * <tt>MetaContactListService</tt>.
+ *
+ * @param filterQuery the query which keeps track of the filtering results
+ */
+ public void applyFilter(FilterQuery filterQuery)
+ {
+ // Create the query that will track filtering.
+ MetaContactQuery query = new MetaContactQuery();
+
+ // Add this query to the filterQuery.
+ filterQuery.addContactQuery(query);
+
+ TreeContactList contactList = GuiActivator.getContactList();
+
+ Collection<UIContactSource> uiContactSourceCollection
+ = contactList.getContactSources(
+ ContactSourceService.CONTACT_LIST_TYPE);
+
+ Iterator<UIContactSource> filterSources
+ = uiContactSourceCollection.iterator();
+ int maxIndex = 0;
+ while (filterSources.hasNext())
+ {
+ UIContactSource filterSource = filterSources.next();
+ int currIx = filterSource.getContactSourceService().getIndex();
+ if(maxIndex < currIx)
+ maxIndex = currIx;
+ }
+
+ contactList.getMetaContactListSource().setIndex(maxIndex + 1);
+
+ filterSources = uiContactSourceCollection.iterator();
+ while (filterSources.hasNext())
+ {
+ UIContactSource filterSource = filterSources.next();
+
+ filterSource.setContactSourceIndex(
+ filterSource.getContactSourceService().getIndex());
+
+ ContactSourceService sourceService
+ = filterSource.getContactSourceService();
+
+ ContactQuery contactQuery
+ = sourceService.createContactQuery(null);
+
+ if(contactQuery == null)
+ continue;
+
+ // Add this query to the filterQuery.
+ filterQuery.addContactQuery(contactQuery);
+
+ contactQuery.addContactQueryListener(contactList);
+
+ contactQuery.start();
+ }
+
+ // Closes this filter to indicate that we finished adding queries to it.
+ filterQuery.close();
+
+ query.addContactQueryListener(GuiActivator.getContactList());
+ int resultCount = 0;
+
+ addMatching(GuiActivator.getContactListService().getRoot(),
+ query,
+ resultCount);
+
+ query.fireQueryEvent(
+ query.isCanceled()
+ ? MetaContactQueryStatusEvent.QUERY_CANCELED
+ : MetaContactQueryStatusEvent.QUERY_COMPLETED);
+ }
+
+ /**
+ * Indicates if the given <tt>uiContact</tt> is matching this filter.
+ *
+ * @param uiContact the <tt>UIContact</tt> to check
+ * @return <tt>true</tt> if the given <tt>uiContact</tt> is matching
+ * this filter, otherwise returns <tt>false</tt>
+ */
+ public boolean isMatching(UIContact uiContact)
+ {
+ Object descriptor = uiContact.getDescriptor();
+
+ if (descriptor instanceof MetaContact)
+ return isMatching((MetaContact) descriptor);
+ else if (descriptor instanceof SourceContact)
+ return isMatching((SourceContact)descriptor);
+ else
+ return false;
+ }
+
+ /**
+ * Indicates if the given <tt>uiGroup</tt> is matching this filter.
+ *
+ * @param uiGroup the <tt>UIGroup</tt> to check
+ * @return <tt>true</tt> if the given <tt>uiGroup</tt> is matching
+ * this filter, otherwise returns <tt>false</tt>
+ */
+ public boolean isMatching(UIGroup uiGroup)
+ {
+ Object descriptor = uiGroup.getDescriptor();
+
+ if (descriptor instanceof MetaContactGroup)
+ return isMatching((MetaContactGroup) descriptor);
+ else
+ return false;
+ }
+
+ /**
+ * Sets the show offline property.
+ *
+ * @param isShowOffline indicates if offline contacts are shown
+ */
+ public void setShowOffline(boolean isShowOffline)
+ {
+ this.isShowOffline = isShowOffline;
+
+ ConfigurationUtils.setShowOffline(isShowOffline);
+ }
+
+ /**
+ * Returns <tt>true</tt> if offline contacts are shown, otherwise returns
+ * <tt>false</tt>.
+ *
+ * @return <tt>true</tt> if offline contacts are shown, otherwise returns
+ * <tt>false</tt>
+ */
+ public boolean isShowOffline()
+ {
+ return isShowOffline;
+ }
+
+ /**
+ * Returns <tt>true</tt> if offline contacts are shown or if the given
+ * <tt>MetaContact</tt> is online, otherwise returns false.
+ *
+ * @param metaContact the <tt>MetaContact</tt> to check
+ * @return <tt>true</tt> if the given <tt>MetaContact</tt> is matching this
+ * filter
+ */
+ public boolean isMatching(MetaContact metaContact)
+ {
+ return isShowOffline || isContactOnline(metaContact);
+ }
+
+ /**
+ * Returns <tt>true</tt> if offline contacts are shown or if the given
+ * <tt>MetaContact</tt> is online, otherwise returns false.
+ *
+ * @param contact the <tt>MetaContact</tt> to check
+ * @return <tt>true</tt> if the given <tt>MetaContact</tt> is matching this
+ * filter
+ */
+ public boolean isMatching(SourceContact contact)
+ {
+ // make sure we always show chat rooms and recent messages
+ return
+ isShowOffline
+ || contact.getPresenceStatus().isOnline()
+ || contact.getContactSource().getType()
+ == ContactSourceService.CONTACT_LIST_TYPE;
+ }
+
+ /**
+ * Returns <tt>true</tt> if offline contacts are shown or if the given
+ * <tt>MetaContactGroup</tt> contains online contacts.
+ *
+ * @param metaGroup the <tt>MetaContactGroup</tt> to check
+ * @return <tt>true</tt> if the given <tt>MetaContactGroup</tt> is matching
+ * this filter
+ */
+ private boolean isMatching(MetaContactGroup metaGroup)
+ {
+ return
+ isShowOffline
+ || (metaGroup.countOnlineChildContacts() > 0)
+ || MetaContactListSource.isNewGroup(metaGroup);
+ }
+
+ /**
+ * Returns <tt>true</tt> if the given meta contact is online, <tt>false</tt>
+ * otherwise.
+ *
+ * @param contact the meta contact
+ * @return <tt>true</tt> if the given meta contact is online, <tt>false</tt>
+ * otherwise
+ */
+ private boolean isContactOnline(MetaContact contact)
+ {
+ // If for some reason the default contact is null we return false.
+ Contact defaultContact = contact.getDefaultContact();
+ if(defaultContact == null)
+ return false;
+
+ // Lays on the fact that the default contact is the most connected.
+ return defaultContact.getPresenceStatus().getStatus()
+ >= PresenceStatus.ONLINE_THRESHOLD;
+ }
+
+ /**
+ * 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);
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java
index 837d369..4735892 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java
@@ -270,14 +270,13 @@ public class SearchFilter
*/
private boolean isMatching(String text)
{
- if (filterPattern != null)
- return filterPattern.matcher(text).find();
+ if (filterPattern != null && filterPattern.matcher(text).find())
+ return true;
if(isSearchingPhoneNumber && this.filterString != null)
return GuiActivator.getPhoneNumberI18nService()
.phoneNumbersMatch(this.filterString, text);
return true;
-
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java
index a916b2b..119e000 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java
@@ -565,10 +565,6 @@ public class TreeContactList
if (isActive)
{
activeContacts.add(contactNode);
-// SystrayService stray = GuiActivator.getSystrayService();
-//
-// if (stray != null)
-// stray.setSystrayIcon(SystrayService.ENVELOPE_IMG_TYPE);
}
else
activeContacts.remove(contactNode);
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java
index 72bec25..1419416 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java
@@ -573,7 +573,7 @@ public class SourceUIContact
}
else
{
- labelText = contactDetail.getDetail();
+ labelText = contactDetail.getDisplayName();
}
jLabels[i] = new JLabel(filterAddressDisplay(labelText));
@@ -709,11 +709,9 @@ public class SourceUIContact
case AIM:
case ICQ:
case Jabber:
- case MSN:
case Yahoo:
case Skype:
case GoogleTalk:
- case Facebook:
label = subCategory.value();
break;
default:
diff --git a/src/net/java/sip/communicator/impl/gui/main/menus/MacOSXPreferencesRegistration.java b/src/net/java/sip/communicator/impl/gui/main/menus/MacOSXPreferencesRegistration.java
index cfb5420..5a75ff1 100644
--- a/src/net/java/sip/communicator/impl/gui/main/menus/MacOSXPreferencesRegistration.java
+++ b/src/net/java/sip/communicator/impl/gui/main/menus/MacOSXPreferencesRegistration.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,30 +15,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.menus;
-
-import com.apple.eawt.*;
-
-/**
- * @author Lubomir Marinov
- */
-public final class MacOSXPreferencesRegistration
-{
- public static boolean run(final Object userData)
- {
- Application application = Application.getApplication();
- if (application != null)
- {
- application.setPreferencesHandler(new PreferencesHandler()
- {
- public void handlePreferences(
- AppEvent.PreferencesEvent preferencesEvent)
- {
- ((ToolsMenu) userData).configActionPerformed();
- }
- });
- return true;
- }
- return false;
- }
-}
+package net.java.sip.communicator.impl.gui.main.menus;
+
+import com.apple.eawt.*;
+
+/**
+ * @author Lubomir Marinov
+ */
+public final class MacOSXPreferencesRegistration
+{
+ public static boolean run(final Object userData)
+ {
+ Application application = Application.getApplication();
+ if (application != null)
+ {
+ application.setPreferencesHandler(new PreferencesHandler()
+ {
+ public void handlePreferences(
+ AppEvent.PreferencesEvent preferencesEvent)
+ {
+ ((ToolsMenu) userData).configActionPerformed();
+ }
+ });
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/menus/MacOSXQuitRegistration.java b/src/net/java/sip/communicator/impl/gui/main/menus/MacOSXQuitRegistration.java
index 594623b..4b786b2 100644
--- a/src/net/java/sip/communicator/impl/gui/main/menus/MacOSXQuitRegistration.java
+++ b/src/net/java/sip/communicator/impl/gui/main/menus/MacOSXQuitRegistration.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,68 +15,68 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.menus;
-
-import com.apple.eawt.*;
-
-/**
- * @author Lubomir Marinov
- */
-public final class MacOSXQuitRegistration
-{
- public static boolean run(final Object userData)
- {
- Application application = Application.getApplication();
- if (application != null)
- {
- application.setQuitHandler(new QuitHandler()
- {
- public void handleQuitRequestWith(AppEvent.QuitEvent quitEvent,
- final QuitResponse quitResponse)
- {
- ((FileMenu) userData).closeActionPerformed();
-
- /*
- * Tell Mac OS X that it shouldn't terminate the
- * application. We've already initiated the quit and we'll
- * eventually complete it i.e. we'll honor the request of
- * Mac OS X to quit.
- *
- * (2011-06-10) Changed to true, we tell that quit is handled
- * as otherwise will stop OS from logout or shutdown and
- * a notification will be shown to user to inform about it.
- *
- * (2011-07-12) Wait before answering to the OS or we will
- * end too quickly. 15sec is the time our shutdown timer
- * waits before force the shutdown.
- */
-
- synchronized(this)
- {
- try
- {
- wait(15000);
- }catch (InterruptedException ex){}
- }
-
- /**
- * Free the event dispatch thread before performing the
- * quit (System.exit), shutdown timer may also has started
- * the quit and is waiting to free the threads which
- * we may be blocking.
- */
- new Thread(new Runnable()
- {
- public void run()
- {
- quitResponse.performQuit();
- }
- }).start();
- }
- });
-
- return true;
- }
- return false;
- }
-}
+package net.java.sip.communicator.impl.gui.main.menus;
+
+import com.apple.eawt.*;
+
+/**
+ * @author Lubomir Marinov
+ */
+public final class MacOSXQuitRegistration
+{
+ public static boolean run(final Object userData)
+ {
+ Application application = Application.getApplication();
+ if (application != null)
+ {
+ application.setQuitHandler(new QuitHandler()
+ {
+ public void handleQuitRequestWith(AppEvent.QuitEvent quitEvent,
+ final QuitResponse quitResponse)
+ {
+ ((FileMenu) userData).closeActionPerformed();
+
+ /*
+ * Tell Mac OS X that it shouldn't terminate the
+ * application. We've already initiated the quit and we'll
+ * eventually complete it i.e. we'll honor the request of
+ * Mac OS X to quit.
+ *
+ * (2011-06-10) Changed to true, we tell that quit is handled
+ * as otherwise will stop OS from logout or shutdown and
+ * a notification will be shown to user to inform about it.
+ *
+ * (2011-07-12) Wait before answering to the OS or we will
+ * end too quickly. 15sec is the time our shutdown timer
+ * waits before force the shutdown.
+ */
+
+ synchronized(this)
+ {
+ try
+ {
+ wait(15000);
+ }catch (InterruptedException ex){}
+ }
+
+ /**
+ * Free the event dispatch thread before performing the
+ * quit (System.exit), shutdown timer may also has started
+ * the quit and is waiting to free the threads which
+ * we may be blocking.
+ */
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ quitResponse.performQuit();
+ }
+ }).start();
+ }
+ });
+
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
index 1861c4a..2795ef9 100644
--- a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
+++ b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
@@ -72,12 +72,12 @@ Import-Package: com.apple.eawt,
org.jitsi.service.neomedia.event,
org.jitsi.service.neomedia.format,
org.jitsi.service.neomedia.recording,
+ org.jitsi.service.neomedia.stats,
org.jitsi.service.resources,
org.jitsi.util,
org.jitsi.util.event,
org.jitsi.util.swing,
org.osgi.framework,
- say.swing,
net.java.sip.communicator.service.credentialsstorage,
net.java.sip.communicator.service.muc,
net.java.sip.communicator.plugin.desktoputil.chat,
diff --git a/src/net/java/sip/communicator/impl/gui/utils/PluginContainer.java b/src/net/java/sip/communicator/impl/gui/utils/PluginContainer.java
index e85c51c..d24855f 100644
--- a/src/net/java/sip/communicator/impl/gui/utils/PluginContainer.java
+++ b/src/net/java/sip/communicator/impl/gui/utils/PluginContainer.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,318 +15,318 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.utils;
-
-import java.awt.*;
-import java.util.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.event.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.gui.Container;
-import net.java.sip.communicator.util.*;
-
-import org.osgi.framework.*;
-
-/**
- * Provides capabilities to a specific <code>JComponent</code> to contain
- * <code>PluginComponent</code>s, track when they are added and removed.
- *
- * @author Lyubomir Marinov
- */
-public class PluginContainer
- implements PluginComponentListener
-{
- /**
- * The <tt>Logger</tt> used by the <tt>PluginContainer</tt> class and its
- * instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(PluginContainer.class);
-
- /**
- * The <code>JComponent</code> which contains the components of the
- * <code>PluginComponent</code>s managed by this instance.
- */
- private final JComponent container;
-
- /**
- * The container id of the <code>PluginComponent</code> managed by this
- * instance.
- */
- private final Container containerId;
-
- /**
- * The list of <code>PluginComponent</code> instances which have their
- * components added to this <code>PluginContainer</code>.
- */
- private final java.util.List<PluginComponent> pluginComponents
- = new LinkedList<PluginComponent>();
-
- /**
- * Initializes a new <code>PluginContainer</code> instance which is to
- * provide capabilities to a specific <code>JComponent</code> container with
- * a specific <code>Container</code> id to contain
- * <code>PluginComponent</code> and track when they are added and removed.
- *
- * @param container
- * the <code>JComponent</code> container the new instance is to
- * provide its capabilities to
- * @param containerId
- * the <code>Container</code> id of the specified
- * <code>container</code>
- */
- public PluginContainer(JComponent container, Container containerId)
- {
- this.container = container;
- this.containerId = containerId;
-
- initPluginComponents();
- }
-
- /**
- * Adds a specific <tt>Component</tt> to a specific <tt>JComponent</tt>
- * container. Allows extenders to apply custom logic to the exact placement
- * of the specified <tt>Component</tt> in the specified container.
- *
- * @param component the <tt>Component</tt> to be added to the specified
- * <tt>JComponent</tt> container
- * @param container the <tt>JComponent</tt> container to add the specified
- * <tt>Component</tt> to
- * @param preferredIndex the index at which <tt>component</tt> is to be
- * added to <tt>container</tt> if possible or <tt>-1</tt> if there is no
- * preference with respect to the index in question
- */
- protected void addComponentToContainer(
- Component component,
- JComponent container,
- int preferredIndex)
- {
- if ((0 <= preferredIndex)
- && (preferredIndex < getComponentCount(container)))
- container.add(component, preferredIndex);
- else
- container.add(component);
- }
-
- /**
- * Adds the component of a specific <tt>PluginComponent</tt> to the
- * associated <tt>Container</tt>.
- *
- * @param factory the <tt>PluginComponentFactory</tt> which is to have its
- * component added to the <tt>Container</tt> associated with this
- * <tt>PluginContainer</tt>
- */
- private synchronized void addPluginComponent(PluginComponentFactory factory)
- {
- PluginComponent c =
- factory.getPluginComponentInstance(PluginContainer.this);
-
- if (logger.isInfoEnabled())
- logger.info("Will add plugin component: " + c);
-
- /*
- * Try to respect positionIndex of PluginComponent to some extent:
- * PluginComponents with positionIndex equal to 0 go at the beginning,
- * these with positionIndex equal to -1 follow them and then go these
- * with positionIndex greater than 0.
- */
- int cIndex = factory.getPositionIndex();
- int index = -1;
- int i = 0;
-
- for (PluginComponent pluginComponent : pluginComponents)
- {
- if (pluginComponent.equals(c))
- return;
-
- if (-1 == index)
- {
- int pluginComponentIndex = factory.getPositionIndex();
-
- if ((0 == cIndex) || (-1 == cIndex))
- {
- if ((0 != pluginComponentIndex)
- && (cIndex != pluginComponentIndex))
- index = i;
- }
- else if (cIndex < pluginComponentIndex)
- index = i;
- }
-
- i++;
- }
-
- int pluginComponentCount = pluginComponents.size();
-
- if (-1 == index)
- index = pluginComponents.size();
-
- /*
- * The container may have added Components of its own apart from the
- * ones this PluginContainer has added to it. Since the common case for
- * the additional Components is to have them appear at the beginning,
- * adjust the index so it gets correct in the common case.
- */
- int containerComponentCount = getComponentCount(container);
-
- addComponentToContainer(
- (Component) c.getComponent(),
- container,
- (containerComponentCount > pluginComponentCount)
- ? (index + (containerComponentCount - pluginComponentCount))
- : index);
- pluginComponents.add(index, c);
-
- container.revalidate();
- container.repaint();
- }
-
- /**
- * Runs clean-up for associated resources which need explicit disposal (e.g.
- * listeners keeping this instance alive because they were added to the
- * model which operationally outlives this instance).
- */
- public void dispose()
- {
- GuiActivator.getUIService().removePluginComponentListener(this);
-
- /*
- * Explicitly remove the components of the PluginComponent instances
- * because the latter are registered with OSGi and are thus global.
- */
- synchronized (this)
- {
- for (PluginComponent pluginComponent : pluginComponents)
- container.remove((Component) pluginComponent.getComponent());
- pluginComponents.clear();
- }
- }
-
- /**
- * Gets the number of <tt>Component</tt>s in a specific <tt>JComponent</tt>
- * container. For example, returns the result of
- * <tt>getMenuComponentCount()</tt> if <tt>container</tt> is an instance of
- * <tt>JMenu</tt>.
- *
- * @param container the <tt>JComponent</tt> container to get the number of
- * <tt>Component</tt>s of
- * @return the number of <tt>Component</tt>s in the specified
- * <tt>container</tt>
- */
- protected int getComponentCount(JComponent container)
- {
- return
- (container instanceof JMenu)
- ? ((JMenu) container).getMenuComponentCount()
- : container.getComponentCount();
- }
-
- /**
- * Gets the <tt>PluginComponent</tt>s of this <tt>PluginContainer</tt>.
- *
- * @return an <tt>Iterable</tt> over the <tt>PluginComponent</tt>s of this
- * <tt>PluginContainer</tt>
- */
- public Iterable<PluginComponent> getPluginComponents()
- {
- return pluginComponents;
- }
-
- /**
- * Adds the <tt>Component</tt>s of the <tt>PluginComponent</tt>s registered
- * in the OSGi <tt>BundleContext</tt> in the associated <tt>Container</tt>.
- */
- private void initPluginComponents()
- {
- GuiActivator.getUIService().addPluginComponentListener(this);
-
- // Look for PluginComponents registered in the OSGi BundleContext.
- ServiceReference[] serRefs = null;
-
- try
- {
- serRefs
- = GuiActivator
- .bundleContext
- .getServiceReferences(
- PluginComponentFactory.class.getName(),
- "("
- + Container.CONTAINER_ID
- + "="
- + containerId.getID()
- + ")");
- }
- catch (InvalidSyntaxException exc)
- {
- logger.error("Could not obtain plugin reference.", exc);
- }
-
- if (serRefs != null)
- {
- for (ServiceReference serRef : serRefs)
- {
- PluginComponentFactory factory
- = (PluginComponentFactory)
- GuiActivator.bundleContext.getService(serRef);
-
- addPluginComponent(factory);
- }
- }
- }
-
- /**
- * Implements
- * {@link PluginComponentListener#pluginComponentAdded(PluginComponentEvent)}.
- *
- * @param event a <tt>PluginComponentEvent</tt> which specifies the
- * <tt>PluginComponent</tt> which has been added
- */
- public void pluginComponentAdded(PluginComponentEvent event)
- {
- PluginComponentFactory factory = event.getPluginComponentFactory();
-
- if (factory.getContainer().equals(containerId))
- addPluginComponent(factory);
- }
-
- /**
- * Implements
- * {@link PluginComponentListener#pluginComponentRemoved(PluginComponentEvent)}.
- *
- * @param event a <tt>PluginComponentEvent</tt> which specifies the
- * <tt>PluginComponent</tt> which has been added
- */
- public void pluginComponentRemoved(PluginComponentEvent event)
- {
- PluginComponentFactory factory = event.getPluginComponentFactory();
-
- if (factory.getContainer().equals(containerId))
- removePluginComponent(factory);
- }
-
- /**
- * Removes the component of a specific <code>PluginComponent</code> from
- * this <code>PluginContainer</code>.
- *
- * @param factory
- * the <code>PluginComponent</code> which is to have its
- * component removed from this <code>PluginContainer</code>
- */
- private synchronized void removePluginComponent(
- PluginComponentFactory factory)
- {
- Iterator<PluginComponent> iterator = pluginComponents.iterator();
- while(iterator.hasNext())
- {
- PluginComponent c = iterator.next();
- if(c.getParentFactory().equals(factory))
- {
- iterator.remove();
- container.remove((Component)c.getComponent());
- }
- }
- }
-}
+package net.java.sip.communicator.impl.gui.utils;
+
+import java.awt.*;
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.event.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.gui.Container;
+import net.java.sip.communicator.util.*;
+
+import org.osgi.framework.*;
+
+/**
+ * Provides capabilities to a specific <code>JComponent</code> to contain
+ * <code>PluginComponent</code>s, track when they are added and removed.
+ *
+ * @author Lyubomir Marinov
+ */
+public class PluginContainer
+ implements PluginComponentListener
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>PluginContainer</tt> class and its
+ * instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(PluginContainer.class);
+
+ /**
+ * The <code>JComponent</code> which contains the components of the
+ * <code>PluginComponent</code>s managed by this instance.
+ */
+ private final JComponent container;
+
+ /**
+ * The container id of the <code>PluginComponent</code> managed by this
+ * instance.
+ */
+ private final Container containerId;
+
+ /**
+ * The list of <code>PluginComponent</code> instances which have their
+ * components added to this <code>PluginContainer</code>.
+ */
+ private final java.util.List<PluginComponent> pluginComponents
+ = new LinkedList<PluginComponent>();
+
+ /**
+ * Initializes a new <code>PluginContainer</code> instance which is to
+ * provide capabilities to a specific <code>JComponent</code> container with
+ * a specific <code>Container</code> id to contain
+ * <code>PluginComponent</code> and track when they are added and removed.
+ *
+ * @param container
+ * the <code>JComponent</code> container the new instance is to
+ * provide its capabilities to
+ * @param containerId
+ * the <code>Container</code> id of the specified
+ * <code>container</code>
+ */
+ public PluginContainer(JComponent container, Container containerId)
+ {
+ this.container = container;
+ this.containerId = containerId;
+
+ initPluginComponents();
+ }
+
+ /**
+ * Adds a specific <tt>Component</tt> to a specific <tt>JComponent</tt>
+ * container. Allows extenders to apply custom logic to the exact placement
+ * of the specified <tt>Component</tt> in the specified container.
+ *
+ * @param component the <tt>Component</tt> to be added to the specified
+ * <tt>JComponent</tt> container
+ * @param container the <tt>JComponent</tt> container to add the specified
+ * <tt>Component</tt> to
+ * @param preferredIndex the index at which <tt>component</tt> is to be
+ * added to <tt>container</tt> if possible or <tt>-1</tt> if there is no
+ * preference with respect to the index in question
+ */
+ protected void addComponentToContainer(
+ Component component,
+ JComponent container,
+ int preferredIndex)
+ {
+ if ((0 <= preferredIndex)
+ && (preferredIndex < getComponentCount(container)))
+ container.add(component, preferredIndex);
+ else
+ container.add(component);
+ }
+
+ /**
+ * Adds the component of a specific <tt>PluginComponent</tt> to the
+ * associated <tt>Container</tt>.
+ *
+ * @param factory the <tt>PluginComponentFactory</tt> which is to have its
+ * component added to the <tt>Container</tt> associated with this
+ * <tt>PluginContainer</tt>
+ */
+ private synchronized void addPluginComponent(PluginComponentFactory factory)
+ {
+ PluginComponent c =
+ factory.getPluginComponentInstance(PluginContainer.this);
+
+ if (logger.isInfoEnabled())
+ logger.info("Will add plugin component: " + c);
+
+ /*
+ * Try to respect positionIndex of PluginComponent to some extent:
+ * PluginComponents with positionIndex equal to 0 go at the beginning,
+ * these with positionIndex equal to -1 follow them and then go these
+ * with positionIndex greater than 0.
+ */
+ int cIndex = factory.getPositionIndex();
+ int index = -1;
+ int i = 0;
+
+ for (PluginComponent pluginComponent : pluginComponents)
+ {
+ if (pluginComponent.equals(c))
+ return;
+
+ if (-1 == index)
+ {
+ int pluginComponentIndex = factory.getPositionIndex();
+
+ if ((0 == cIndex) || (-1 == cIndex))
+ {
+ if ((0 != pluginComponentIndex)
+ && (cIndex != pluginComponentIndex))
+ index = i;
+ }
+ else if (cIndex < pluginComponentIndex)
+ index = i;
+ }
+
+ i++;
+ }
+
+ int pluginComponentCount = pluginComponents.size();
+
+ if (-1 == index)
+ index = pluginComponents.size();
+
+ /*
+ * The container may have added Components of its own apart from the
+ * ones this PluginContainer has added to it. Since the common case for
+ * the additional Components is to have them appear at the beginning,
+ * adjust the index so it gets correct in the common case.
+ */
+ int containerComponentCount = getComponentCount(container);
+
+ addComponentToContainer(
+ (Component) c.getComponent(),
+ container,
+ (containerComponentCount > pluginComponentCount)
+ ? (index + (containerComponentCount - pluginComponentCount))
+ : index);
+ pluginComponents.add(index, c);
+
+ container.revalidate();
+ container.repaint();
+ }
+
+ /**
+ * Runs clean-up for associated resources which need explicit disposal (e.g.
+ * listeners keeping this instance alive because they were added to the
+ * model which operationally outlives this instance).
+ */
+ public void dispose()
+ {
+ GuiActivator.getUIService().removePluginComponentListener(this);
+
+ /*
+ * Explicitly remove the components of the PluginComponent instances
+ * because the latter are registered with OSGi and are thus global.
+ */
+ synchronized (this)
+ {
+ for (PluginComponent pluginComponent : pluginComponents)
+ container.remove((Component) pluginComponent.getComponent());
+ pluginComponents.clear();
+ }
+ }
+
+ /**
+ * Gets the number of <tt>Component</tt>s in a specific <tt>JComponent</tt>
+ * container. For example, returns the result of
+ * <tt>getMenuComponentCount()</tt> if <tt>container</tt> is an instance of
+ * <tt>JMenu</tt>.
+ *
+ * @param container the <tt>JComponent</tt> container to get the number of
+ * <tt>Component</tt>s of
+ * @return the number of <tt>Component</tt>s in the specified
+ * <tt>container</tt>
+ */
+ protected int getComponentCount(JComponent container)
+ {
+ return
+ (container instanceof JMenu)
+ ? ((JMenu) container).getMenuComponentCount()
+ : container.getComponentCount();
+ }
+
+ /**
+ * Gets the <tt>PluginComponent</tt>s of this <tt>PluginContainer</tt>.
+ *
+ * @return an <tt>Iterable</tt> over the <tt>PluginComponent</tt>s of this
+ * <tt>PluginContainer</tt>
+ */
+ public Iterable<PluginComponent> getPluginComponents()
+ {
+ return pluginComponents;
+ }
+
+ /**
+ * Adds the <tt>Component</tt>s of the <tt>PluginComponent</tt>s registered
+ * in the OSGi <tt>BundleContext</tt> in the associated <tt>Container</tt>.
+ */
+ private void initPluginComponents()
+ {
+ GuiActivator.getUIService().addPluginComponentListener(this);
+
+ // Look for PluginComponents registered in the OSGi BundleContext.
+ ServiceReference[] serRefs = null;
+
+ try
+ {
+ serRefs
+ = GuiActivator
+ .bundleContext
+ .getServiceReferences(
+ PluginComponentFactory.class.getName(),
+ "("
+ + Container.CONTAINER_ID
+ + "="
+ + containerId.getID()
+ + ")");
+ }
+ catch (InvalidSyntaxException exc)
+ {
+ logger.error("Could not obtain plugin reference.", exc);
+ }
+
+ if (serRefs != null)
+ {
+ for (ServiceReference serRef : serRefs)
+ {
+ PluginComponentFactory factory
+ = (PluginComponentFactory)
+ GuiActivator.bundleContext.getService(serRef);
+
+ addPluginComponent(factory);
+ }
+ }
+ }
+
+ /**
+ * Implements
+ * {@link PluginComponentListener#pluginComponentAdded(PluginComponentEvent)}.
+ *
+ * @param event a <tt>PluginComponentEvent</tt> which specifies the
+ * <tt>PluginComponent</tt> which has been added
+ */
+ public void pluginComponentAdded(PluginComponentEvent event)
+ {
+ PluginComponentFactory factory = event.getPluginComponentFactory();
+
+ if (factory.getContainer().equals(containerId))
+ addPluginComponent(factory);
+ }
+
+ /**
+ * Implements
+ * {@link PluginComponentListener#pluginComponentRemoved(PluginComponentEvent)}.
+ *
+ * @param event a <tt>PluginComponentEvent</tt> which specifies the
+ * <tt>PluginComponent</tt> which has been added
+ */
+ public void pluginComponentRemoved(PluginComponentEvent event)
+ {
+ PluginComponentFactory factory = event.getPluginComponentFactory();
+
+ if (factory.getContainer().equals(containerId))
+ removePluginComponent(factory);
+ }
+
+ /**
+ * Removes the component of a specific <code>PluginComponent</code> from
+ * this <code>PluginContainer</code>.
+ *
+ * @param factory
+ * the <code>PluginComponent</code> which is to have its
+ * component removed from this <code>PluginContainer</code>
+ */
+ private synchronized void removePluginComponent(
+ PluginComponentFactory factory)
+ {
+ Iterator<PluginComponent> iterator = pluginComponents.iterator();
+ while(iterator.hasNext())
+ {
+ PluginComponent c = iterator.next();
+ if(c.getParentFactory().equals(factory))
+ {
+ iterator.remove();
+ container.remove((Component)c.getComponent());
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/hid/HIDServiceImpl.java b/src/net/java/sip/communicator/impl/hid/HIDServiceImpl.java
index 00046d7..02552db 100644
--- a/src/net/java/sip/communicator/impl/hid/HIDServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/hid/HIDServiceImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,223 +15,223 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package net.java.sip.communicator.impl.hid;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import net.java.sip.communicator.service.hid.*;
-import net.java.sip.communicator.util.Logger;
-
-import org.jitsi.util.*;
-
-/**
- * Implementation of the HID service to provide way of regenerate key press
- * and mouse interactions.
- *
- * @author Sebastien Vincent
- */
-public class HIDServiceImpl implements HIDService
-{
- /**
- * The <tt>Logger</tt> used by the <tt>NeomediaActivator</tt> class and its
- * instances for logging output.
- */
- private final Logger logger = Logger.getLogger(HIDServiceImpl.class);
-
- /**
- * The robot used to perform some operations (mouse/key interactions).
- */
- private Robot robot = null;
-
- /**
- * Object to regenerates keys with JNI.
- */
- private NativeKeyboard nativeKeyboard = null;
-
- /**
- * Constructor.
- */
- protected HIDServiceImpl()
- {
- try
- {
- robot = new Robot();
- nativeKeyboard = new NativeKeyboard();
- }
- catch(Throwable e)
- {
- if(!GraphicsEnvironment.isHeadless())
- logger.error(
- "Error when creating Robot/NativeKeyboard instance", e);
- }
- }
-
- /**
- * Press a specific key using its keycode.
- *
- * @param keycode the Java keycode, all available keycodes can be found
- * in java.awt.event.KeyEvent class (VK_A, VK_SPACE, ...)
- * @see java.awt.event.KeyEvent
- */
- public void keyPress(int keycode)
- {
- if(OSUtils.IS_WINDOWS || OSUtils.IS_MAC)
- {
- /* do not allow modifiers for Windows (as
- * they are handled in native code with
- * VkScanCode) and Mac OS X
- */
- if(keycode == KeyEvent.VK_ALT ||
- keycode == KeyEvent.VK_SHIFT ||
- keycode == KeyEvent.VK_ALT_GRAPH)
- {
- return;
- }
- }
-
- /* AltGr does not seems to work with robot, handle it via our
- * JNI code
- */
- if(keycode == KeyEvent.VK_ALT_GRAPH)
- {
- symbolPress("altgr");
- }
- else
- {
- robot.keyPress(keycode);
- }
- }
-
- /**
- * Release a specific key using its keycode.
- *
- * @param keycode the Java keycode, all available keycode can be found
- * in java.awt.event.KeyEvent class (VK_A, VK_SPACE, ...)
- * @see java.awt.event.KeyEvent
- */
- public void keyRelease(int keycode)
- {
- /* AltGr does not seems to work with robot, handle it via our
- * JNI code
- */
- if(keycode == KeyEvent.VK_ALT_GRAPH)
- {
- symbolRelease("altgr");
- }
- else
- {
- robot.keyRelease(keycode);
- }
- }
-
- /**
- * Press a specific key using its char representation.
- *
- * @param key char representation of the key
- */
- public void keyPress(char key)
- {
- /* check for CTRL+X where X is [A-Z]
- * CTRL+A = 1, A = 65
- */
- if(key >= 1 && key <= 0x1A)
- {
- key = (char)(key + 64);
- robot.keyPress(key);
- return;
- }
-
- nativeKeyboard.keyPress(key);
- }
-
- /**
- * Release a specific key using its char representation.
- *
- * @param key char representation of the key
- */
- public void keyRelease(char key)
- {
- /* check for CTRL+X where X is [A-Z]
- * CTRL+A = 1, A = 65
- */
- if(key >= 1 && key <= 0x1A)
- {
- key = (char)(key + 64);
- robot.keyRelease(key);
- return;
- }
-
- if(nativeKeyboard != null)
- nativeKeyboard.keyRelease(key);
- }
-
- /**
- * Press a specific symbol (such as SHIFT or CTRL).
- *
- * @param symbol symbol name
- */
- private void symbolPress(String symbol)
- {
- if(nativeKeyboard != null)
- nativeKeyboard.symbolPress(symbol);
- }
-
- /**
- * Release a specific symbol (such as SHIFT or CTRL).
- *
- * @param symbol symbol name
- */
- private void symbolRelease(String symbol)
- {
- if(nativeKeyboard != null)
- nativeKeyboard.symbolRelease(symbol);
- }
-
- /**
- * Press a mouse button(s).
- *
- * @param btns button masks
- * @see java.awt.Robot#mousePress(int btns)
- */
- public void mousePress(int btns)
- {
- robot.mousePress(btns);
- }
-
- /**
- * Release a mouse button(s).
- *
- * @param btns button masks
- * @see java.awt.Robot#mouseRelease(int btns)
- */
- public void mouseRelease(int btns)
- {
- robot.mouseRelease(btns);
- }
-
- /**
- * Move the mouse on the screen.
- *
- * @param x x position on the screen
- * @param y y position on the screen
- * @see java.awt.Robot#mouseMove(int x, int y)
- */
- public void mouseMove(int x, int y)
- {
- robot.mouseMove(x, y);
- }
-
- /**
- * Release a mouse button(s).
- *
- * @param rotation wheel rotation (could be negative or positive depending
- * on the direction).
- * @see java.awt.Robot#mouseWheel(int wheelAmt)
- */
- public void mouseWheel(int rotation)
- {
- robot.mouseWheel(rotation);
- }
-}
+
+package net.java.sip.communicator.impl.hid;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import net.java.sip.communicator.service.hid.*;
+import net.java.sip.communicator.util.Logger;
+
+import org.jitsi.util.*;
+
+/**
+ * Implementation of the HID service to provide way of regenerate key press
+ * and mouse interactions.
+ *
+ * @author Sebastien Vincent
+ */
+public class HIDServiceImpl implements HIDService
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>NeomediaActivator</tt> class and its
+ * instances for logging output.
+ */
+ private final Logger logger = Logger.getLogger(HIDServiceImpl.class);
+
+ /**
+ * The robot used to perform some operations (mouse/key interactions).
+ */
+ private Robot robot = null;
+
+ /**
+ * Object to regenerates keys with JNI.
+ */
+ private NativeKeyboard nativeKeyboard = null;
+
+ /**
+ * Constructor.
+ */
+ protected HIDServiceImpl()
+ {
+ try
+ {
+ robot = new Robot();
+ nativeKeyboard = new NativeKeyboard();
+ }
+ catch(Throwable e)
+ {
+ if(!GraphicsEnvironment.isHeadless())
+ logger.error(
+ "Error when creating Robot/NativeKeyboard instance", e);
+ }
+ }
+
+ /**
+ * Press a specific key using its keycode.
+ *
+ * @param keycode the Java keycode, all available keycodes can be found
+ * in java.awt.event.KeyEvent class (VK_A, VK_SPACE, ...)
+ * @see java.awt.event.KeyEvent
+ */
+ public void keyPress(int keycode)
+ {
+ if(OSUtils.IS_WINDOWS || OSUtils.IS_MAC)
+ {
+ /* do not allow modifiers for Windows (as
+ * they are handled in native code with
+ * VkScanCode) and Mac OS X
+ */
+ if(keycode == KeyEvent.VK_ALT ||
+ keycode == KeyEvent.VK_SHIFT ||
+ keycode == KeyEvent.VK_ALT_GRAPH)
+ {
+ return;
+ }
+ }
+
+ /* AltGr does not seems to work with robot, handle it via our
+ * JNI code
+ */
+ if(keycode == KeyEvent.VK_ALT_GRAPH)
+ {
+ symbolPress("altgr");
+ }
+ else
+ {
+ robot.keyPress(keycode);
+ }
+ }
+
+ /**
+ * Release a specific key using its keycode.
+ *
+ * @param keycode the Java keycode, all available keycode can be found
+ * in java.awt.event.KeyEvent class (VK_A, VK_SPACE, ...)
+ * @see java.awt.event.KeyEvent
+ */
+ public void keyRelease(int keycode)
+ {
+ /* AltGr does not seems to work with robot, handle it via our
+ * JNI code
+ */
+ if(keycode == KeyEvent.VK_ALT_GRAPH)
+ {
+ symbolRelease("altgr");
+ }
+ else
+ {
+ robot.keyRelease(keycode);
+ }
+ }
+
+ /**
+ * Press a specific key using its char representation.
+ *
+ * @param key char representation of the key
+ */
+ public void keyPress(char key)
+ {
+ /* check for CTRL+X where X is [A-Z]
+ * CTRL+A = 1, A = 65
+ */
+ if(key >= 1 && key <= 0x1A)
+ {
+ key = (char)(key + 64);
+ robot.keyPress(key);
+ return;
+ }
+
+ nativeKeyboard.keyPress(key);
+ }
+
+ /**
+ * Release a specific key using its char representation.
+ *
+ * @param key char representation of the key
+ */
+ public void keyRelease(char key)
+ {
+ /* check for CTRL+X where X is [A-Z]
+ * CTRL+A = 1, A = 65
+ */
+ if(key >= 1 && key <= 0x1A)
+ {
+ key = (char)(key + 64);
+ robot.keyRelease(key);
+ return;
+ }
+
+ if(nativeKeyboard != null)
+ nativeKeyboard.keyRelease(key);
+ }
+
+ /**
+ * Press a specific symbol (such as SHIFT or CTRL).
+ *
+ * @param symbol symbol name
+ */
+ private void symbolPress(String symbol)
+ {
+ if(nativeKeyboard != null)
+ nativeKeyboard.symbolPress(symbol);
+ }
+
+ /**
+ * Release a specific symbol (such as SHIFT or CTRL).
+ *
+ * @param symbol symbol name
+ */
+ private void symbolRelease(String symbol)
+ {
+ if(nativeKeyboard != null)
+ nativeKeyboard.symbolRelease(symbol);
+ }
+
+ /**
+ * Press a mouse button(s).
+ *
+ * @param btns button masks
+ * @see java.awt.Robot#mousePress(int btns)
+ */
+ public void mousePress(int btns)
+ {
+ robot.mousePress(btns);
+ }
+
+ /**
+ * Release a mouse button(s).
+ *
+ * @param btns button masks
+ * @see java.awt.Robot#mouseRelease(int btns)
+ */
+ public void mouseRelease(int btns)
+ {
+ robot.mouseRelease(btns);
+ }
+
+ /**
+ * Move the mouse on the screen.
+ *
+ * @param x x position on the screen
+ * @param y y position on the screen
+ * @see java.awt.Robot#mouseMove(int x, int y)
+ */
+ public void mouseMove(int x, int y)
+ {
+ robot.mouseMove(x, y);
+ }
+
+ /**
+ * Release a mouse button(s).
+ *
+ * @param rotation wheel rotation (could be negative or positive depending
+ * on the direction).
+ * @see java.awt.Robot#mouseWheel(int wheelAmt)
+ */
+ public void mouseWheel(int rotation)
+ {
+ robot.mouseWheel(rotation);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/ldap/LdapPersonFoundImpl.java b/src/net/java/sip/communicator/impl/ldap/LdapPersonFoundImpl.java
index 3d8e6bf..36632fa 100644
--- a/src/net/java/sip/communicator/impl/ldap/LdapPersonFoundImpl.java
+++ b/src/net/java/sip/communicator/impl/ldap/LdapPersonFoundImpl.java
@@ -433,4 +433,10 @@ public class LdapPersonFoundImpl
return this.toString().equals(o.toString()) &&
this.getDN().equals(((LdapPersonFound) o).getDN());
}
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(toString(), getDN());
+ }
}
diff --git a/src/net/java/sip/communicator/impl/ldap/LdapSSLSocketFactoryDelegate.java b/src/net/java/sip/communicator/impl/ldap/LdapSSLSocketFactoryDelegate.java
index dabb4c6..9888628 100644
--- a/src/net/java/sip/communicator/impl/ldap/LdapSSLSocketFactoryDelegate.java
+++ b/src/net/java/sip/communicator/impl/ldap/LdapSSLSocketFactoryDelegate.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,75 +15,75 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.ldap;
-
-import java.io.*;
-import java.net.*;
-import java.security.*;
-
-import javax.net.*;
-
-import net.java.sip.communicator.service.certificate.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Utility class to delegate the creation of sockets to LDAP servers to our
- * {@link CertificateService}.
- * <p>
- * Note that the documentation says to extend {@link SocketFactory}, but the
- * LDAP directory context tries to create an unconnected socket without a
- * hostname first by calling <tt>createSocket</tt>. It would be impossible to
- * validate the hostname against the certificate, which leads to an insecure
- * communication. It only calls {@link #createSocket(String, int)} when
- * <tt>createSocket</tt> is not found
- *
- * @author Ingo Bauersachs
- */
-public class LdapSSLSocketFactoryDelegate
-{
- /**
- * Logger for this class.
- */
- private final static Logger logger =
- Logger.getLogger(LdapSSLSocketFactoryDelegate.class);
-
- /**
- * Get default SSL socket factory delegate.
- *
- * @return default SSL socket factory delegate.
- */
- public static Object getDefault()
- {
- return new LdapSSLSocketFactoryDelegate();
- }
-
- /**
- * Creates a socket for the specified destination host and port.
- *
- * @param host The hostname that the socket connects to.
- * @param port The port that the socket connects to.
- * @return The created socket.
- * @throws IOException
- * @throws UnknownHostException When the hostname cannot be resolved to an
- * IP address.
- */
- public Socket createSocket(String host, int port)
- throws IOException,
- UnknownHostException
- {
- try
- {
- return LdapServiceImpl
- .getCertificateService()
- .getSSLContext(
- LdapServiceImpl.getCertificateService().getTrustManager(
- host)).getSocketFactory().createSocket(host, port);
- }
- catch (GeneralSecurityException e)
- {
- logger.error(
- "unable to create socket through the certificate service", e);
- throw new IOException(e.getMessage());
- }
- }
-}
+package net.java.sip.communicator.impl.ldap;
+
+import java.io.*;
+import java.net.*;
+import java.security.*;
+
+import javax.net.*;
+
+import net.java.sip.communicator.service.certificate.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Utility class to delegate the creation of sockets to LDAP servers to our
+ * {@link CertificateService}.
+ * <p>
+ * Note that the documentation says to extend {@link SocketFactory}, but the
+ * LDAP directory context tries to create an unconnected socket without a
+ * hostname first by calling <tt>createSocket</tt>. It would be impossible to
+ * validate the hostname against the certificate, which leads to an insecure
+ * communication. It only calls {@link #createSocket(String, int)} when
+ * <tt>createSocket</tt> is not found
+ *
+ * @author Ingo Bauersachs
+ */
+public class LdapSSLSocketFactoryDelegate
+{
+ /**
+ * Logger for this class.
+ */
+ private final static Logger logger =
+ Logger.getLogger(LdapSSLSocketFactoryDelegate.class);
+
+ /**
+ * Get default SSL socket factory delegate.
+ *
+ * @return default SSL socket factory delegate.
+ */
+ public static Object getDefault()
+ {
+ return new LdapSSLSocketFactoryDelegate();
+ }
+
+ /**
+ * Creates a socket for the specified destination host and port.
+ *
+ * @param host The hostname that the socket connects to.
+ * @param port The port that the socket connects to.
+ * @return The created socket.
+ * @throws IOException
+ * @throws UnknownHostException When the hostname cannot be resolved to an
+ * IP address.
+ */
+ public Socket createSocket(String host, int port)
+ throws IOException,
+ UnknownHostException
+ {
+ try
+ {
+ return LdapServiceImpl
+ .getCertificateService()
+ .getSSLContext(
+ LdapServiceImpl.getCertificateService().getTrustManager(
+ host)).getSocketFactory().createSocket(host, port);
+ }
+ catch (GeneralSecurityException e)
+ {
+ logger.error(
+ "unable to create socket through the certificate service", e);
+ throw new IOException(e.getMessage());
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/muc/BaseChatRoomSourceContact.java b/src/net/java/sip/communicator/impl/muc/BaseChatRoomSourceContact.java
index 3225af9..28f43e3 100644
--- a/src/net/java/sip/communicator/impl/muc/BaseChatRoomSourceContact.java
+++ b/src/net/java/sip/communicator/impl/muc/BaseChatRoomSourceContact.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,141 +15,141 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.muc;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.muc.*;
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * Basic source contact for the chat rooms.
- *
- * @author Hristo Terezov
- */
-public class BaseChatRoomSourceContact
- extends SortedGenericSourceContact
-{
-
- /**
- * The parent contact query.
- */
- protected final ContactQuery parentQuery;
-
- /**
- * The name of the chat room associated with the contact.
- */
- private String chatRoomName;
-
- /**
- * The ID of the chat room associated with the contact.
- */
- private String chatRoomID;
-
- /**
- * The protocol provider of the chat room associated with the contact.
- */
- private ProtocolProviderService provider;
-
- /**
- * Contsructs new chat room source contact.
- * @param chatRoomName the name of the chat room associated with the room.
- * @param chatRoomID the id of the chat room associated with the room.
- * @param query the query associated with the contact.
- * @param pps the protocol provider of the contact.
- */
- public BaseChatRoomSourceContact(String chatRoomName,
- String chatRoomID, ContactQuery query, ProtocolProviderService pps)
- {
- super(query, query.getContactSource(), chatRoomName,
- generateDefaultContactDetails(chatRoomName));
-
- this.chatRoomName = chatRoomName;
- this.chatRoomID = chatRoomID;
- this.provider = pps;
- this.parentQuery = query;
-
- initContactProperties(ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE);
- setDisplayDetails(pps.getAccountID().getDisplayName());
- }
-
-
- /**
- * Sets the given presence status and the name of the chat room associated
- * with the contact.
- * @param status the presence status to be set.
- */
- protected void initContactProperties(PresenceStatus status)
- {
- setPresenceStatus(status);
- setContactAddress(chatRoomName);
- }
-
- /**
- * Generates the default contact details for
- * <tt>BaseChatRoomSourceContact</tt> instances.
- *
- * @param chatRoomName the name of the chat room associated with the contact
- * @return list of default <tt>ContactDetail</tt>s for the contact.
- */
- private static List<ContactDetail> generateDefaultContactDetails(
- String chatRoomName)
- {
- ContactDetail contactDetail
- = new ContactDetail(chatRoomName);
- List<Class<? extends OperationSet>> supportedOpSets
- = new ArrayList<Class<? extends OperationSet>>();
-
- supportedOpSets.add(OperationSetMultiUserChat.class);
- contactDetail.setSupportedOpSets(supportedOpSets);
-
- List<ContactDetail> contactDetails
- = new ArrayList<ContactDetail>();
-
- contactDetails.add(contactDetail);
- return contactDetails;
- }
-
- /**
- * Returns the id of the chat room associated with the contact.
- *
- * @return the chat room id.
- */
- public String getChatRoomID()
- {
- return chatRoomID;
- }
-
- /**
- * Returns the name of the chat room associated with the contact.
- *
- * @return the chat room name
- */
- public String getChatRoomName()
- {
- return chatRoomName;
- }
-
- /**
- * Returns the provider of the chat room associated with the contact.
- *
- * @return the provider
- */
- public ProtocolProviderService getProvider()
- {
- return provider;
- }
-
- /**
- * Returns the index of this source contact in its parent group.
- *
- * @return the index of this contact in its parent
- */
- public int getIndex()
- {
- if(parentQuery instanceof ServerChatRoomQuery)
- return ((ServerChatRoomQuery)parentQuery).indexOf(this);
- return -1;
- }
-}
+package net.java.sip.communicator.impl.muc;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.muc.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Basic source contact for the chat rooms.
+ *
+ * @author Hristo Terezov
+ */
+public class BaseChatRoomSourceContact
+ extends SortedGenericSourceContact
+{
+
+ /**
+ * The parent contact query.
+ */
+ protected final ContactQuery parentQuery;
+
+ /**
+ * The name of the chat room associated with the contact.
+ */
+ private String chatRoomName;
+
+ /**
+ * The ID of the chat room associated with the contact.
+ */
+ private String chatRoomID;
+
+ /**
+ * The protocol provider of the chat room associated with the contact.
+ */
+ private ProtocolProviderService provider;
+
+ /**
+ * Contsructs new chat room source contact.
+ * @param chatRoomName the name of the chat room associated with the room.
+ * @param chatRoomID the id of the chat room associated with the room.
+ * @param query the query associated with the contact.
+ * @param pps the protocol provider of the contact.
+ */
+ public BaseChatRoomSourceContact(String chatRoomName,
+ String chatRoomID, ContactQuery query, ProtocolProviderService pps)
+ {
+ super(query, query.getContactSource(), chatRoomName,
+ generateDefaultContactDetails(chatRoomName));
+
+ this.chatRoomName = chatRoomName;
+ this.chatRoomID = chatRoomID;
+ this.provider = pps;
+ this.parentQuery = query;
+
+ initContactProperties(ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE);
+ setDisplayDetails(pps.getAccountID().getDisplayName());
+ }
+
+
+ /**
+ * Sets the given presence status and the name of the chat room associated
+ * with the contact.
+ * @param status the presence status to be set.
+ */
+ protected void initContactProperties(PresenceStatus status)
+ {
+ setPresenceStatus(status);
+ setContactAddress(chatRoomName);
+ }
+
+ /**
+ * Generates the default contact details for
+ * <tt>BaseChatRoomSourceContact</tt> instances.
+ *
+ * @param chatRoomName the name of the chat room associated with the contact
+ * @return list of default <tt>ContactDetail</tt>s for the contact.
+ */
+ private static List<ContactDetail> generateDefaultContactDetails(
+ String chatRoomName)
+ {
+ ContactDetail contactDetail
+ = new ContactDetail(chatRoomName);
+ List<Class<? extends OperationSet>> supportedOpSets
+ = new ArrayList<Class<? extends OperationSet>>();
+
+ supportedOpSets.add(OperationSetMultiUserChat.class);
+ contactDetail.setSupportedOpSets(supportedOpSets);
+
+ List<ContactDetail> contactDetails
+ = new ArrayList<ContactDetail>();
+
+ contactDetails.add(contactDetail);
+ return contactDetails;
+ }
+
+ /**
+ * Returns the id of the chat room associated with the contact.
+ *
+ * @return the chat room id.
+ */
+ public String getChatRoomID()
+ {
+ return chatRoomID;
+ }
+
+ /**
+ * Returns the name of the chat room associated with the contact.
+ *
+ * @return the chat room name
+ */
+ public String getChatRoomName()
+ {
+ return chatRoomName;
+ }
+
+ /**
+ * Returns the provider of the chat room associated with the contact.
+ *
+ * @return the provider
+ */
+ public ProtocolProviderService getProvider()
+ {
+ return provider;
+ }
+
+ /**
+ * Returns the index of this source contact in its parent group.
+ *
+ * @return the index of this contact in its parent
+ */
+ public int getIndex()
+ {
+ if(parentQuery instanceof ServerChatRoomQuery)
+ return ((ServerChatRoomQuery)parentQuery).indexOf(this);
+ return -1;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/muc/ChatRoomQuery.java b/src/net/java/sip/communicator/impl/muc/ChatRoomQuery.java
index 24a3ebd..9cd1c21 100644
--- a/src/net/java/sip/communicator/impl/muc/ChatRoomQuery.java
+++ b/src/net/java/sip/communicator/impl/muc/ChatRoomQuery.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,543 +15,543 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.muc;
-
-import java.util.*;
-import java.util.regex.*;
-
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.muc.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-
-import org.osgi.framework.*;
-
-/**
- * The <tt>ChatRoomQuery</tt> is a query over the
- * <tt>ChatRoomContactSourceService</tt>.
- *
- * @author Hristo Terezov
- */
-public class ChatRoomQuery
- extends AsyncContactQuery<ContactSourceService>
- implements LocalUserChatRoomPresenceListener,
- ChatRoomListChangeListener,
- ChatRoomProviderWrapperListener
-{
- /**
- * The query string.
- */
- private String queryString;
-
- /**
- * List with the current results for the query.
- */
- private Set<ChatRoomSourceContact> contactResults
- = new TreeSet<ChatRoomSourceContact>();
-
- /**
- * MUC service.
- */
- private MUCServiceImpl mucService;
-
- /**
- * The number of contact query listeners.
- */
- private int contactQueryListenersCount = 0;
-
- /**
- * The protocol provider registration listener.
- */
- private ServiceListener protolProviderRegistrationListener = null;
-
- /**
- * Creates an instance of <tt>ChatRoomQuery</tt> by specifying
- * the parent contact source, the query string to match and the maximum
- * result contacts to return.
- *
- * @param queryString the query string to match
- * @param contactSource the parent contact source
- */
- public ChatRoomQuery(String queryString,
- ChatRoomContactSourceService contactSource)
- {
- super(contactSource,
- Pattern.compile(queryString, Pattern.CASE_INSENSITIVE
- | Pattern.LITERAL), true);
- this.queryString = queryString;
-
- mucService = MUCActivator.getMUCService();
-
- }
-
- /**
- * Adds listeners for the query
- */
- private void initListeners()
- {
- for(ProtocolProviderService pps : MUCActivator.getChatRoomProviders())
- {
- addQueryToProviderPresenceListeners(pps);
- }
-
- mucService.addChatRoomListChangeListener(this);
- mucService.addChatRoomProviderWrapperListener(this);
- protolProviderRegistrationListener = new ProtocolProviderRegListener();
- MUCActivator.bundleContext.addServiceListener(
- protolProviderRegistrationListener);
- }
-
- /**
- * Adds the query as presence listener to protocol provider service.
- * @param pps the protocol provider service.
- */
- public void addQueryToProviderPresenceListeners(ProtocolProviderService pps)
- {
- OperationSetMultiUserChat opSetMUC
- = pps.getOperationSet(OperationSetMultiUserChat.class);
- if(opSetMUC != null)
- {
- opSetMUC.addPresenceListener(this);
- }
- }
-
- /**
- * Removes the query from protocol provider service presence listeners.
- * @param pps the protocol provider service.
- */
- public void removeQueryFromProviderPresenceListeners(
- ProtocolProviderService pps)
- {
- OperationSetMultiUserChat opSetMUC
- = pps.getOperationSet(OperationSetMultiUserChat.class);
- if(opSetMUC != null)
- {
- opSetMUC.removePresenceListener(this);
- }
- }
-
- @Override
- protected void run()
- {
- Iterator<ChatRoomProviderWrapper> chatRoomProviders
- = mucService.getChatRoomProviders();
-
- while (chatRoomProviders.hasNext())
- {
- ChatRoomProviderWrapper provider = chatRoomProviders.next();
- providerAdded(provider, true);
- }
-
- if (getStatus() != QUERY_CANCELED)
- setStatus(QUERY_COMPLETED);
- }
-
- /**
- * Handles adding a chat room provider.
- * @param provider the provider.
- * @param addQueryResult indicates whether we should add the chat room to
- * the query results or fire an event without adding it to the results.
- */
- private void providerAdded(ChatRoomProviderWrapper provider,
- boolean addQueryResult)
- {
-
- for(int i = 0; i < provider.countChatRooms(); i++)
- {
- ChatRoomWrapper chatRoom = provider.getChatRoom(i);
- addChatRoom( provider.getProtocolProvider(),
- chatRoom.getChatRoomName(), chatRoom.getChatRoomID(),
- addQueryResult, chatRoom.isAutojoin());
- }
- }
-
- /**
- * Handles chat room presence status updates.
- *
- * @param evt the <tt>LocalUserChatRoomPresenceChangeEvent</tt> instance
- * containing the chat room and the type, and reason of the change
- */
- @Override
- public void localUserPresenceChanged(
- LocalUserChatRoomPresenceChangeEvent evt)
- {
- ChatRoom sourceChatRoom = evt.getChatRoom();
-
- String eventType = evt.getEventType();
-
- boolean existingContact = false;
- ChatRoomSourceContact foundContact = null;
- synchronized (contactResults)
- {
- for (ChatRoomSourceContact contact : contactResults)
- {
- if (contactEqualsChatRoom(contact, sourceChatRoom))
- {
- existingContact = true;
- foundContact = contact;
- contactResults.remove(contact);
- break;
- }
- }
- }
-
- if (LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_JOINED.equals(eventType))
- {
- if(existingContact)
- {
- foundContact.setPresenceStatus(
- ChatRoomPresenceStatus.CHAT_ROOM_ONLINE);
- synchronized (contactResults)
- {
- contactResults.add(foundContact);
- }
- fireContactChanged(foundContact);
- }
- else
- {
- ChatRoomWrapper chatRoom
- = MUCActivator.getMUCService()
- .findChatRoomWrapperFromChatRoom(sourceChatRoom);
- if(chatRoom != null)
- addChatRoom(sourceChatRoom, false, chatRoom.isAutojoin());
- }
- }
- else if ((LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_LEFT.equals(eventType)
- || LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_KICKED.equals(eventType)
- || LocalUserChatRoomPresenceChangeEvent
- .LOCAL_USER_DROPPED.equals(eventType))
- )
- {
- if(existingContact)
- {
- foundContact.setPresenceStatus(
- ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE);
- synchronized (contactResults)
- {
- contactResults.add(foundContact);
- }
- fireContactChanged(foundContact);
- }
- }
- }
-
- /**
- * Adds found result to the query results.
- *
- * @param room the chat room.
- * @param addQueryResult indicates whether we should add the chat room to
- * the query results or fire an event without adding it to the results.
- * @param isAutoJoin the auto join state of the contact.
- */
- private void addChatRoom(ChatRoom room, boolean addQueryResult,
- boolean isAutoJoin)
- {
- if(queryString == null
- || ((room.getName().contains(
- queryString)
- || room.getIdentifier().contains(queryString)
- )))
- {
- ChatRoomSourceContact contact
- = new ChatRoomSourceContact(room, this, isAutoJoin);
- synchronized (contactResults)
- {
- contactResults.add(contact);
- }
-
- if(addQueryResult)
- {
- addQueryResult(contact, false);
- }
- else
- {
- fireContactReceived(contact, false);
- }
- }
- }
-
- /**
- * Adds found result to the query results.
- *
- * @param pps the protocol provider associated with the found chat room.
- * @param chatRoomName the name of the chat room.
- * @param chatRoomID the id of the chat room.
- * @param addQueryResult indicates whether we should add the chat room to
- * the query results or fire an event without adding it to the results.
- * @param isAutoJoin the auto join state of the contact.
- */
- private void addChatRoom(ProtocolProviderService pps,
- String chatRoomName, String chatRoomID, boolean addQueryResult,
- boolean isAutoJoin)
- {
- if(queryString == null
- || ((chatRoomName.contains(
- queryString)
- || chatRoomID.contains(queryString)
- )))
- {
- ChatRoomSourceContact contact
- = new ChatRoomSourceContact(chatRoomName, chatRoomID, this, pps,
- isAutoJoin);
- synchronized (contactResults)
- {
- contactResults.add(contact);
- }
-
- if(addQueryResult)
- {
- addQueryResult(contact, false);
- }
- else
- {
- fireContactReceived(contact, false);
- }
- }
- }
-
- /**
- * Indicates that a change has occurred in the chat room data list.
- * @param evt the event that describes the change.
- */
- @Override
- public void contentChanged(final ChatRoomListChangeEvent evt)
- {
- ChatRoomWrapper chatRoom = evt.getSourceChatRoom();
- switch (evt.getEventID())
- {
- case ChatRoomListChangeEvent.CHAT_ROOM_ADDED:
- addChatRoom(chatRoom.getChatRoom(), false,
- chatRoom.isAutojoin());
- break;
- case ChatRoomListChangeEvent.CHAT_ROOM_REMOVED:
- LinkedList<ChatRoomSourceContact> tmpContactResults;
- synchronized (contactResults)
- {
- tmpContactResults
- = new LinkedList<ChatRoomSourceContact>(contactResults);
-
- for (ChatRoomSourceContact contact : tmpContactResults)
- {
- if (contactEqualsChatRoom(contact, chatRoom))
- {
- contactResults.remove(contact);
- fireContactRemoved(contact);
- break;
- }
- }
- }
- break;
- case ChatRoomListChangeEvent.CHAT_ROOM_CHANGED:
- synchronized (contactResults)
- {
- for (ChatRoomSourceContact contact : contactResults)
- {
- if (contactEqualsChatRoom(contact,
- chatRoom.getChatRoom()))
- {
- if (chatRoom.isAutojoin() != contact.isAutoJoin())
- {
- contact.setAutoJoin(chatRoom.isAutojoin());
- fireContactChanged(contact);
- }
- break;
- }
- }
- }
- break;
- default:
- break;
- }
- }
-
- @Override
- public void chatRoomProviderWrapperAdded(ChatRoomProviderWrapper provider)
- {
- providerAdded(provider, false);
- }
-
- @Override
- public void chatRoomProviderWrapperRemoved(ChatRoomProviderWrapper provider)
- {
- LinkedList<ChatRoomSourceContact> tmpContactResults;
- synchronized (contactResults)
- {
- tmpContactResults
- = new LinkedList<ChatRoomSourceContact>(contactResults);
-
- for(ChatRoomSourceContact contact : tmpContactResults)
- {
- if(contact.getProvider().equals(provider.getProtocolProvider()))
- {
- contactResults.remove(contact);
- fireContactRemoved(contact);
- }
- }
- }
- }
-
- /**
- * Test equality of contact to chat room. This test recognizes that chat
- * rooms may have equal names but connected to different accounts.
- *
- * @param contact the contact
- * @param chatRoom the chat room
- * @return returns <tt>true</tt> if they are equal, or <tt>false</tt> if
- * they are different
- */
- private boolean contactEqualsChatRoom(final ChatRoomSourceContact contact,
- final ChatRoom chatRoom)
- {
- return contact.getProvider() == chatRoom.getParentProvider()
- && chatRoom.getIdentifier().equals(contact.getContactAddress());
- }
-
- /**
- * Test equality of contact to chat room wrapper. This method does not rely
- * on a chat room instance, since that may not be available in case of
- * removal.
- *
- * @param contact the contact
- * @param chatRoomWrapper the chat room wrapper
- * @return returns <tt>true</tt> if they are equal, or <tt>false</tt> if
- * they are different.
- */
- private boolean contactEqualsChatRoom(final ChatRoomSourceContact contact,
- final ChatRoomWrapper chatRoomWrapper)
- {
- return contact.getProvider() == chatRoomWrapper.getParentProvider()
- .getProtocolProvider()
- && contact.getContactAddress().equals(
- chatRoomWrapper.getChatRoomID());
- }
-
- /**
- * Returns the index of the contact in the contact results list.
- * @param contact the contact.
- * @return the index of the contact in the contact results list.
- */
- public synchronized int indexOf(ChatRoomSourceContact contact)
- {
- Iterator<ChatRoomSourceContact> it = contactResults.iterator();
- int i = 0;
- while(it.hasNext())
- {
- if(contact.equals(it.next()))
- {
- return i;
- }
- i++;
- }
- return -1;
- }
-
- /**
- * Clears any listener we used.
- */
- private void clearListeners()
- {
- mucService.removeChatRoomListChangeListener(this);
- mucService.removeChatRoomProviderWrapperListener(this);
- if(protolProviderRegistrationListener != null)
- MUCActivator.bundleContext.removeServiceListener(
- protolProviderRegistrationListener);
- protolProviderRegistrationListener = null;
- for(ProtocolProviderService pps : MUCActivator.getChatRoomProviders())
- {
- removeQueryFromProviderPresenceListeners(pps);
- }
- }
-
- /**
- * Cancels this <tt>ContactQuery</tt>.
- *
- * @see ContactQuery#cancel()
- */
- public void cancel()
- {
- clearListeners();
-
- super.cancel();
- }
-
- /**
- * If query has status changed to cancel, let's clear listeners.
- * @param status {@link ContactQuery#QUERY_CANCELED},
- * {@link ContactQuery#QUERY_COMPLETED}
- */
- public void setStatus(int status)
- {
- if(status == QUERY_CANCELED)
- clearListeners();
-
- super.setStatus(status);
- }
-
- @Override
- public void addContactQueryListener(ContactQueryListener l)
- {
- super.addContactQueryListener(l);
- contactQueryListenersCount++;
- if(contactQueryListenersCount == 1)
- {
- initListeners();
- }
- }
-
- @Override
- public void removeContactQueryListener(ContactQueryListener l)
- {
- super.removeContactQueryListener(l);
- contactQueryListenersCount--;
- if(contactQueryListenersCount == 0)
- {
- clearListeners();
- }
- }
-
- /**
- * Listens for <tt>ProtocolProviderService</tt> registrations.
- */
- private class ProtocolProviderRegListener
- implements ServiceListener
- {
- /**
- * Handles service change events.
- */
- public void serviceChanged(ServiceEvent event)
- {
- ServiceReference serviceRef = event.getServiceReference();
-
- // if the event is caused by a bundle being stopped, we don't want to
- // know
- if (serviceRef.getBundle().getState() == Bundle.STOPPING)
- {
- return;
- }
-
- Object service = MUCActivator.bundleContext.getService(serviceRef);
-
- // we don't care if the source service is not a protocol provider
- if (!(service instanceof ProtocolProviderService))
- {
- return;
- }
-
- switch (event.getType())
- {
- case ServiceEvent.REGISTERED:
- addQueryToProviderPresenceListeners(
- (ProtocolProviderService) service);
- break;
- case ServiceEvent.UNREGISTERING:
- removeQueryFromProviderPresenceListeners(
- (ProtocolProviderService) service);
- break;
- }
- }
- }
+package net.java.sip.communicator.impl.muc;
+
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.muc.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+
+import org.osgi.framework.*;
+
+/**
+ * The <tt>ChatRoomQuery</tt> is a query over the
+ * <tt>ChatRoomContactSourceService</tt>.
+ *
+ * @author Hristo Terezov
+ */
+public class ChatRoomQuery
+ extends AsyncContactQuery<ContactSourceService>
+ implements LocalUserChatRoomPresenceListener,
+ ChatRoomListChangeListener,
+ ChatRoomProviderWrapperListener
+{
+ /**
+ * The query string.
+ */
+ private String queryString;
+
+ /**
+ * List with the current results for the query.
+ */
+ private Set<ChatRoomSourceContact> contactResults
+ = new TreeSet<ChatRoomSourceContact>();
+
+ /**
+ * MUC service.
+ */
+ private MUCServiceImpl mucService;
+
+ /**
+ * The number of contact query listeners.
+ */
+ private int contactQueryListenersCount = 0;
+
+ /**
+ * The protocol provider registration listener.
+ */
+ private ServiceListener protolProviderRegistrationListener = null;
+
+ /**
+ * Creates an instance of <tt>ChatRoomQuery</tt> by specifying
+ * the parent contact source, the query string to match and the maximum
+ * result contacts to return.
+ *
+ * @param queryString the query string to match
+ * @param contactSource the parent contact source
+ */
+ public ChatRoomQuery(String queryString,
+ ChatRoomContactSourceService contactSource)
+ {
+ super(contactSource,
+ Pattern.compile(queryString, Pattern.CASE_INSENSITIVE
+ | Pattern.LITERAL), true);
+ this.queryString = queryString;
+
+ mucService = MUCActivator.getMUCService();
+
+ }
+
+ /**
+ * Adds listeners for the query
+ */
+ private void initListeners()
+ {
+ for(ProtocolProviderService pps : MUCActivator.getChatRoomProviders())
+ {
+ addQueryToProviderPresenceListeners(pps);
+ }
+
+ mucService.addChatRoomListChangeListener(this);
+ mucService.addChatRoomProviderWrapperListener(this);
+ protolProviderRegistrationListener = new ProtocolProviderRegListener();
+ MUCActivator.bundleContext.addServiceListener(
+ protolProviderRegistrationListener);
+ }
+
+ /**
+ * Adds the query as presence listener to protocol provider service.
+ * @param pps the protocol provider service.
+ */
+ public void addQueryToProviderPresenceListeners(ProtocolProviderService pps)
+ {
+ OperationSetMultiUserChat opSetMUC
+ = pps.getOperationSet(OperationSetMultiUserChat.class);
+ if(opSetMUC != null)
+ {
+ opSetMUC.addPresenceListener(this);
+ }
+ }
+
+ /**
+ * Removes the query from protocol provider service presence listeners.
+ * @param pps the protocol provider service.
+ */
+ public void removeQueryFromProviderPresenceListeners(
+ ProtocolProviderService pps)
+ {
+ OperationSetMultiUserChat opSetMUC
+ = pps.getOperationSet(OperationSetMultiUserChat.class);
+ if(opSetMUC != null)
+ {
+ opSetMUC.removePresenceListener(this);
+ }
+ }
+
+ @Override
+ protected void run()
+ {
+ Iterator<ChatRoomProviderWrapper> chatRoomProviders
+ = mucService.getChatRoomProviders();
+
+ while (chatRoomProviders.hasNext())
+ {
+ ChatRoomProviderWrapper provider = chatRoomProviders.next();
+ providerAdded(provider, true);
+ }
+
+ if (getStatus() != QUERY_CANCELED)
+ setStatus(QUERY_COMPLETED);
+ }
+
+ /**
+ * Handles adding a chat room provider.
+ * @param provider the provider.
+ * @param addQueryResult indicates whether we should add the chat room to
+ * the query results or fire an event without adding it to the results.
+ */
+ private void providerAdded(ChatRoomProviderWrapper provider,
+ boolean addQueryResult)
+ {
+
+ for(int i = 0; i < provider.countChatRooms(); i++)
+ {
+ ChatRoomWrapper chatRoom = provider.getChatRoom(i);
+ addChatRoom( provider.getProtocolProvider(),
+ chatRoom.getChatRoomName(), chatRoom.getChatRoomID(),
+ addQueryResult, chatRoom.isAutojoin());
+ }
+ }
+
+ /**
+ * Handles chat room presence status updates.
+ *
+ * @param evt the <tt>LocalUserChatRoomPresenceChangeEvent</tt> instance
+ * containing the chat room and the type, and reason of the change
+ */
+ @Override
+ public void localUserPresenceChanged(
+ LocalUserChatRoomPresenceChangeEvent evt)
+ {
+ ChatRoom sourceChatRoom = evt.getChatRoom();
+
+ String eventType = evt.getEventType();
+
+ boolean existingContact = false;
+ ChatRoomSourceContact foundContact = null;
+ synchronized (contactResults)
+ {
+ for (ChatRoomSourceContact contact : contactResults)
+ {
+ if (contactEqualsChatRoom(contact, sourceChatRoom))
+ {
+ existingContact = true;
+ foundContact = contact;
+ contactResults.remove(contact);
+ break;
+ }
+ }
+ }
+
+ if (LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_JOINED.equals(eventType))
+ {
+ if(existingContact)
+ {
+ foundContact.setPresenceStatus(
+ ChatRoomPresenceStatus.CHAT_ROOM_ONLINE);
+ synchronized (contactResults)
+ {
+ contactResults.add(foundContact);
+ }
+ fireContactChanged(foundContact);
+ }
+ else
+ {
+ ChatRoomWrapper chatRoom
+ = MUCActivator.getMUCService()
+ .findChatRoomWrapperFromChatRoom(sourceChatRoom);
+ if(chatRoom != null)
+ addChatRoom(sourceChatRoom, false, chatRoom.isAutojoin());
+ }
+ }
+ else if ((LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_LEFT.equals(eventType)
+ || LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_KICKED.equals(eventType)
+ || LocalUserChatRoomPresenceChangeEvent
+ .LOCAL_USER_DROPPED.equals(eventType))
+ )
+ {
+ if(existingContact)
+ {
+ foundContact.setPresenceStatus(
+ ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE);
+ synchronized (contactResults)
+ {
+ contactResults.add(foundContact);
+ }
+ fireContactChanged(foundContact);
+ }
+ }
+ }
+
+ /**
+ * Adds found result to the query results.
+ *
+ * @param room the chat room.
+ * @param addQueryResult indicates whether we should add the chat room to
+ * the query results or fire an event without adding it to the results.
+ * @param isAutoJoin the auto join state of the contact.
+ */
+ private void addChatRoom(ChatRoom room, boolean addQueryResult,
+ boolean isAutoJoin)
+ {
+ if(queryString == null
+ || ((room.getName().contains(
+ queryString)
+ || room.getIdentifier().contains(queryString)
+ )))
+ {
+ ChatRoomSourceContact contact
+ = new ChatRoomSourceContact(room, this, isAutoJoin);
+ synchronized (contactResults)
+ {
+ contactResults.add(contact);
+ }
+
+ if(addQueryResult)
+ {
+ addQueryResult(contact, false);
+ }
+ else
+ {
+ fireContactReceived(contact, false);
+ }
+ }
+ }
+
+ /**
+ * Adds found result to the query results.
+ *
+ * @param pps the protocol provider associated with the found chat room.
+ * @param chatRoomName the name of the chat room.
+ * @param chatRoomID the id of the chat room.
+ * @param addQueryResult indicates whether we should add the chat room to
+ * the query results or fire an event without adding it to the results.
+ * @param isAutoJoin the auto join state of the contact.
+ */
+ private void addChatRoom(ProtocolProviderService pps,
+ String chatRoomName, String chatRoomID, boolean addQueryResult,
+ boolean isAutoJoin)
+ {
+ if(queryString == null
+ || ((chatRoomName.contains(
+ queryString)
+ || chatRoomID.contains(queryString)
+ )))
+ {
+ ChatRoomSourceContact contact
+ = new ChatRoomSourceContact(chatRoomName, chatRoomID, this, pps,
+ isAutoJoin);
+ synchronized (contactResults)
+ {
+ contactResults.add(contact);
+ }
+
+ if(addQueryResult)
+ {
+ addQueryResult(contact, false);
+ }
+ else
+ {
+ fireContactReceived(contact, false);
+ }
+ }
+ }
+
+ /**
+ * Indicates that a change has occurred in the chat room data list.
+ * @param evt the event that describes the change.
+ */
+ @Override
+ public void contentChanged(final ChatRoomListChangeEvent evt)
+ {
+ ChatRoomWrapper chatRoom = evt.getSourceChatRoom();
+ switch (evt.getEventID())
+ {
+ case ChatRoomListChangeEvent.CHAT_ROOM_ADDED:
+ addChatRoom(chatRoom.getChatRoom(), false,
+ chatRoom.isAutojoin());
+ break;
+ case ChatRoomListChangeEvent.CHAT_ROOM_REMOVED:
+ LinkedList<ChatRoomSourceContact> tmpContactResults;
+ synchronized (contactResults)
+ {
+ tmpContactResults
+ = new LinkedList<ChatRoomSourceContact>(contactResults);
+
+ for (ChatRoomSourceContact contact : tmpContactResults)
+ {
+ if (contactEqualsChatRoom(contact, chatRoom))
+ {
+ contactResults.remove(contact);
+ fireContactRemoved(contact);
+ break;
+ }
+ }
+ }
+ break;
+ case ChatRoomListChangeEvent.CHAT_ROOM_CHANGED:
+ synchronized (contactResults)
+ {
+ for (ChatRoomSourceContact contact : contactResults)
+ {
+ if (contactEqualsChatRoom(contact,
+ chatRoom.getChatRoom()))
+ {
+ if (chatRoom.isAutojoin() != contact.isAutoJoin())
+ {
+ contact.setAutoJoin(chatRoom.isAutojoin());
+ fireContactChanged(contact);
+ }
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void chatRoomProviderWrapperAdded(ChatRoomProviderWrapper provider)
+ {
+ providerAdded(provider, false);
+ }
+
+ @Override
+ public void chatRoomProviderWrapperRemoved(ChatRoomProviderWrapper provider)
+ {
+ LinkedList<ChatRoomSourceContact> tmpContactResults;
+ synchronized (contactResults)
+ {
+ tmpContactResults
+ = new LinkedList<ChatRoomSourceContact>(contactResults);
+
+ for(ChatRoomSourceContact contact : tmpContactResults)
+ {
+ if(contact.getProvider().equals(provider.getProtocolProvider()))
+ {
+ contactResults.remove(contact);
+ fireContactRemoved(contact);
+ }
+ }
+ }
+ }
+
+ /**
+ * Test equality of contact to chat room. This test recognizes that chat
+ * rooms may have equal names but connected to different accounts.
+ *
+ * @param contact the contact
+ * @param chatRoom the chat room
+ * @return returns <tt>true</tt> if they are equal, or <tt>false</tt> if
+ * they are different
+ */
+ private boolean contactEqualsChatRoom(final ChatRoomSourceContact contact,
+ final ChatRoom chatRoom)
+ {
+ return contact.getProvider() == chatRoom.getParentProvider()
+ && chatRoom.getIdentifier().equals(contact.getContactAddress());
+ }
+
+ /**
+ * Test equality of contact to chat room wrapper. This method does not rely
+ * on a chat room instance, since that may not be available in case of
+ * removal.
+ *
+ * @param contact the contact
+ * @param chatRoomWrapper the chat room wrapper
+ * @return returns <tt>true</tt> if they are equal, or <tt>false</tt> if
+ * they are different.
+ */
+ private boolean contactEqualsChatRoom(final ChatRoomSourceContact contact,
+ final ChatRoomWrapper chatRoomWrapper)
+ {
+ return contact.getProvider() == chatRoomWrapper.getParentProvider()
+ .getProtocolProvider()
+ && contact.getContactAddress().equals(
+ chatRoomWrapper.getChatRoomID());
+ }
+
+ /**
+ * Returns the index of the contact in the contact results list.
+ * @param contact the contact.
+ * @return the index of the contact in the contact results list.
+ */
+ public synchronized int indexOf(ChatRoomSourceContact contact)
+ {
+ Iterator<ChatRoomSourceContact> it = contactResults.iterator();
+ int i = 0;
+ while(it.hasNext())
+ {
+ if(contact.equals(it.next()))
+ {
+ return i;
+ }
+ i++;
+ }
+ return -1;
+ }
+
+ /**
+ * Clears any listener we used.
+ */
+ private void clearListeners()
+ {
+ mucService.removeChatRoomListChangeListener(this);
+ mucService.removeChatRoomProviderWrapperListener(this);
+ if(protolProviderRegistrationListener != null)
+ MUCActivator.bundleContext.removeServiceListener(
+ protolProviderRegistrationListener);
+ protolProviderRegistrationListener = null;
+ for(ProtocolProviderService pps : MUCActivator.getChatRoomProviders())
+ {
+ removeQueryFromProviderPresenceListeners(pps);
+ }
+ }
+
+ /**
+ * Cancels this <tt>ContactQuery</tt>.
+ *
+ * @see ContactQuery#cancel()
+ */
+ public void cancel()
+ {
+ clearListeners();
+
+ super.cancel();
+ }
+
+ /**
+ * If query has status changed to cancel, let's clear listeners.
+ * @param status {@link ContactQuery#QUERY_CANCELED},
+ * {@link ContactQuery#QUERY_COMPLETED}
+ */
+ public void setStatus(int status)
+ {
+ if(status == QUERY_CANCELED)
+ clearListeners();
+
+ super.setStatus(status);
+ }
+
+ @Override
+ public void addContactQueryListener(ContactQueryListener l)
+ {
+ super.addContactQueryListener(l);
+ contactQueryListenersCount++;
+ if(contactQueryListenersCount == 1)
+ {
+ initListeners();
+ }
+ }
+
+ @Override
+ public void removeContactQueryListener(ContactQueryListener l)
+ {
+ super.removeContactQueryListener(l);
+ contactQueryListenersCount--;
+ if(contactQueryListenersCount == 0)
+ {
+ clearListeners();
+ }
+ }
+
+ /**
+ * Listens for <tt>ProtocolProviderService</tt> registrations.
+ */
+ private class ProtocolProviderRegListener
+ implements ServiceListener
+ {
+ /**
+ * Handles service change events.
+ */
+ public void serviceChanged(ServiceEvent event)
+ {
+ ServiceReference serviceRef = event.getServiceReference();
+
+ // if the event is caused by a bundle being stopped, we don't want to
+ // know
+ if (serviceRef.getBundle().getState() == Bundle.STOPPING)
+ {
+ return;
+ }
+
+ Object service = MUCActivator.bundleContext.getService(serviceRef);
+
+ // we don't care if the source service is not a protocol provider
+ if (!(service instanceof ProtocolProviderService))
+ {
+ return;
+ }
+
+ switch (event.getType())
+ {
+ case ServiceEvent.REGISTERED:
+ addQueryToProviderPresenceListeners(
+ (ProtocolProviderService) service);
+ break;
+ case ServiceEvent.UNREGISTERING:
+ removeQueryFromProviderPresenceListeners(
+ (ProtocolProviderService) service);
+ break;
+ }
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/muc/ChatRoomSourceContact.java b/src/net/java/sip/communicator/impl/muc/ChatRoomSourceContact.java
index a659a6e..842cc15 100644
--- a/src/net/java/sip/communicator/impl/muc/ChatRoomSourceContact.java
+++ b/src/net/java/sip/communicator/impl/muc/ChatRoomSourceContact.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,113 +15,113 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.muc;
-
-import net.java.sip.communicator.service.muc.*;
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * Source contact for the chat rooms.
- *
- * @author Hristo Terezov
- */
-public class ChatRoomSourceContact
- extends BaseChatRoomSourceContact
-{
- /**
- * The protocol provider of the chat room associated with the contact.
- */
- private boolean isAutoJoin;
-
- /**
- * Constructs a new chat room source contact.
- *
- * @param chatRoomName the name of the chat room associated with the room.
- * @param chatRoomID the id of the chat room associated with the room.
- * @param query the query associated with the contact.
- * @param pps the protocol provider of the contact.
- * @param isAutoJoin the auto join state.
- */
- public ChatRoomSourceContact(String chatRoomName,
- String chatRoomID, ChatRoomQuery query, ProtocolProviderService pps,
- boolean isAutoJoin)
- {
- super(chatRoomName, chatRoomID, query, pps);
-
- this.isAutoJoin = isAutoJoin;
-
- initContactProperties(getChatRoomStateByName());
- }
-
- /**
- * Constructs new chat room source contact.
- *
- * @param chatRoom the chat room associated with the contact.
- * @param query the query associated with the contact.
- * @param isAutoJoin the auto join state
- */
- public ChatRoomSourceContact(ChatRoom chatRoom, ChatRoomQuery query,
- boolean isAutoJoin)
- {
- super(chatRoom.getName(), chatRoom.getIdentifier(), query,
- chatRoom.getParentProvider());
- this.isAutoJoin = isAutoJoin;
-
- initContactProperties(
- chatRoom.isJoined()
- ? ChatRoomPresenceStatus.CHAT_ROOM_ONLINE
- : ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE);
-
- }
-
- /**
- * Checks if the chat room associated with the contact is joined or not and
- * returns it presence status.
- *
- * @return the presence status of the chat room associated with the contact.
- */
- private PresenceStatus getChatRoomStateByName()
- {
- for(ChatRoom room :
- getProvider().getOperationSet(OperationSetMultiUserChat.class)
- .getCurrentlyJoinedChatRooms())
- {
- if(room.getName().equals(getChatRoomName()))
- {
- return ChatRoomPresenceStatus.CHAT_ROOM_ONLINE;
- }
- }
- return ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE;
- }
-
- /**
- * Returns the index of this source contact in its parent group.
- *
- * @return the index of this contact in its parent
- */
- @Override
- public int getIndex()
- {
- return ((ChatRoomQuery)parentQuery).indexOf(this);
- }
-
- /**
- * Returns the auto join state of the contact.
- *
- * @return the auto join state of the contact.
- */
- public boolean isAutoJoin()
- {
- return isAutoJoin;
- }
-
- /**
- * Sets the auto join state of the contact.
- *
- * @param isAutoJoin the auto join state to be set.
- */
- public void setAutoJoin(boolean isAutoJoin)
- {
- this.isAutoJoin = isAutoJoin;
- }
-}
+package net.java.sip.communicator.impl.muc;
+
+import net.java.sip.communicator.service.muc.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Source contact for the chat rooms.
+ *
+ * @author Hristo Terezov
+ */
+public class ChatRoomSourceContact
+ extends BaseChatRoomSourceContact
+{
+ /**
+ * The protocol provider of the chat room associated with the contact.
+ */
+ private boolean isAutoJoin;
+
+ /**
+ * Constructs a new chat room source contact.
+ *
+ * @param chatRoomName the name of the chat room associated with the room.
+ * @param chatRoomID the id of the chat room associated with the room.
+ * @param query the query associated with the contact.
+ * @param pps the protocol provider of the contact.
+ * @param isAutoJoin the auto join state.
+ */
+ public ChatRoomSourceContact(String chatRoomName,
+ String chatRoomID, ChatRoomQuery query, ProtocolProviderService pps,
+ boolean isAutoJoin)
+ {
+ super(chatRoomName, chatRoomID, query, pps);
+
+ this.isAutoJoin = isAutoJoin;
+
+ initContactProperties(getChatRoomStateByName());
+ }
+
+ /**
+ * Constructs new chat room source contact.
+ *
+ * @param chatRoom the chat room associated with the contact.
+ * @param query the query associated with the contact.
+ * @param isAutoJoin the auto join state
+ */
+ public ChatRoomSourceContact(ChatRoom chatRoom, ChatRoomQuery query,
+ boolean isAutoJoin)
+ {
+ super(chatRoom.getName(), chatRoom.getIdentifier(), query,
+ chatRoom.getParentProvider());
+ this.isAutoJoin = isAutoJoin;
+
+ initContactProperties(
+ chatRoom.isJoined()
+ ? ChatRoomPresenceStatus.CHAT_ROOM_ONLINE
+ : ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE);
+
+ }
+
+ /**
+ * Checks if the chat room associated with the contact is joined or not and
+ * returns it presence status.
+ *
+ * @return the presence status of the chat room associated with the contact.
+ */
+ private PresenceStatus getChatRoomStateByName()
+ {
+ for(ChatRoom room :
+ getProvider().getOperationSet(OperationSetMultiUserChat.class)
+ .getCurrentlyJoinedChatRooms())
+ {
+ if(room.getName().equals(getChatRoomName()))
+ {
+ return ChatRoomPresenceStatus.CHAT_ROOM_ONLINE;
+ }
+ }
+ return ChatRoomPresenceStatus.CHAT_ROOM_OFFLINE;
+ }
+
+ /**
+ * Returns the index of this source contact in its parent group.
+ *
+ * @return the index of this contact in its parent
+ */
+ @Override
+ public int getIndex()
+ {
+ return ((ChatRoomQuery)parentQuery).indexOf(this);
+ }
+
+ /**
+ * Returns the auto join state of the contact.
+ *
+ * @return the auto join state of the contact.
+ */
+ public boolean isAutoJoin()
+ {
+ return isAutoJoin;
+ }
+
+ /**
+ * Sets the auto join state of the contact.
+ *
+ * @param isAutoJoin the auto join state to be set.
+ */
+ public void setAutoJoin(boolean isAutoJoin)
+ {
+ this.isAutoJoin = isAutoJoin;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/muc/MUCCustomContactActionService.java b/src/net/java/sip/communicator/impl/muc/MUCCustomContactActionService.java
index 7c45b1d..0f4622d 100644
--- a/src/net/java/sip/communicator/impl/muc/MUCCustomContactActionService.java
+++ b/src/net/java/sip/communicator/impl/muc/MUCCustomContactActionService.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,845 +15,845 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.muc;
-
-import java.util.*;
-
-import net.java.sip.communicator.plugin.desktoputil.chat.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.customcontactactions.*;
-import net.java.sip.communicator.service.muc.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.service.resources.*;
-
-/**
- * Implements <tt>CustomContactActionsService</tt> for MUC contact source.
- *
- * @author Hristo Terezov
- */
-public class MUCCustomContactActionService
- implements CustomContactActionsService<SourceContact>
-{
- /**
- * List of custom menu items.
- */
- private final List<ContactActionMenuItem<SourceContact>> mucActionMenuItems
- = new LinkedList<ContactActionMenuItem<SourceContact>>();
-
- /**
- * List of custom actions.
- */
- private final List<ContactAction<SourceContact>> mucActions
- = new LinkedList<ContactAction<SourceContact>>();
-
- /**
- *
- */
- private static final String OWNER_CANT_REMOVE_CHATROOM_PROPERTY
- = "net.java.sip.communicator.impl.muc.OWNER_CANT_REMOVE_CHATROOM";
-
- /**
- * Array of names for the custom actions.
- */
- private String[] actionsNames = {
- "leave",
- "join",
- "autojoin",
- "autojoin_pressed",
- "destroy_chatroom"
- };
-
- /**
- * Array of labels for the custom actions.
- */
- private String[] actionsLabels = {
- "service.gui.LEAVE",
- "service.gui.JOIN",
- "service.gui.JOIN_AUTOMATICALLY",
- "service.gui.JOIN_AUTOMATICALLY",
- "service.gui.DESTROY_CHATROOM"
- };
-
- /**
- * Array of icons for the custom actions.
- */
- private String[] actionsIcons = {
- "service.gui.icons.LEAVE_ICON_BUTTON",
- "service.gui.icons.JOIN_ICON_BUTTON",
- "service.gui.icons.AUTOJOIN_ON_ICON_BUTTON",
- "service.gui.icons.AUTOJOIN_OFF_ICON_BUTTON",
- "service.gui.icons.DESTROY_ICON_BUTTON"
- };
-
- /**
- * Array of rollover icons for the custom actions.
- */
- private String[] actionsIconsRollover = {
- "service.gui.icons.LEAVE_ICON_ROLLOVER_BUTTON",
- "service.gui.icons.JOIN_ICON_ROLLOVER_BUTTON",
- "service.gui.icons.AUTOJOIN_ON_ICON_ROLLOVER_BUTTON",
- "service.gui.icons.AUTOJOIN_OFF_ICON_ROLLOVER_BUTTON",
- "service.gui.icons.DESTROY_ICON_ROLLOVER_BUTTON"
- };
-
- /**
- * Array of pressed icons for the custom actions.
- */
- private String[] actionsIconsPressed = {
- "service.gui.icons.LEAVE_ICON_PRESSED_BUTTON",
- "service.gui.icons.JOIN_ICON_PRESSED_BUTTON",
- "service.gui.icons.AUTOJOIN_ON_ICON_PRESSED_BUTTON",
- "service.gui.icons.AUTOJOIN_OFF_ICON_PRESSED_BUTTON",
- "service.gui.icons.DESTROY_ICON_PRESSED_BUTTON"
- };
-
- /**
- * Array of names for the custom menu items.
- */
- private String[] menuActionsNames = {
- "open",
- "join",
- "join_as",
- "leave",
- "remove",
- "change_nick",
- "autojoin",
- "autojoin_pressed",
- "open_automatically",
- "destroy_chatroom"
- };
-
- /**
- * Array of labels for the custom menu items.
- */
- private String[] menuActionsLabels = {
- "service.gui.OPEN",
- "service.gui.JOIN",
- "service.gui.JOIN_AS",
- "service.gui.LEAVE",
- "service.gui.REMOVE",
- "service.gui.CHANGE_NICK",
- "service.gui.JOIN_AUTOMATICALLY",
- "service.gui.DONT_JOIN_AUTOMATICALLY",
- "service.gui.OPEN_AUTOMATICALLY",
- "service.gui.DESTROY_CHATROOM"
- };
-
- /**
- * Array of icons for the custom menu items.
- */
- private String[] menuActionsIcons = {
- "service.gui.icons.CHAT_ROOM_16x16_ICON",
- "service.gui.icons.JOIN_ICON",
- "service.gui.icons.JOIN_AS_ICON",
- "service.gui.icons.LEAVE_ICON",
- "service.gui.icons.REMOVE_CHAT_ICON",
- "service.gui.icons.RENAME_16x16_ICON",
- "service.gui.icons.AUTOJOIN",
- "service.gui.icons.AUTOJOIN",
- "service.gui.icons.OPEN_AUTOMATICALLY",
- "service.gui.icons.DESTROY_CHATROOM"
- };
-
- /**
- * A runnable that leaves the chat room.
- */
- private MUCCustomActionRunnable leaveRunnable
- = new MUCCustomActionRunnable()
- {
-
- @Override
- public void run()
- {
- ChatRoomWrapper leavedRoomWrapped
- = MUCActivator.getMUCService().leaveChatRoom(
- chatRoomWrapper);
- if(leavedRoomWrapped != null)
- MUCActivator.getUIService().closeChatRoomWindow(
- leavedRoomWrapped);
- }
- };
-
- /**
- * A runnable that joins the chat room.
- */
- private MUCCustomActionRunnable joinRunnable
- = new MUCCustomActionRunnable()
- {
-
- @Override
- public void run()
- {
- String[] joinOptions;
- String subject = null;
- String nickName = null;
-
- nickName =
- ConfigurationUtils.getChatRoomProperty(
- chatRoomWrapper.getParentProvider()
- .getProtocolProvider(), chatRoomWrapper
- .getChatRoomID(), "userNickName");
- if(nickName == null)
- {
- joinOptions = ChatRoomJoinOptionsDialog.getJoinOptions(
- chatRoomWrapper.getParentProvider()
- .getProtocolProvider(),
- chatRoomWrapper.getChatRoomID(),
- MUCActivator.getGlobalDisplayDetailsService()
- .getDisplayName(chatRoomWrapper.getParentProvider()
- .getProtocolProvider()));
- nickName = joinOptions[0];
- subject = joinOptions[1];
- }
-
- if (nickName != null)
- MUCActivator.getMUCService().joinChatRoom(chatRoomWrapper,
- nickName, null, subject);
- }
- };
-
- /**
- * A runnable that sets / unsets auto join setting of the chat room.
- */
- private MUCCustomActionRunnable autoJoinRunnable
- = new MUCCustomActionRunnable()
- {
-
- @Override
- public void run()
- {
- chatRoomWrapper.setAutoJoin(!chatRoomWrapper.isAutojoin());
-
- }
- };
-
- /**
- * A runnable that destroys the chat room.
- */
- private MUCCustomActionRunnable destroyActionRunnable
- = new MUCCustomActionRunnable()
- {
-
- @Override
- public void run()
- {
- String destroyOptions[]
- = ChatRoomDestroyReasonDialog.getDestroyOptions();
- if(destroyOptions == null)
- return;
-
- MUCActivator.getMUCService().destroyChatRoom(chatRoomWrapper,
- destroyOptions[0], destroyOptions[1]);
-
- }
- };
-
- /**
- * Array of <tt>MUCCustomActionRunnable</tt> objects for the custom menu
- * items. They will be executed when the item is pressed.
- */
- private MUCCustomActionRunnable[] actionsRunnable = {
- leaveRunnable,
- joinRunnable,
- autoJoinRunnable,
- autoJoinRunnable,
- destroyActionRunnable
- };
-
- /**
- * Array of <tt>MUCCustomActionRunnable</tt> objects for the custom menu
- * items. They will be executed when the item is pressed.
- */
- private MUCCustomActionRunnable[] menuActionsRunnable = {
- new MUCCustomActionRunnable()
- {
- @Override
- public void run()
- {
- MUCActivator.getMUCService().openChatRoom(chatRoomWrapper);
- }
- },
- joinRunnable,
- new MUCCustomActionRunnable()
- {
-
- @Override
- public void run()
- {
- String[] joinOptions;
- joinOptions = ChatRoomJoinOptionsDialog.getJoinOptions(
- chatRoomWrapper.getParentProvider().getProtocolProvider(),
- chatRoomWrapper.getChatRoomID(),
- MUCActivator.getGlobalDisplayDetailsService()
- .getDisplayName(chatRoomWrapper.getParentProvider()
- .getProtocolProvider()));
- if(joinOptions[0] == null)
- return;
- MUCActivator.getMUCService()
- .joinChatRoom(chatRoomWrapper, joinOptions[0], null,
- joinOptions[1]);
- }
- },
- leaveRunnable,
- new MUCCustomActionRunnable()
- {
-
- @Override
- public void run()
- {
- ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
-
- if (chatRoom != null)
- {
- ChatRoomWrapper leavedRoomWrapped
- = MUCActivator.getMUCService().leaveChatRoom(
- chatRoomWrapper);
- if(leavedRoomWrapped != null)
- MUCActivator.getUIService().closeChatRoomWindow(
- leavedRoomWrapped);
- }
-
- MUCActivator.getUIService().closeChatRoomWindow(chatRoomWrapper);
-
- MUCActivator.getMUCService().removeChatRoom(chatRoomWrapper);
- }
- },
- new MUCCustomActionRunnable()
- {
-
- @Override
- public void run()
- {
- ChatRoomJoinOptionsDialog.getJoinOptions(true,
- chatRoomWrapper.getParentProvider().getProtocolProvider(),
- chatRoomWrapper.getChatRoomID(),
- MUCActivator.getGlobalDisplayDetailsService()
- .getDisplayName(chatRoomWrapper.getParentProvider()
- .getProtocolProvider()));
- }
- },
- autoJoinRunnable,
- autoJoinRunnable,
- new MUCCustomActionRunnable()
- {
-
- @Override
- public void run()
- {
- MUCActivator.getUIService().showChatRoomAutoOpenConfigDialog(
- chatRoomWrapper.getParentProvider().getProtocolProvider(),
- chatRoomWrapper.getChatRoomID());
- }
- },
- destroyActionRunnable
- };
-
- /**
- * Array of <tt>EnableChecker</tt> objects for the custom menu items. They
- * are used to check if the item is enabled or disabled.
- */
- private EnableChecker[] actionsEnabledCheckers = {
- null,
- new JoinEnableChecker(),
- new JoinEnableChecker(),
- new LeaveEnableChecker(),
- null,
- null,
- null,
- null,
- null,
- null
- };
-
- /**
- * The resource management service instance.
- */
- ResourceManagementService resources = MUCActivator.getResources();
-
- /**
- * Constructs the custom actions.
- */
- public MUCCustomContactActionService()
- {
- for(int i = 0; i < menuActionsLabels.length; i++)
- {
- MUCActionMenuItems item
- = new MUCActionMenuItems(
- menuActionsNames[i],
- menuActionsLabels[i],
- menuActionsIcons[i],
- menuActionsRunnable[i]);
- mucActionMenuItems.add(item);
- if(actionsEnabledCheckers[i] != null)
- item.setEnabled(actionsEnabledCheckers[i]);
- }
-
- for(int i = 0; i < actionsLabels.length; i++)
- {
- MUCAction item = new MUCAction(
- actionsNames[i],
- actionsLabels[i],
- actionsIcons[i],
- actionsIconsRollover[i],
- actionsIconsPressed[i],
- actionsRunnable[i]);
- mucActions.add(item);
- }
-
- }
-
- /**
- * Returns the template class that this service has been initialized with
- *
- * @return the template class
- */
- public Class<SourceContact> getContactSourceClass()
- {
- return SourceContact.class;
- }
-
- @Override
- public Iterator<ContactActionMenuItem<SourceContact>>
- getCustomContactActionsMenuItems()
- {
- return mucActionMenuItems.iterator();
- }
-
-
- @Override
- public Iterator<ContactAction<SourceContact>> getCustomContactActions()
- {
- return mucActions.iterator();
- }
-
- /**
- * Implements the MUC custom action.
- */
- private class MUCAction
- implements ContactAction<SourceContact>
- {
- /**
- * The text of the action.
- */
- private String text;
-
- /**
- * The icon of the action
- */
- private byte[] icon;
-
- /**
- * The icon that is shown when the action is pressed.
- */
- private byte[] iconPressed;
-
- /**
- * The runnable that is executed when the action is pressed.
- */
- private MUCCustomActionRunnable actionPerformed;
-
- /**
- * The icon that is shown when the mouse is over the action.
- */
- private byte[] iconRollover;
-
- /**
- * The name of the action.
- */
- private String name;
-
- /**
- * Constructs <tt>MUCAction</tt> instance.
- *
- * @param textKey the key used to retrieve the label for the action.
- * @param iconKey the key used to retrieve the icon for the action.
- * @param actionPerformed the action executed when the action is
- * pressed.
- * @param iconRolloverKey the key used to retrieve the rollover icon for
- * the action.
- * @param iconPressedKey the key used to retrieve the pressed icon for
- * the action.
- */
- public MUCAction(String name, String textKey, String iconKey,
- String iconRolloverKey, String iconPressedKey,
- MUCCustomActionRunnable actionPerformed)
- {
- this.name = name;
- this.text = resources.getI18NString(textKey);
- this.icon = resources.getImageInBytes(iconKey);
- this.iconRollover = resources.getImageInBytes(iconRolloverKey);
- this.iconPressed = resources.getImageInBytes(iconPressedKey);
- this.actionPerformed = actionPerformed;
- }
-
- @Override
- public void actionPerformed(SourceContact actionSource, int x, int y)
- throws OperationFailedException
- {
- if(!(actionSource instanceof ChatRoomSourceContact))
- return;
- actionPerformed.setContact(actionSource);
- new Thread(actionPerformed).start();
- }
-
- @Override
- public byte[] getIcon()
- {
- return icon;
- }
-
- @Override
- public byte[] getRolloverIcon()
- {
- return iconRollover;
- }
-
- @Override
- public byte[] getPressedIcon()
- {
- return iconPressed;
- }
-
- @Override
- public String getToolTipText()
- {
- return text;
- }
-
- @Override
- public boolean isVisible(SourceContact actionSource)
- {
- if(actionSource instanceof ChatRoomSourceContact)
- {
- if(name.equals("leave"))
- {
- return actionsEnabledCheckers[3].check(actionSource);
- }
- else if(name.equals("join"))
- {
- return actionsEnabledCheckers[1].check(actionSource);
- }
- else if(name.equals("destroy_chatroom"))
- {
- ChatRoomSourceContact contact
- = (ChatRoomSourceContact) actionSource;
- ChatRoomWrapper room = MUCActivator.getMUCService()
- .findChatRoomWrapperFromSourceContact(contact);
- if(room == null || room.getChatRoom() == null)
- return false;
- if(room.getChatRoom().getUserRole().equals(ChatRoomMemberRole.OWNER))
- return true;
- return false;
- }
- else
- {
- ChatRoomSourceContact contact
- = (ChatRoomSourceContact) actionSource;
- ChatRoomWrapper room = MUCActivator.getMUCService()
- .findChatRoomWrapperFromSourceContact(contact);
- if(room == null)
- return false;
-
- if(name.equals("autojoin"))
- return room.isAutojoin();
- else if(name.equals("autojoin_pressed"))
- return !room.isAutojoin();
- }
- }
- return false;
- }
-
- }
-
- /**
- * Implements the MUC custom menu items.
- */
- private class MUCActionMenuItems
- implements ContactActionMenuItem<SourceContact>
- {
- /**
- * The label for the menu item.
- */
- private String text;
-
- /**
- * The the icon for the menu item.
- */
- private byte[] image;
-
- /**
- * The action executed when the menu item is pressed.
- */
- private MUCCustomActionRunnable actionPerformed;
-
- /**
- * Object that is used to check if the item is enabled or disabled.
- */
- private EnableChecker enabled;
-
- /**
- * The name of the custom action menu item.
- */
- private String name;
-
- /**
- * The mnemonic for the action.
- */
- private char mnemonics;
-
- /**
- * Constructs <tt>MUCActionMenuItems</tt> instance.
- *
- * @param textKey the key used to retrieve the label for the menu item.
- * @param imageKey the key used to retrieve the icon for the menu item.
- * @param actionPerformed the action executed when the menu item is
- * pressed.
- */
- public MUCActionMenuItems(String name, String textKey, String imageKey,
- MUCCustomActionRunnable actionPerformed)
- {
- this.text = resources.getI18NString(textKey);
- this.image = (imageKey == null)? null :
- resources.getImageInBytes(imageKey);
- this.actionPerformed = actionPerformed;
- this.enabled = new EnableChecker();
- this.name = name;
- this.mnemonics = resources.getI18nMnemonic(textKey);
- }
-
- @Override
- public void actionPerformed(SourceContact actionSource)
- throws OperationFailedException
- {
- if(!(actionSource instanceof ChatRoomSourceContact))
- return;
- actionPerformed.setContact(actionSource);
- new Thread(actionPerformed).start();
- }
-
- @Override
- public byte[] getIcon()
- {
- return image;
- }
-
-
- @Override
- public String getText(SourceContact actionSource)
- {
- if(!(actionSource instanceof ChatRoomSourceContact))
- return "";
-
- if(!name.equals("open_automatically"))
- return text;
-
- String openAutomaticallyValue
- = MUCService.getChatRoomAutoOpenOption(
- ((ChatRoomSourceContact)actionSource).getProvider(),
- ((ChatRoomSourceContact)actionSource).getChatRoomID());
- if(openAutomaticallyValue == null)
- openAutomaticallyValue = MUCService.DEFAULT_AUTO_OPEN_BEHAVIOUR;
- String openAutomaticallyKey = MUCService.autoOpenConfigValuesTexts
- .get(openAutomaticallyValue);
- return "<html>" + text + "...<br><font size=\"2\"><center> ("
- + resources.getI18NString(openAutomaticallyKey)
- + ")</center></font></html>";
- }
-
- @Override
- public boolean isVisible(SourceContact actionSource)
- {
- if(!(actionSource instanceof ChatRoomSourceContact))
- return false;
-
- ChatRoomSourceContact contact
- = (ChatRoomSourceContact) actionSource;
- ChatRoomWrapper room = MUCActivator.getMUCService()
- .findChatRoomWrapperFromSourceContact(contact);
- if(name.equals("autojoin") || name.equals("autojoin_pressed"))
- {
- if(room == null)
- return true;
-
- if(name.equals("autojoin"))
- return !room.isAutojoin();
-
- if(name.equals("autojoin_pressed"))
- return room.isAutojoin();
- }
- else if(name.equals("remove"))
- {
- if(room == null || room.getChatRoom() == null)
- return true;
-
- boolean ownerCannotRemoveRoom
- = MUCActivator.getConfigurationService().getBoolean(
- OWNER_CANT_REMOVE_CHATROOM_PROPERTY, false);
-
- // when joined role will be owner or member
- // when not joined and if we never has entered the room role
- // will be guest, if we joined and left the room the role
- // will be owner or member
- if(room.getChatRoom().getUserRole().equals(
- ChatRoomMemberRole.MEMBER))
- {
- return true;
- }
- else
- {
- if(ownerCannotRemoveRoom)
- return false;
- else
- return true;
- }
- }
- else if(name.equals("destroy_chatroom"))
- {
- if(room == null || room.getChatRoom() == null)
- return false;
- if(room.getChatRoom().getUserRole().equals(
- ChatRoomMemberRole.OWNER))
- return true;
- return false;
- }
- return true;
- }
-
- @Override
- public char getMnemonics()
- {
- return mnemonics;
- }
-
- @Override
- public boolean isEnabled(SourceContact actionSource)
- {
- return enabled.check(actionSource);
- }
-
- /**
- * Sets <tt>EnabledChecker</tt> instance that will be used to check if
- * the item should be enabled or disabled.
- *
- * @param enabled the <tt>EnabledChecker</tt> instance.
- */
- public void setEnabled(EnableChecker enabled)
- {
- this.enabled = enabled;
- }
-
- @Override
- public boolean isCheckBox()
- {
- return false;
- }
-
- @Override
- public boolean isSelected(SourceContact contact)
- {
- ChatRoomWrapper chatRoomWrapper = MUCActivator.getMUCService()
- .findChatRoomWrapperFromSourceContact(contact);
- if(chatRoomWrapper == null)
- return false;
- return chatRoomWrapper.isAutojoin();
- }
-
- }
-
- /**
- * Checks if the menu item should be enabled or disabled. This is default
- * implementation. Always returns that the item should be enabled.
- */
- private static class EnableChecker
- {
- /**
- * Checks if the menu item should be enabled or disabled.
- *
- * @param contact the contact associated with the menu item.
- * @return always <tt>true</tt>
- */
- public boolean check(SourceContact contact)
- {
- return true;
- }
- }
-
- /**
- * Implements <tt>EnableChecker</tt> for the join menu items.
- */
- private static class JoinEnableChecker
- extends EnableChecker
- {
- /**
- * Checks if the menu item should be enabled or disabled.
- *
- * @param contact the contact associated with the menu item.
- * @return <tt>true</tt> if the item should be enabled and
- * <tt>false</tt> if not.
- */
- public boolean check(SourceContact contact)
- {
- ChatRoomWrapper chatRoomWrapper = MUCActivator.getMUCService()
- .findChatRoomWrapperFromSourceContact(contact);
- ChatRoom chatRoom = null;
- if(chatRoomWrapper != null)
- {
- chatRoom = chatRoomWrapper.getChatRoom();
- }
-
- if((chatRoom != null) && chatRoom.isJoined())
- return false;
- return true;
- }
- }
-
- /**
- * Implements <tt>EnableChecker</tt> for the leave menu item.
- */
- private static class LeaveEnableChecker
- extends JoinEnableChecker
- {
- /**
- * Checks if the menu item should be enabled or disabled.
- *
- * @param contact the contact associated with the menu item.
- * @return <tt>true</tt> if the item should be enabled and
- * <tt>false</tt> if not.
- */
- public boolean check(SourceContact contact)
- {
- return !super.check(contact);
- }
- }
-
- /**
- * Implements base properties for the MUC menu items.These properties are
- * used when the menu item is pressed.
- */
- private abstract class MUCCustomActionRunnable
- implements Runnable
- {
- /**
- * The contact associated with the menu item.
- */
- protected SourceContact contact;
-
- /**
- * The contact associated with the menu item.
- */
- protected ChatRoomWrapper chatRoomWrapper;
-
- /**
- * Sets the source contact.
- * @param contact the contact to set
- */
- public void setContact(SourceContact contact)
- {
- this.contact = contact;
- chatRoomWrapper = MUCActivator.getMUCService()
- .findChatRoomWrapperFromSourceContact(contact);
- }
- }
-}
+package net.java.sip.communicator.impl.muc;
+
+import java.util.*;
+
+import net.java.sip.communicator.plugin.desktoputil.chat.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.customcontactactions.*;
+import net.java.sip.communicator.service.muc.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+import org.jitsi.service.resources.*;
+
+/**
+ * Implements <tt>CustomContactActionsService</tt> for MUC contact source.
+ *
+ * @author Hristo Terezov
+ */
+public class MUCCustomContactActionService
+ implements CustomContactActionsService<SourceContact>
+{
+ /**
+ * List of custom menu items.
+ */
+ private final List<ContactActionMenuItem<SourceContact>> mucActionMenuItems
+ = new LinkedList<ContactActionMenuItem<SourceContact>>();
+
+ /**
+ * List of custom actions.
+ */
+ private final List<ContactAction<SourceContact>> mucActions
+ = new LinkedList<ContactAction<SourceContact>>();
+
+ /**
+ *
+ */
+ private static final String OWNER_CANT_REMOVE_CHATROOM_PROPERTY
+ = "net.java.sip.communicator.impl.muc.OWNER_CANT_REMOVE_CHATROOM";
+
+ /**
+ * Array of names for the custom actions.
+ */
+ private String[] actionsNames = {
+ "leave",
+ "join",
+ "autojoin",
+ "autojoin_pressed",
+ "destroy_chatroom"
+ };
+
+ /**
+ * Array of labels for the custom actions.
+ */
+ private String[] actionsLabels = {
+ "service.gui.LEAVE",
+ "service.gui.JOIN",
+ "service.gui.JOIN_AUTOMATICALLY",
+ "service.gui.JOIN_AUTOMATICALLY",
+ "service.gui.DESTROY_CHATROOM"
+ };
+
+ /**
+ * Array of icons for the custom actions.
+ */
+ private String[] actionsIcons = {
+ "service.gui.icons.LEAVE_ICON_BUTTON",
+ "service.gui.icons.JOIN_ICON_BUTTON",
+ "service.gui.icons.AUTOJOIN_ON_ICON_BUTTON",
+ "service.gui.icons.AUTOJOIN_OFF_ICON_BUTTON",
+ "service.gui.icons.DESTROY_ICON_BUTTON"
+ };
+
+ /**
+ * Array of rollover icons for the custom actions.
+ */
+ private String[] actionsIconsRollover = {
+ "service.gui.icons.LEAVE_ICON_ROLLOVER_BUTTON",
+ "service.gui.icons.JOIN_ICON_ROLLOVER_BUTTON",
+ "service.gui.icons.AUTOJOIN_ON_ICON_ROLLOVER_BUTTON",
+ "service.gui.icons.AUTOJOIN_OFF_ICON_ROLLOVER_BUTTON",
+ "service.gui.icons.DESTROY_ICON_ROLLOVER_BUTTON"
+ };
+
+ /**
+ * Array of pressed icons for the custom actions.
+ */
+ private String[] actionsIconsPressed = {
+ "service.gui.icons.LEAVE_ICON_PRESSED_BUTTON",
+ "service.gui.icons.JOIN_ICON_PRESSED_BUTTON",
+ "service.gui.icons.AUTOJOIN_ON_ICON_PRESSED_BUTTON",
+ "service.gui.icons.AUTOJOIN_OFF_ICON_PRESSED_BUTTON",
+ "service.gui.icons.DESTROY_ICON_PRESSED_BUTTON"
+ };
+
+ /**
+ * Array of names for the custom menu items.
+ */
+ private String[] menuActionsNames = {
+ "open",
+ "join",
+ "join_as",
+ "leave",
+ "remove",
+ "change_nick",
+ "autojoin",
+ "autojoin_pressed",
+ "open_automatically",
+ "destroy_chatroom"
+ };
+
+ /**
+ * Array of labels for the custom menu items.
+ */
+ private String[] menuActionsLabels = {
+ "service.gui.OPEN",
+ "service.gui.JOIN",
+ "service.gui.JOIN_AS",
+ "service.gui.LEAVE",
+ "service.gui.REMOVE",
+ "service.gui.CHANGE_NICK",
+ "service.gui.JOIN_AUTOMATICALLY",
+ "service.gui.DONT_JOIN_AUTOMATICALLY",
+ "service.gui.OPEN_AUTOMATICALLY",
+ "service.gui.DESTROY_CHATROOM"
+ };
+
+ /**
+ * Array of icons for the custom menu items.
+ */
+ private String[] menuActionsIcons = {
+ "service.gui.icons.CHAT_ROOM_16x16_ICON",
+ "service.gui.icons.JOIN_ICON",
+ "service.gui.icons.JOIN_AS_ICON",
+ "service.gui.icons.LEAVE_ICON",
+ "service.gui.icons.REMOVE_CHAT_ICON",
+ "service.gui.icons.RENAME_16x16_ICON",
+ "service.gui.icons.AUTOJOIN",
+ "service.gui.icons.AUTOJOIN",
+ "service.gui.icons.OPEN_AUTOMATICALLY",
+ "service.gui.icons.DESTROY_CHATROOM"
+ };
+
+ /**
+ * A runnable that leaves the chat room.
+ */
+ private MUCCustomActionRunnable leaveRunnable
+ = new MUCCustomActionRunnable()
+ {
+
+ @Override
+ public void run()
+ {
+ ChatRoomWrapper leavedRoomWrapped
+ = MUCActivator.getMUCService().leaveChatRoom(
+ chatRoomWrapper);
+ if(leavedRoomWrapped != null)
+ MUCActivator.getUIService().closeChatRoomWindow(
+ leavedRoomWrapped);
+ }
+ };
+
+ /**
+ * A runnable that joins the chat room.
+ */
+ private MUCCustomActionRunnable joinRunnable
+ = new MUCCustomActionRunnable()
+ {
+
+ @Override
+ public void run()
+ {
+ String[] joinOptions;
+ String subject = null;
+ String nickName = null;
+
+ nickName =
+ ConfigurationUtils.getChatRoomProperty(
+ chatRoomWrapper.getParentProvider()
+ .getProtocolProvider(), chatRoomWrapper
+ .getChatRoomID(), "userNickName");
+ if(nickName == null)
+ {
+ joinOptions = ChatRoomJoinOptionsDialog.getJoinOptions(
+ chatRoomWrapper.getParentProvider()
+ .getProtocolProvider(),
+ chatRoomWrapper.getChatRoomID(),
+ MUCActivator.getGlobalDisplayDetailsService()
+ .getDisplayName(chatRoomWrapper.getParentProvider()
+ .getProtocolProvider()));
+ nickName = joinOptions[0];
+ subject = joinOptions[1];
+ }
+
+ if (nickName != null)
+ MUCActivator.getMUCService().joinChatRoom(chatRoomWrapper,
+ nickName, null, subject);
+ }
+ };
+
+ /**
+ * A runnable that sets / unsets auto join setting of the chat room.
+ */
+ private MUCCustomActionRunnable autoJoinRunnable
+ = new MUCCustomActionRunnable()
+ {
+
+ @Override
+ public void run()
+ {
+ chatRoomWrapper.setAutoJoin(!chatRoomWrapper.isAutojoin());
+
+ }
+ };
+
+ /**
+ * A runnable that destroys the chat room.
+ */
+ private MUCCustomActionRunnable destroyActionRunnable
+ = new MUCCustomActionRunnable()
+ {
+
+ @Override
+ public void run()
+ {
+ String destroyOptions[]
+ = ChatRoomDestroyReasonDialog.getDestroyOptions();
+ if(destroyOptions == null)
+ return;
+
+ MUCActivator.getMUCService().destroyChatRoom(chatRoomWrapper,
+ destroyOptions[0], destroyOptions[1]);
+
+ }
+ };
+
+ /**
+ * Array of <tt>MUCCustomActionRunnable</tt> objects for the custom menu
+ * items. They will be executed when the item is pressed.
+ */
+ private MUCCustomActionRunnable[] actionsRunnable = {
+ leaveRunnable,
+ joinRunnable,
+ autoJoinRunnable,
+ autoJoinRunnable,
+ destroyActionRunnable
+ };
+
+ /**
+ * Array of <tt>MUCCustomActionRunnable</tt> objects for the custom menu
+ * items. They will be executed when the item is pressed.
+ */
+ private MUCCustomActionRunnable[] menuActionsRunnable = {
+ new MUCCustomActionRunnable()
+ {
+ @Override
+ public void run()
+ {
+ MUCActivator.getMUCService().openChatRoom(chatRoomWrapper);
+ }
+ },
+ joinRunnable,
+ new MUCCustomActionRunnable()
+ {
+
+ @Override
+ public void run()
+ {
+ String[] joinOptions;
+ joinOptions = ChatRoomJoinOptionsDialog.getJoinOptions(
+ chatRoomWrapper.getParentProvider().getProtocolProvider(),
+ chatRoomWrapper.getChatRoomID(),
+ MUCActivator.getGlobalDisplayDetailsService()
+ .getDisplayName(chatRoomWrapper.getParentProvider()
+ .getProtocolProvider()));
+ if(joinOptions[0] == null)
+ return;
+ MUCActivator.getMUCService()
+ .joinChatRoom(chatRoomWrapper, joinOptions[0], null,
+ joinOptions[1]);
+ }
+ },
+ leaveRunnable,
+ new MUCCustomActionRunnable()
+ {
+
+ @Override
+ public void run()
+ {
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+
+ if (chatRoom != null)
+ {
+ ChatRoomWrapper leavedRoomWrapped
+ = MUCActivator.getMUCService().leaveChatRoom(
+ chatRoomWrapper);
+ if(leavedRoomWrapped != null)
+ MUCActivator.getUIService().closeChatRoomWindow(
+ leavedRoomWrapped);
+ }
+
+ MUCActivator.getUIService().closeChatRoomWindow(chatRoomWrapper);
+
+ MUCActivator.getMUCService().removeChatRoom(chatRoomWrapper);
+ }
+ },
+ new MUCCustomActionRunnable()
+ {
+
+ @Override
+ public void run()
+ {
+ ChatRoomJoinOptionsDialog.getJoinOptions(true,
+ chatRoomWrapper.getParentProvider().getProtocolProvider(),
+ chatRoomWrapper.getChatRoomID(),
+ MUCActivator.getGlobalDisplayDetailsService()
+ .getDisplayName(chatRoomWrapper.getParentProvider()
+ .getProtocolProvider()));
+ }
+ },
+ autoJoinRunnable,
+ autoJoinRunnable,
+ new MUCCustomActionRunnable()
+ {
+
+ @Override
+ public void run()
+ {
+ MUCActivator.getUIService().showChatRoomAutoOpenConfigDialog(
+ chatRoomWrapper.getParentProvider().getProtocolProvider(),
+ chatRoomWrapper.getChatRoomID());
+ }
+ },
+ destroyActionRunnable
+ };
+
+ /**
+ * Array of <tt>EnableChecker</tt> objects for the custom menu items. They
+ * are used to check if the item is enabled or disabled.
+ */
+ private EnableChecker[] actionsEnabledCheckers = {
+ null,
+ new JoinEnableChecker(),
+ new JoinEnableChecker(),
+ new LeaveEnableChecker(),
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ };
+
+ /**
+ * The resource management service instance.
+ */
+ ResourceManagementService resources = MUCActivator.getResources();
+
+ /**
+ * Constructs the custom actions.
+ */
+ public MUCCustomContactActionService()
+ {
+ for(int i = 0; i < menuActionsLabels.length; i++)
+ {
+ MUCActionMenuItems item
+ = new MUCActionMenuItems(
+ menuActionsNames[i],
+ menuActionsLabels[i],
+ menuActionsIcons[i],
+ menuActionsRunnable[i]);
+ mucActionMenuItems.add(item);
+ if(actionsEnabledCheckers[i] != null)
+ item.setEnabled(actionsEnabledCheckers[i]);
+ }
+
+ for(int i = 0; i < actionsLabels.length; i++)
+ {
+ MUCAction item = new MUCAction(
+ actionsNames[i],
+ actionsLabels[i],
+ actionsIcons[i],
+ actionsIconsRollover[i],
+ actionsIconsPressed[i],
+ actionsRunnable[i]);
+ mucActions.add(item);
+ }
+
+ }
+
+ /**
+ * Returns the template class that this service has been initialized with
+ *
+ * @return the template class
+ */
+ public Class<SourceContact> getContactSourceClass()
+ {
+ return SourceContact.class;
+ }
+
+ @Override
+ public Iterator<ContactActionMenuItem<SourceContact>>
+ getCustomContactActionsMenuItems()
+ {
+ return mucActionMenuItems.iterator();
+ }
+
+
+ @Override
+ public Iterator<ContactAction<SourceContact>> getCustomContactActions()
+ {
+ return mucActions.iterator();
+ }
+
+ /**
+ * Implements the MUC custom action.
+ */
+ private class MUCAction
+ implements ContactAction<SourceContact>
+ {
+ /**
+ * The text of the action.
+ */
+ private String text;
+
+ /**
+ * The icon of the action
+ */
+ private byte[] icon;
+
+ /**
+ * The icon that is shown when the action is pressed.
+ */
+ private byte[] iconPressed;
+
+ /**
+ * The runnable that is executed when the action is pressed.
+ */
+ private MUCCustomActionRunnable actionPerformed;
+
+ /**
+ * The icon that is shown when the mouse is over the action.
+ */
+ private byte[] iconRollover;
+
+ /**
+ * The name of the action.
+ */
+ private String name;
+
+ /**
+ * Constructs <tt>MUCAction</tt> instance.
+ *
+ * @param textKey the key used to retrieve the label for the action.
+ * @param iconKey the key used to retrieve the icon for the action.
+ * @param actionPerformed the action executed when the action is
+ * pressed.
+ * @param iconRolloverKey the key used to retrieve the rollover icon for
+ * the action.
+ * @param iconPressedKey the key used to retrieve the pressed icon for
+ * the action.
+ */
+ public MUCAction(String name, String textKey, String iconKey,
+ String iconRolloverKey, String iconPressedKey,
+ MUCCustomActionRunnable actionPerformed)
+ {
+ this.name = name;
+ this.text = resources.getI18NString(textKey);
+ this.icon = resources.getImageInBytes(iconKey);
+ this.iconRollover = resources.getImageInBytes(iconRolloverKey);
+ this.iconPressed = resources.getImageInBytes(iconPressedKey);
+ this.actionPerformed = actionPerformed;
+ }
+
+ @Override
+ public void actionPerformed(SourceContact actionSource, int x, int y)
+ throws OperationFailedException
+ {
+ if(!(actionSource instanceof ChatRoomSourceContact))
+ return;
+ actionPerformed.setContact(actionSource);
+ new Thread(actionPerformed).start();
+ }
+
+ @Override
+ public byte[] getIcon()
+ {
+ return icon;
+ }
+
+ @Override
+ public byte[] getRolloverIcon()
+ {
+ return iconRollover;
+ }
+
+ @Override
+ public byte[] getPressedIcon()
+ {
+ return iconPressed;
+ }
+
+ @Override
+ public String getToolTipText()
+ {
+ return text;
+ }
+
+ @Override
+ public boolean isVisible(SourceContact actionSource)
+ {
+ if(actionSource instanceof ChatRoomSourceContact)
+ {
+ if(name.equals("leave"))
+ {
+ return actionsEnabledCheckers[3].check(actionSource);
+ }
+ else if(name.equals("join"))
+ {
+ return actionsEnabledCheckers[1].check(actionSource);
+ }
+ else if(name.equals("destroy_chatroom"))
+ {
+ ChatRoomSourceContact contact
+ = (ChatRoomSourceContact) actionSource;
+ ChatRoomWrapper room = MUCActivator.getMUCService()
+ .findChatRoomWrapperFromSourceContact(contact);
+ if(room == null || room.getChatRoom() == null)
+ return false;
+ if(room.getChatRoom().getUserRole().equals(ChatRoomMemberRole.OWNER))
+ return true;
+ return false;
+ }
+ else
+ {
+ ChatRoomSourceContact contact
+ = (ChatRoomSourceContact) actionSource;
+ ChatRoomWrapper room = MUCActivator.getMUCService()
+ .findChatRoomWrapperFromSourceContact(contact);
+ if(room == null)
+ return false;
+
+ if(name.equals("autojoin"))
+ return room.isAutojoin();
+ else if(name.equals("autojoin_pressed"))
+ return !room.isAutojoin();
+ }
+ }
+ return false;
+ }
+
+ }
+
+ /**
+ * Implements the MUC custom menu items.
+ */
+ private class MUCActionMenuItems
+ implements ContactActionMenuItem<SourceContact>
+ {
+ /**
+ * The label for the menu item.
+ */
+ private String text;
+
+ /**
+ * The the icon for the menu item.
+ */
+ private byte[] image;
+
+ /**
+ * The action executed when the menu item is pressed.
+ */
+ private MUCCustomActionRunnable actionPerformed;
+
+ /**
+ * Object that is used to check if the item is enabled or disabled.
+ */
+ private EnableChecker enabled;
+
+ /**
+ * The name of the custom action menu item.
+ */
+ private String name;
+
+ /**
+ * The mnemonic for the action.
+ */
+ private char mnemonics;
+
+ /**
+ * Constructs <tt>MUCActionMenuItems</tt> instance.
+ *
+ * @param textKey the key used to retrieve the label for the menu item.
+ * @param imageKey the key used to retrieve the icon for the menu item.
+ * @param actionPerformed the action executed when the menu item is
+ * pressed.
+ */
+ public MUCActionMenuItems(String name, String textKey, String imageKey,
+ MUCCustomActionRunnable actionPerformed)
+ {
+ this.text = resources.getI18NString(textKey);
+ this.image = (imageKey == null)? null :
+ resources.getImageInBytes(imageKey);
+ this.actionPerformed = actionPerformed;
+ this.enabled = new EnableChecker();
+ this.name = name;
+ this.mnemonics = resources.getI18nMnemonic(textKey);
+ }
+
+ @Override
+ public void actionPerformed(SourceContact actionSource)
+ throws OperationFailedException
+ {
+ if(!(actionSource instanceof ChatRoomSourceContact))
+ return;
+ actionPerformed.setContact(actionSource);
+ new Thread(actionPerformed).start();
+ }
+
+ @Override
+ public byte[] getIcon()
+ {
+ return image;
+ }
+
+
+ @Override
+ public String getText(SourceContact actionSource)
+ {
+ if(!(actionSource instanceof ChatRoomSourceContact))
+ return "";
+
+ if(!name.equals("open_automatically"))
+ return text;
+
+ String openAutomaticallyValue
+ = MUCService.getChatRoomAutoOpenOption(
+ ((ChatRoomSourceContact)actionSource).getProvider(),
+ ((ChatRoomSourceContact)actionSource).getChatRoomID());
+ if(openAutomaticallyValue == null)
+ openAutomaticallyValue = MUCService.DEFAULT_AUTO_OPEN_BEHAVIOUR;
+ String openAutomaticallyKey = MUCService.autoOpenConfigValuesTexts
+ .get(openAutomaticallyValue);
+ return "<html>" + text + "...<br><font size=\"2\"><center> ("
+ + resources.getI18NString(openAutomaticallyKey)
+ + ")</center></font></html>";
+ }
+
+ @Override
+ public boolean isVisible(SourceContact actionSource)
+ {
+ if(!(actionSource instanceof ChatRoomSourceContact))
+ return false;
+
+ ChatRoomSourceContact contact
+ = (ChatRoomSourceContact) actionSource;
+ ChatRoomWrapper room = MUCActivator.getMUCService()
+ .findChatRoomWrapperFromSourceContact(contact);
+ if(name.equals("autojoin") || name.equals("autojoin_pressed"))
+ {
+ if(room == null)
+ return true;
+
+ if(name.equals("autojoin"))
+ return !room.isAutojoin();
+
+ if(name.equals("autojoin_pressed"))
+ return room.isAutojoin();
+ }
+ else if(name.equals("remove"))
+ {
+ if(room == null || room.getChatRoom() == null)
+ return true;
+
+ boolean ownerCannotRemoveRoom
+ = MUCActivator.getConfigurationService().getBoolean(
+ OWNER_CANT_REMOVE_CHATROOM_PROPERTY, false);
+
+ // when joined role will be owner or member
+ // when not joined and if we never has entered the room role
+ // will be guest, if we joined and left the room the role
+ // will be owner or member
+ if(room.getChatRoom().getUserRole().equals(
+ ChatRoomMemberRole.MEMBER))
+ {
+ return true;
+ }
+ else
+ {
+ if(ownerCannotRemoveRoom)
+ return false;
+ else
+ return true;
+ }
+ }
+ else if(name.equals("destroy_chatroom"))
+ {
+ if(room == null || room.getChatRoom() == null)
+ return false;
+ if(room.getChatRoom().getUserRole().equals(
+ ChatRoomMemberRole.OWNER))
+ return true;
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public char getMnemonics()
+ {
+ return mnemonics;
+ }
+
+ @Override
+ public boolean isEnabled(SourceContact actionSource)
+ {
+ return enabled.check(actionSource);
+ }
+
+ /**
+ * Sets <tt>EnabledChecker</tt> instance that will be used to check if
+ * the item should be enabled or disabled.
+ *
+ * @param enabled the <tt>EnabledChecker</tt> instance.
+ */
+ public void setEnabled(EnableChecker enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public boolean isCheckBox()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isSelected(SourceContact contact)
+ {
+ ChatRoomWrapper chatRoomWrapper = MUCActivator.getMUCService()
+ .findChatRoomWrapperFromSourceContact(contact);
+ if(chatRoomWrapper == null)
+ return false;
+ return chatRoomWrapper.isAutojoin();
+ }
+
+ }
+
+ /**
+ * Checks if the menu item should be enabled or disabled. This is default
+ * implementation. Always returns that the item should be enabled.
+ */
+ private static class EnableChecker
+ {
+ /**
+ * Checks if the menu item should be enabled or disabled.
+ *
+ * @param contact the contact associated with the menu item.
+ * @return always <tt>true</tt>
+ */
+ public boolean check(SourceContact contact)
+ {
+ return true;
+ }
+ }
+
+ /**
+ * Implements <tt>EnableChecker</tt> for the join menu items.
+ */
+ private static class JoinEnableChecker
+ extends EnableChecker
+ {
+ /**
+ * Checks if the menu item should be enabled or disabled.
+ *
+ * @param contact the contact associated with the menu item.
+ * @return <tt>true</tt> if the item should be enabled and
+ * <tt>false</tt> if not.
+ */
+ public boolean check(SourceContact contact)
+ {
+ ChatRoomWrapper chatRoomWrapper = MUCActivator.getMUCService()
+ .findChatRoomWrapperFromSourceContact(contact);
+ ChatRoom chatRoom = null;
+ if(chatRoomWrapper != null)
+ {
+ chatRoom = chatRoomWrapper.getChatRoom();
+ }
+
+ if((chatRoom != null) && chatRoom.isJoined())
+ return false;
+ return true;
+ }
+ }
+
+ /**
+ * Implements <tt>EnableChecker</tt> for the leave menu item.
+ */
+ private static class LeaveEnableChecker
+ extends JoinEnableChecker
+ {
+ /**
+ * Checks if the menu item should be enabled or disabled.
+ *
+ * @param contact the contact associated with the menu item.
+ * @return <tt>true</tt> if the item should be enabled and
+ * <tt>false</tt> if not.
+ */
+ public boolean check(SourceContact contact)
+ {
+ return !super.check(contact);
+ }
+ }
+
+ /**
+ * Implements base properties for the MUC menu items.These properties are
+ * used when the menu item is pressed.
+ */
+ private abstract class MUCCustomActionRunnable
+ implements Runnable
+ {
+ /**
+ * The contact associated with the menu item.
+ */
+ protected SourceContact contact;
+
+ /**
+ * The contact associated with the menu item.
+ */
+ protected ChatRoomWrapper chatRoomWrapper;
+
+ /**
+ * Sets the source contact.
+ * @param contact the contact to set
+ */
+ public void setContact(SourceContact contact)
+ {
+ this.contact = contact;
+ chatRoomWrapper = MUCActivator.getMUCService()
+ .findChatRoomWrapperFromSourceContact(contact);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/muc/MUCServiceImpl.java b/src/net/java/sip/communicator/impl/muc/MUCServiceImpl.java
index f6dd1c7..be02255 100644
--- a/src/net/java/sip/communicator/impl/muc/MUCServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/muc/MUCServiceImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,1117 +15,1117 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.muc;
-
-import static net.java.sip.communicator.service.muc.ChatRoomWrapper.*;
-
-import java.util.*;
-
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.plugin.desktoputil.chat.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.muc.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.globalstatus.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.service.resources.*;
-
-/**
- * The <tt>MUCServiceImpl</tt> class implements the service for the chat rooms.
- *
- * @author Hristo Terezov
- */
-public class MUCServiceImpl
- extends MUCService
-{
-
- /**
- * The list of persistent chat rooms.
- */
- private final ChatRoomListImpl chatRoomList = new ChatRoomListImpl();
-
- /**
- * The <tt>Logger</tt> used by the <tt>MUCServiceImpl</tt> class and its
- * instances for logging output.
- */
- private static Logger logger = Logger.getLogger(MUCServiceImpl.class);
-
- /**
- * Called to accept an incoming invitation. Adds the invitation chat room
- * to the list of chat rooms and joins it.
- *
- * @param invitation the invitation to accept.
- */
- public void acceptInvitation(ChatRoomInvitation invitation)
- {
- ChatRoom chatRoom = invitation.getTargetChatRoom();
- byte[] password = invitation.getChatRoomPassword();
-
- String nickName =
- ConfigurationUtils.getChatRoomProperty(
- chatRoom.getParentProvider(),
- chatRoom.getIdentifier(), "userNickName");
- if(nickName == null)
- {
- String[] joinOptions = ChatRoomJoinOptionsDialog.getJoinOptions(
- true,
- chatRoom.getParentProvider(),
- chatRoom.getIdentifier(),
- MUCActivator.getGlobalDisplayDetailsService()
- .getDisplayName(chatRoom.getParentProvider()));
- nickName = joinOptions[0];
- }
-
- joinChatRoom(chatRoom, nickName, password);
- }
-
- /**
- * Adds a change listener to the <tt>ChatRoomList</tt>.
- *
- * @param l the listener.
- */
- public void addChatRoomListChangeListener(ChatRoomListChangeListener l)
- {
- chatRoomList.addChatRoomListChangeListener(l);
- }
-
- /**
- * Removes a change listener to the <tt>ChatRoomList</tt>.
- *
- * @param l the listener.
- */
- public void removeChatRoomListChangeListener(ChatRoomListChangeListener l)
- {
- chatRoomList.removeChatRoomListChangeListener(l);
- }
-
- /**
- * Fires a <tt>ChatRoomListChangedEvent</tt> event.
- *
- * @param chatRoomWrapper the chat room.
- * @param eventID the id of the event.
- */
- public void fireChatRoomListChangedEvent( ChatRoomWrapper chatRoomWrapper,
- int eventID)
- {
- chatRoomList.fireChatRoomListChangedEvent(chatRoomWrapper, eventID);
- }
-
- /**
- * Joins the given chat room with the given password and manages all the
- * exceptions that could occur during the join process.
- *
- * @param chatRoomWrapper the chat room to join.
- * @param nickName the nickname we choose for the given chat room.
- * @param password the password.
- * @param rememberPassword if true the password should be saved.
- * @param isFirstAttempt is this the first attempt to join room, used
- * to check whether to show some error messages
- * @param subject the subject which will be set to the room after the user
- * join successful.
- */
- private void joinChatRoom( ChatRoomWrapper chatRoomWrapper,
- String nickName,
- byte[] password,
- boolean rememberPassword,
- boolean isFirstAttempt,
- String subject)
- {
- ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
-
- if(chatRoom == null)
- {
- MUCActivator.getAlertUIService().showAlertDialog(
- MUCActivator.getResources().getI18NString("service.gui.WARNING"),
- MUCActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_NOT_CONNECTED",
- new String[]{chatRoomWrapper.getChatRoomName()}));
- return;
- }
-
- new JoinChatRoomTask(
- (ChatRoomWrapperImpl)chatRoomWrapper,
- nickName,
- password,
- rememberPassword,
- isFirstAttempt,
- subject)
- .start();
- }
-
- /**
- * Joins the given chat room with the given password and manages all the
- * exceptions that could occur during the join process.
- *
- * @param chatRoomWrapper the chat room to join.
- * @param nickName the nickname we choose for the given chat room.
- * @param password the password.
- */
- public void joinChatRoom( ChatRoomWrapper chatRoomWrapper,
- String nickName,
- byte[] password)
- {
- if (chatRoomWrapper.getChatRoom() == null)
- {
- chatRoomWrapper = createChatRoom(
- chatRoomWrapper.getChatRoomName(),
- chatRoomWrapper.getParentProvider().getProtocolProvider(),
- new ArrayList<String>(), "", false, false, true);
- }
-
- ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
-
- if(chatRoom == null)
- {
- MUCActivator.getAlertUIService().showAlertDialog(
- MUCActivator.getResources().getI18NString("service.gui.WARNING"),
- MUCActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_NOT_CONNECTED",
- new String[]{chatRoomWrapper.getChatRoomName()}));
- return;
- }
-
- new JoinChatRoomTask(
- (ChatRoomWrapperImpl)chatRoomWrapper, nickName, password)
- .start();
- }
-
- /**
- * Joins the given chat room with the given password and manages all the
- * exceptions that could occur during the join process.
- *
- * @param chatRoomWrapper the chat room to join.
- * @param nickName the nickname we choose for the given chat room.
- * @param password the password.
- * @param subject the subject which will be set to the room after the user
- * join successful.
- */
- public void joinChatRoom( ChatRoomWrapper chatRoomWrapper,
- String nickName,
- byte[] password,
- String subject)
- {
- if (chatRoomWrapper.getChatRoom() == null)
- {
- chatRoomWrapper = createChatRoom(
- chatRoomWrapper.getChatRoomName(),
- chatRoomWrapper.getParentProvider().getProtocolProvider(),
- new ArrayList<String>(), "", false, false, true);
- }
-
- ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
- if (chatRoom == null)
- {
- MUCActivator.getAlertUIService().showAlertDialog(
- MUCActivator.getResources().getI18NString("service.gui.WARNING"),
- MUCActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_NOT_CONNECTED",
- new String[]{chatRoomWrapper.getChatRoomName()}));
-
- return;
- }
-
- // join from add chat room dialog
-
- new JoinChatRoomTask(
- (ChatRoomWrapperImpl) chatRoomWrapper,
- nickName,
- password,
- subject)
- .start();
- }
-
-
- /**
- * Join chat room.
- * @param chatRoomWrapper
- */
- public void joinChatRoom(ChatRoomWrapper chatRoomWrapper)
- {
- if (chatRoomWrapper.getChatRoom() == null)
- {
- chatRoomWrapper = createChatRoom(
- chatRoomWrapper.getChatRoomName(),
- chatRoomWrapper.getParentProvider().getProtocolProvider(),
- new ArrayList<String>(), "", false, false, true);
- }
-
- ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
-
- if(chatRoom == null)
- {
- MUCActivator.getAlertUIService().showAlertDialog(
- MUCActivator.getResources().getI18NString("service.gui.WARNING"),
- MUCActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_NOT_CONNECTED",
- new String[]{chatRoomWrapper.getChatRoomName()}));
-
- return;
- }
-
- new JoinChatRoomTask((ChatRoomWrapperImpl)chatRoomWrapper, null, null)
- .start();
- }
-
-
- /**
- * Joins the given chat room and manages all the exceptions that could
- * occur during the join process.
- *
- * @param chatRoom the chat room to join
- * @param nickname the nickname we're using to join
- * @param password the password we're using to join
- */
- public void joinChatRoom( ChatRoom chatRoom,
- String nickname,
- byte[] password)
- {
- ChatRoomWrapper chatRoomWrapper
- = chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
-
- if(chatRoomWrapper == null)
- {
- ChatRoomProviderWrapper parentProvider
- = chatRoomList.findServerWrapperFromProvider(
- chatRoom.getParentProvider());
-
- chatRoomWrapper
- = new ChatRoomWrapperImpl(parentProvider, chatRoom);
-
- chatRoomList.addChatRoom(chatRoomWrapper);
-
- }
-
- this.joinChatRoom(chatRoomWrapper, nickname, password);
- }
-
- /**
- * Joins the room with the given name though the given chat room provider.
- *
- * @param chatRoomName the name of the room to join.
- * @param chatRoomProvider the chat room provider to join through.
- */
- public void joinChatRoom( String chatRoomName,
- ChatRoomProviderWrapper chatRoomProvider)
- {
- OperationSetMultiUserChat groupChatOpSet
- = chatRoomProvider
- .getProtocolProvider().getOperationSet(
- OperationSetMultiUserChat.class);
-
- ChatRoom chatRoom = null;
- try
- {
- chatRoom = groupChatOpSet.findRoom(chatRoomName);
- }
- catch (Exception e)
- {
- if (logger.isTraceEnabled())
- logger.trace("Un exception occurred while searching for room:"
- + chatRoomName, e);
- }
-
- if (chatRoom != null)
- {
- ChatRoomWrapper chatRoomWrapper
- = chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
-
- if(chatRoomWrapper == null)
- {
- ChatRoomProviderWrapper parentProvider
- = chatRoomList
- .findServerWrapperFromProvider(
- chatRoom.getParentProvider());
-
- chatRoomWrapper
- = new ChatRoomWrapperImpl(parentProvider, chatRoom);
-
- chatRoomList.addChatRoom(chatRoomWrapper);
-
- fireChatRoomListChangedEvent(
- chatRoomWrapper,
- ChatRoomListChangeEvent.CHAT_ROOM_ADDED);
- }
- joinChatRoom(chatRoomWrapper);
- }
- else
- MUCActivator.getAlertUIService().showAlertDialog(
- MUCActivator.getResources().getI18NString("service.gui.ERROR"),
- MUCActivator.getResources().getI18NString(
- "service.gui.CHAT_ROOM_NOT_EXIST",
- new String[]{chatRoomName,
- chatRoomProvider.getProtocolProvider()
- .getAccountID().getService()}));
- }
-
-
- /**
- * Creates a chat room, by specifying the chat room name, the parent
- * protocol provider and eventually, the contacts invited to participate in
- * this chat room.
- *
- * @param roomName the name of the room
- * @param protocolProvider the parent protocol provider.
- * @param contacts the contacts invited when creating the chat room.
- * @param reason
- * @param persistent is the room persistent
- * @param isPrivate whether the room will be private or public.
- * @return the <tt>ChatRoomWrapper</tt> corresponding to the created room
- */
- public ChatRoomWrapper createChatRoom(
- String roomName,
- ProtocolProviderService protocolProvider,
- Collection<String> contacts,
- String reason,
- boolean persistent,
- boolean isPrivate)
- {
- return createChatRoom(
- roomName, protocolProvider, contacts, reason, true, persistent,
- isPrivate);
- }
-
- /**
- * Creates a chat room, by specifying the chat room name, the parent
- * protocol provider and eventually, the contacts invited to participate in
- * this chat room.
- *
- * @param roomName the name of the room
- * @param protocolProvider the parent protocol provider.
- * @param contacts the contacts invited when creating the chat room.
- * @param reason
- * @param persistent is the room persistent
- * @return the <tt>ChatRoomWrapper</tt> corresponding to the created room
- */
- public ChatRoomWrapper createChatRoom(
- String roomName,
- ProtocolProviderService protocolProvider,
- Collection<String> contacts,
- String reason,
- boolean persistent)
- {
- return createChatRoom(
- roomName, protocolProvider, contacts, reason, true, persistent,
- false);
- }
-
- /**
- * Creates a chat room, by specifying the chat room name, the parent
- * protocol provider and eventually, the contacts invited to participate in
- * this chat room.
- *
- * @param roomName the name of the room
- * @param protocolProvider the parent protocol provider.
- * @param contacts the contacts invited when creating the chat room.
- * @param reason
- * @param join whether we should join the room after creating it.
- * @param persistent whether the newly created room will be persistent.
- * @param isPrivate whether the room will be private or public.
- * @return the <tt>ChatRoomWrapper</tt> corresponding to the created room or
- * <tt>null</tt> if the protocol fails to create the chat room.
- */
- public ChatRoomWrapper createChatRoom(
- String roomName,
- ProtocolProviderService protocolProvider,
- Collection<String> contacts,
- String reason,
- boolean join,
- boolean persistent,
- boolean isPrivate)
- {
- ChatRoomWrapper chatRoomWrapper = null;
- OperationSetMultiUserChat groupChatOpSet
- = protocolProvider.getOperationSet(OperationSetMultiUserChat.class);
-
- // If there's no group chat operation set we have nothing to do here.
- if (groupChatOpSet == null)
- return null;
-
- ChatRoom chatRoom = null;
- try
- {
-
-
- HashMap<String, Object> roomProperties =
- new HashMap<String, Object>();
- roomProperties.put("isPrivate", isPrivate);
- chatRoom = groupChatOpSet.createChatRoom(roomName, roomProperties);
-
- if(join)
- {
- chatRoom.join();
-
- for(String contact : contacts)
- chatRoom.invite(contact, reason);
- }
- }
- catch (OperationFailedException ex)
- {
- logger.error("Failed to create chat room.", ex);
-
- MUCActivator.getAlertUIService().showAlertDialog(
- MUCActivator.getResources().getI18NString("service.gui.ERROR"),
- MUCActivator.getResources().getI18NString(
- "service.gui.CREATE_CHAT_ROOM_ERROR",
- new String[]{protocolProvider.getProtocolDisplayName()}),
- ex);
- }
- catch (OperationNotSupportedException ex)
- {
- logger.error("Failed to create chat room.", ex);
-
- MUCActivator.getAlertUIService().showAlertDialog(
- MUCActivator.getResources().getI18NString("service.gui.ERROR"),
- MUCActivator.getResources().getI18NString(
- "service.gui.CREATE_CHAT_ROOM_ERROR",
- new String[]{protocolProvider.getProtocolDisplayName()}),
- ex);
- }
-
- if(chatRoom != null)
- {
- ChatRoomProviderWrapper parentProvider
- = chatRoomList.findServerWrapperFromProvider(protocolProvider);
-
- // if there is the same room ids don't add new wrapper as old one
- // maybe already created
- chatRoomWrapper =
- chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
-
- if(chatRoomWrapper == null)
- {
- chatRoomWrapper
- = new ChatRoomWrapperImpl(parentProvider, chatRoom);
- chatRoomWrapper.setPersistent(persistent);
- chatRoomList.addChatRoom(chatRoomWrapper);
- }
- }
-
- return chatRoomWrapper;
- }
-
- /**
- * Creates a private chat room, by specifying the parent
- * protocol provider and eventually, the contacts invited to participate in
- * this chat room.
- *
- * @param protocolProvider the parent protocol provider.
- * @param contacts the contacts invited when creating the chat room.
- * @param reason
- * @param persistent is the room persistent
- * @return the <tt>ChatRoomWrapper</tt> corresponding to the created room
- */
- public ChatRoomWrapper createPrivateChatRoom(
- ProtocolProviderService protocolProvider,
- Collection<String> contacts,
- String reason,
- boolean persistent)
- {
- return this.createChatRoom(
- null, protocolProvider, contacts, reason, persistent, true);
- }
-
-
- /**
- * Returns existing chat rooms for the given <tt>chatRoomProvider</tt>.
- * @param chatRoomProvider the <tt>ChatRoomProviderWrapper</tt>, which
- * chat rooms we're looking for
- * @return existing chat rooms for the given <tt>chatRoomProvider</tt>
- */
- public List<String> getExistingChatRooms(
- ChatRoomProviderWrapper chatRoomProvider)
- {
- if (chatRoomProvider == null)
- return null;
-
- ProtocolProviderService protocolProvider
- = chatRoomProvider.getProtocolProvider();
-
- if (protocolProvider == null)
- return null;
-
- OperationSetMultiUserChat groupChatOpSet
- = protocolProvider
- .getOperationSet(OperationSetMultiUserChat.class);
-
- if (groupChatOpSet == null)
- return null;
-
- List<String> chatRooms = null;
- try
- {
- chatRooms = groupChatOpSet.getExistingChatRooms();
- }
- catch (OperationFailedException e)
- {
- if (logger.isTraceEnabled())
- logger.trace("Failed to obtain existing chat rooms for server: "
- + protocolProvider.getAccountID().getService(), e);
- }
- catch (OperationNotSupportedException e)
- {
- if (logger.isTraceEnabled())
- logger.trace("Failed to obtain existing chat rooms for server: "
- + protocolProvider.getAccountID().getService(), e);
- }
-
- return chatRooms;
- }
-
- /**
- * Rejects the given invitation with the specified reason.
- *
- * @param multiUserChatOpSet the operation set to use for rejecting the
- * invitation
- * @param invitation the invitation to reject
- * @param reason the reason for the rejection
- */
- public void rejectInvitation( OperationSetMultiUserChat multiUserChatOpSet,
- ChatRoomInvitation invitation,
- String reason)
- {
- multiUserChatOpSet.rejectInvitation(invitation, reason);
- }
-
- /**
- * Leaves the given chat room.
- *
- * @param chatRoomWrapper the chat room to leave.
- * @return <tt>ChatRoomWrapper</tt> instance associated with the chat room.
- */
- public ChatRoomWrapper leaveChatRoom(ChatRoomWrapper chatRoomWrapper)
- {
- ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
-
- if (chatRoom == null)
- {
- ResourceManagementService resources = MUCActivator.getResources();
-
- MUCActivator.getAlertUIService().showAlertDialog(
- resources.getI18NString("service.gui.WARNING"),
- resources
- .getI18NString(
- "service.gui.CHAT_ROOM_LEAVE_NOT_CONNECTED"));
-
- return null;
- }
-
- if (chatRoom.isJoined())
- chatRoom.leave();
-
- ChatRoomWrapper existChatRoomWrapper
- = chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
-
- if(existChatRoomWrapper == null)
- return null;
-
- // We save the choice of the user, before the chat room is really
- // joined, because even the join fails we want the next time when
- // we login to join this chat room automatically.
- ConfigurationUtils.updateChatRoomStatus(
- chatRoomWrapper.getParentProvider().getProtocolProvider(),
- chatRoomWrapper.getChatRoomID(),
- GlobalStatusEnum.OFFLINE_STATUS);
-
- return existChatRoomWrapper;
- }
-
- /**
- * Joins a chat room in an asynchronous way.
- */
- private class JoinChatRoomTask
- extends Thread
- {
- private final ChatRoomWrapperImpl chatRoomWrapper;
-
- private final String nickName;
-
- private final byte[] password;
-
- private final boolean rememberPassword;
-
- private final boolean isFirstAttempt;
-
- private final String subject;
-
- private ResourceManagementService resources
- = MUCActivator.getResources();
-
- JoinChatRoomTask( ChatRoomWrapperImpl chatRoomWrapper,
- String nickName,
- byte[] password,
- boolean rememberPassword,
- boolean isFirstAttempt,
- String subject)
- {
- this.chatRoomWrapper = chatRoomWrapper;
- this.nickName = nickName;
- this.isFirstAttempt = isFirstAttempt;
- this.subject = subject;
-
- if(password == null)
- {
- String passString = chatRoomWrapper.loadPassword();
- if(passString != null)
- {
- this.password = passString.getBytes();
- }
- else
- {
- this.password = null;
- }
- }
- else
- {
- this.password = password;
- }
- this.rememberPassword = rememberPassword;
- }
-
- JoinChatRoomTask( ChatRoomWrapperImpl chatRoomWrapper,
- String nickName,
- byte[] password)
- {
- this(chatRoomWrapper, nickName, password, false, true, null);
- }
-
- JoinChatRoomTask( ChatRoomWrapperImpl chatRoomWrapper,
- String nickName,
- byte[] password,
- String subject)
- {
- this(chatRoomWrapper, nickName, password, false, true, subject);
- }
-
- /**
- * @override {@link Thread}{@link #run()} to perform all asynchronous
- * tasks.
- */
- @Override
- public void run()
- {
- ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
-
- try
- {
- if(password != null && password.length > 0)
- chatRoom.joinAs(nickName, password);
- else if (nickName != null)
- chatRoom.joinAs(nickName);
- else
- chatRoom.join();
-
- done(JOIN_SUCCESS_PROP);
- }
- catch (OperationFailedException e)
- {
- if (logger.isTraceEnabled())
- logger.trace("Failed to join chat room: "
- + chatRoom.getName(), e);
-
- switch (e.getErrorCode())
- {
- case OperationFailedException.AUTHENTICATION_FAILED:
- done(JOIN_AUTHENTICATION_FAILED_PROP);
- break;
- case OperationFailedException.REGISTRATION_REQUIRED:
- done(JOIN_REGISTRATION_REQUIRED_PROP);
- break;
- case OperationFailedException.PROVIDER_NOT_REGISTERED:
- done(JOIN_PROVIDER_NOT_REGISTERED_PROP);
- break;
- case OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS:
- done(JOIN_SUBSCRIPTION_ALREADY_EXISTS_PROP);
- break;
- default:
- done(JOIN_UNKNOWN_ERROR_PROP);
- }
- }
- }
-
- /**
- * Performs UI changes after the chat room join task has finished.
- * @param returnCode the result code from the chat room join task.
- */
- private void done(String returnCode)
- {
- ConfigurationUtils.updateChatRoomStatus(
- chatRoomWrapper.getParentProvider().getProtocolProvider(),
- chatRoomWrapper.getChatRoomID(),
- GlobalStatusEnum.ONLINE_STATUS);
-
- String errorMessage = null;
- if(JOIN_AUTHENTICATION_FAILED_PROP.equals(returnCode))
- {
- chatRoomWrapper.removePassword();
-
- AuthenticationWindowService authWindowsService
- = ServiceUtils.getService(
- MUCActivator.bundleContext,
- AuthenticationWindowService.class);
-
- AuthenticationWindowService.AuthenticationWindow authWindow =
- authWindowsService.create(
- null, null, null, false,
- chatRoomWrapper.isPersistent(),
- AuthenticationWindow.getAuthenticationWindowIcon(
- chatRoomWrapper.getParentProvider()
- .getProtocolProvider()),
- resources.getI18NString(
- "service.gui.AUTHENTICATION_WINDOW_TITLE",
- new String[]{chatRoomWrapper.getParentProvider()
- .getName()}),
- resources.getI18NString(
- "service.gui.CHAT_ROOM_REQUIRES_PASSWORD",
- new String[]{
- chatRoomWrapper.getChatRoomName()}),
- "", null,
- isFirstAttempt ?
- null :
- resources.getI18NString(
- "service.gui.AUTHENTICATION_FAILED",
- new String[]{chatRoomWrapper.getChatRoomName()}),
- null);
-
- authWindow.setVisible(true);
-
- if (!authWindow.isCanceled())
- {
- joinChatRoom(
- chatRoomWrapper,
- nickName,
- new String(authWindow.getPassword()).getBytes(),
- authWindow.isRememberPassword(),
- false,
- subject);
- }
- }
- else if(JOIN_REGISTRATION_REQUIRED_PROP.equals(returnCode))
- {
- errorMessage
- = resources
- .getI18NString(
- "service.gui.CHAT_ROOM_REGISTRATION_REQUIRED",
- new String[]{chatRoomWrapper.getChatRoomName()});
- }
- else if(JOIN_PROVIDER_NOT_REGISTERED_PROP.equals(returnCode))
- {
- errorMessage
- = resources
- .getI18NString("service.gui.CHAT_ROOM_NOT_CONNECTED",
- new String[]{chatRoomWrapper.getChatRoomName()});
- }
- else if(JOIN_SUBSCRIPTION_ALREADY_EXISTS_PROP.equals(returnCode))
- {
- errorMessage
- = resources
- .getI18NString("service.gui.CHAT_ROOM_ALREADY_JOINED",
- new String[]{chatRoomWrapper.getChatRoomName()});
- }
- else
- {
- errorMessage
- = resources
- .getI18NString("service.gui.FAILED_TO_JOIN_CHAT_ROOM",
- new String[]{chatRoomWrapper.getChatRoomName()});
- }
-
- if (!JOIN_SUCCESS_PROP.equals(returnCode) &&
- !JOIN_AUTHENTICATION_FAILED_PROP.equals(returnCode))
- {
- MUCActivator.getAlertUIService().showAlertPopup(
- resources.getI18NString("service.gui.ERROR"), errorMessage);
- }
-
- if (JOIN_SUCCESS_PROP.equals(returnCode))
- {
- if(rememberPassword)
- {
- chatRoomWrapper.savePassword(new String(password));
- }
-
- if(subject != null)
- {
- try
- {
- chatRoomWrapper.getChatRoom().setSubject(subject);
- }
- catch(OperationFailedException ex)
- {
- logger.warn("Failed to set subject.");
- }
- }
- }
-
- chatRoomWrapper.firePropertyChange(returnCode);
- }
- }
-
- /**
- * Finds the <tt>ChatRoomWrapper</tt> instance associated with the
- * source contact.
- * @param contact the source contact.
- * @return the <tt>ChatRoomWrapper</tt> instance.
- */
- public ChatRoomWrapper findChatRoomWrapperFromSourceContact(
- SourceContact contact)
- {
- if(!(contact instanceof ChatRoomSourceContact))
- return null;
- ChatRoomSourceContact chatRoomContact = (ChatRoomSourceContact) contact;
- return chatRoomList.findChatRoomWrapperFromChatRoomID(
- chatRoomContact.getChatRoomID(), chatRoomContact.getProvider());
- }
-
- /**
- * Finds the <tt>ChatRoomWrapper</tt> instance associated with the
- * chat room.
- * @param chatRoomID the id of the chat room.
- * @param pps the provider of the chat room.
- * @return the <tt>ChatRoomWrapper</tt> instance.
- */
- public ChatRoomWrapper findChatRoomWrapperFromChatRoomID(String chatRoomID,
- ProtocolProviderService pps)
- {
- return chatRoomList.findChatRoomWrapperFromChatRoomID(chatRoomID, pps);
- }
-
- /**
- * Searches for chat room wrapper in chat room list by chat room.
- *
- * @param chatRoom the chat room.
- * @param create if <tt>true</tt> and the chat room wrapper is not found new
- * chatRoomWrapper is created.
- * @return found chat room wrapper or the created chat room wrapper.
- */
- @Override
- public ChatRoomWrapper getChatRoomWrapperByChatRoom(ChatRoom chatRoom,
- boolean create)
- {
- ChatRoomWrapper chatRoomWrapper
- = chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
-
- if ((chatRoomWrapper == null) && create)
- {
- ChatRoomProviderWrapper parentProvider
- = chatRoomList.findServerWrapperFromProvider(
- chatRoom.getParentProvider());
-
- chatRoomWrapper
- = new ChatRoomWrapperImpl(
- parentProvider, chatRoom);
-
- chatRoomList.addChatRoom(chatRoomWrapper);
- }
- return chatRoomWrapper;
- }
-
- /**
- * Goes through the locally stored chat rooms list and for each
- * {@link ChatRoomWrapper} tries to find the corresponding server stored
- * {@link ChatRoom} in the specified operation set. Joins automatically all
- * found chat rooms.
- *
- * @param protocolProvider the protocol provider for the account to
- * synchronize
- * @param opSet the multi user chat operation set, which give us access to
- * chat room server
- */
- public void synchronizeOpSetWithLocalContactList(
- ProtocolProviderService protocolProvider,
- final OperationSetMultiUserChat opSet)
- {
- ChatRoomProviderWrapper chatRoomProvider
- = findServerWrapperFromProvider(protocolProvider);
-
- if(chatRoomProvider == null)
- {
- chatRoomProvider = chatRoomList.addRegisteredChatProvider(protocolProvider);
- }
-
- if (chatRoomProvider != null)
- {
- chatRoomProvider.synchronizeProvider();
- }
- }
-
- /**
- * Returns an iterator to the list of chat room providers.
- *
- * @return an iterator to the list of chat room providers.
- */
- public Iterator<ChatRoomProviderWrapper> getChatRoomProviders()
- {
- return chatRoomList.getChatRoomProviders();
- }
-
- /**
- * Removes the given <tt>ChatRoom</tt> from the list of all chat rooms.
- *
- * @param chatRoomWrapper the <tt>ChatRoomWrapper</tt> to remove
- */
- public void removeChatRoom(ChatRoomWrapper chatRoomWrapper)
- {
- chatRoomList.removeChatRoom(chatRoomWrapper);
- }
-
- /**
- * Destroys the given <tt>ChatRoom</tt> from the list of all chat rooms.
- *
- * @param chatRoomWrapper the <tt>ChatRoomWrapper</tt> to be destroyed.
- * @param reason the reason for destroying.
- * @param alternateAddress the alternate address.
- */
- public void destroyChatRoom(ChatRoomWrapper chatRoomWrapper,
- String reason, String alternateAddress)
- {
- if(chatRoomWrapper.getChatRoom().destroy(reason, alternateAddress))
- {
- MUCActivator.getUIService().closeChatRoomWindow(
- chatRoomWrapper);
- chatRoomList.removeChatRoom(chatRoomWrapper);
- }
- else
- {
- // if we leave a chat room which is not persistent
- // the room can be destroyed on the server, and error is returned
- // when we try to destroy it not-authorized(401)
- if(!chatRoomWrapper.getChatRoom().isPersistent()
- && !chatRoomWrapper.getChatRoom().isJoined())
- {
- chatRoomList.removeChatRoom(chatRoomWrapper);
- }
- }
-
- }
-
- /**
- * Adds a ChatRoomProviderWrapperListener to the listener list.
- *
- * @param listener the ChatRoomProviderWrapperListener to be added
- */
- public void addChatRoomProviderWrapperListener(
- ChatRoomProviderWrapperListener listener)
- {
- chatRoomList.addChatRoomProviderWrapperListener(listener);
- }
-
- /**
- * Removes the ChatRoomProviderWrapperListener to the listener list.
- *
- * @param listener the ChatRoomProviderWrapperListener to be removed
- */
- public void removeChatRoomProviderWrapperListener(
- ChatRoomProviderWrapperListener listener)
- {
- chatRoomList.removeChatRoomProviderWrapperListener(listener);
- }
-
- /**
- * Returns the <tt>ChatRoomProviderWrapper</tt> that correspond to the
- * given <tt>ProtocolProviderService</tt>. If the list doesn't contain a
- * corresponding wrapper - returns null.
- *
- * @param protocolProvider the protocol provider that we're looking for
- * @return the <tt>ChatRoomProvider</tt> object corresponding to
- * the given <tt>ProtocolProviderService</tt>
- */
- public ChatRoomProviderWrapper findServerWrapperFromProvider(
- ProtocolProviderService protocolProvider)
- {
- return chatRoomList.findServerWrapperFromProvider(protocolProvider);
- }
-
- /**
- * Returns the <tt>ChatRoomWrapper</tt> that correspond to the given
- * <tt>ChatRoom</tt>. If the list of chat rooms doesn't contain a
- * corresponding wrapper - returns null.
- *
- * @param chatRoom the <tt>ChatRoom</tt> that we're looking for
- * @return the <tt>ChatRoomWrapper</tt> object corresponding to the given
- * <tt>ChatRoom</tt>
- */
- public ChatRoomWrapper findChatRoomWrapperFromChatRoom(ChatRoom chatRoom)
- {
- return chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
- }
-
- /**
- * Opens a chat window for the chat room.
- *
- * @param room the chat room.
- */
- public void openChatRoom(ChatRoomWrapper room)
- {
- if (room.getChatRoom() == null)
- {
- room = createChatRoom(
- room.getChatRoomName(),
- room.getParentProvider().getProtocolProvider(),
- new ArrayList<String>(),"", false, false, true);
-
- // leave the chatroom because getChatRoom().isJoined() returns true
- // otherwise
- if (room.getChatRoom().isJoined())
- room.getChatRoom().leave();
-
- }
-
- if(!room.getChatRoom().isJoined())
- {
- String savedNick =
- ConfigurationUtils.getChatRoomProperty(room
- .getParentProvider().getProtocolProvider(), room
- .getChatRoomID(), "userNickName");
- String subject = null;
-
- if (savedNick == null)
- {
- String[] joinOptions = ChatRoomJoinOptionsDialog.getJoinOptions(
- room.getParentProvider().getProtocolProvider(),
- room.getChatRoomID(),
- MUCActivator.getGlobalDisplayDetailsService()
- .getDisplayName(
- room.getParentProvider().getProtocolProvider()));
- savedNick = joinOptions[0];
- subject = joinOptions[1];
-
- }
-
- if (savedNick != null)
- {
- joinChatRoom(room, savedNick, null,
- subject);
- }
- else
- return;
- }
-
- MUCActivator.getUIService().openChatRoomWindow(room);
- }
-
- /**
- * Returns instance of the <tt>ServerChatRoomContactSourceService</tt>
- * contact source.
- * @return instance of the <tt>ServerChatRoomContactSourceService</tt>
- * contact source.
- */
- public ContactSourceService getServerChatRoomsContactSourceForProvider(
- ChatRoomProviderWrapper pps)
- {
- return new ServerChatRoomContactSourceService(pps);
- }
-
- /**
- * Returns <tt>true</tt> if the contact is <tt>ChatRoomSourceContact</tt>
- *
- * @param contact the contact
- * @return <tt>true</tt> if the contact is <tt>ChatRoomSourceContact</tt>
- */
- public boolean isMUCSourceContact(SourceContact contact)
- {
- return (contact instanceof ChatRoomSourceContact);
- }
-}
+package net.java.sip.communicator.impl.muc;
+
+import static net.java.sip.communicator.service.muc.ChatRoomWrapper.*;
+
+import java.util.*;
+
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.plugin.desktoputil.chat.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.muc.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.globalstatus.*;
+import net.java.sip.communicator.util.*;
+
+import org.jitsi.service.resources.*;
+
+/**
+ * The <tt>MUCServiceImpl</tt> class implements the service for the chat rooms.
+ *
+ * @author Hristo Terezov
+ */
+public class MUCServiceImpl
+ extends MUCService
+{
+
+ /**
+ * The list of persistent chat rooms.
+ */
+ private final ChatRoomListImpl chatRoomList = new ChatRoomListImpl();
+
+ /**
+ * The <tt>Logger</tt> used by the <tt>MUCServiceImpl</tt> class and its
+ * instances for logging output.
+ */
+ private static Logger logger = Logger.getLogger(MUCServiceImpl.class);
+
+ /**
+ * Called to accept an incoming invitation. Adds the invitation chat room
+ * to the list of chat rooms and joins it.
+ *
+ * @param invitation the invitation to accept.
+ */
+ public void acceptInvitation(ChatRoomInvitation invitation)
+ {
+ ChatRoom chatRoom = invitation.getTargetChatRoom();
+ byte[] password = invitation.getChatRoomPassword();
+
+ String nickName =
+ ConfigurationUtils.getChatRoomProperty(
+ chatRoom.getParentProvider(),
+ chatRoom.getIdentifier(), "userNickName");
+ if(nickName == null)
+ {
+ String[] joinOptions = ChatRoomJoinOptionsDialog.getJoinOptions(
+ true,
+ chatRoom.getParentProvider(),
+ chatRoom.getIdentifier(),
+ MUCActivator.getGlobalDisplayDetailsService()
+ .getDisplayName(chatRoom.getParentProvider()));
+ nickName = joinOptions[0];
+ }
+
+ joinChatRoom(chatRoom, nickName, password);
+ }
+
+ /**
+ * Adds a change listener to the <tt>ChatRoomList</tt>.
+ *
+ * @param l the listener.
+ */
+ public void addChatRoomListChangeListener(ChatRoomListChangeListener l)
+ {
+ chatRoomList.addChatRoomListChangeListener(l);
+ }
+
+ /**
+ * Removes a change listener to the <tt>ChatRoomList</tt>.
+ *
+ * @param l the listener.
+ */
+ public void removeChatRoomListChangeListener(ChatRoomListChangeListener l)
+ {
+ chatRoomList.removeChatRoomListChangeListener(l);
+ }
+
+ /**
+ * Fires a <tt>ChatRoomListChangedEvent</tt> event.
+ *
+ * @param chatRoomWrapper the chat room.
+ * @param eventID the id of the event.
+ */
+ public void fireChatRoomListChangedEvent( ChatRoomWrapper chatRoomWrapper,
+ int eventID)
+ {
+ chatRoomList.fireChatRoomListChangedEvent(chatRoomWrapper, eventID);
+ }
+
+ /**
+ * Joins the given chat room with the given password and manages all the
+ * exceptions that could occur during the join process.
+ *
+ * @param chatRoomWrapper the chat room to join.
+ * @param nickName the nickname we choose for the given chat room.
+ * @param password the password.
+ * @param rememberPassword if true the password should be saved.
+ * @param isFirstAttempt is this the first attempt to join room, used
+ * to check whether to show some error messages
+ * @param subject the subject which will be set to the room after the user
+ * join successful.
+ */
+ private void joinChatRoom( ChatRoomWrapper chatRoomWrapper,
+ String nickName,
+ byte[] password,
+ boolean rememberPassword,
+ boolean isFirstAttempt,
+ String subject)
+ {
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+
+ if(chatRoom == null)
+ {
+ MUCActivator.getAlertUIService().showAlertDialog(
+ MUCActivator.getResources().getI18NString("service.gui.WARNING"),
+ MUCActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_NOT_CONNECTED",
+ new String[]{chatRoomWrapper.getChatRoomName()}));
+ return;
+ }
+
+ new JoinChatRoomTask(
+ (ChatRoomWrapperImpl)chatRoomWrapper,
+ nickName,
+ password,
+ rememberPassword,
+ isFirstAttempt,
+ subject)
+ .start();
+ }
+
+ /**
+ * Joins the given chat room with the given password and manages all the
+ * exceptions that could occur during the join process.
+ *
+ * @param chatRoomWrapper the chat room to join.
+ * @param nickName the nickname we choose for the given chat room.
+ * @param password the password.
+ */
+ public void joinChatRoom( ChatRoomWrapper chatRoomWrapper,
+ String nickName,
+ byte[] password)
+ {
+ if (chatRoomWrapper.getChatRoom() == null)
+ {
+ chatRoomWrapper = createChatRoom(
+ chatRoomWrapper.getChatRoomName(),
+ chatRoomWrapper.getParentProvider().getProtocolProvider(),
+ new ArrayList<String>(), "", false, false, true);
+ }
+
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+
+ if(chatRoom == null)
+ {
+ MUCActivator.getAlertUIService().showAlertDialog(
+ MUCActivator.getResources().getI18NString("service.gui.WARNING"),
+ MUCActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_NOT_CONNECTED",
+ new String[]{chatRoomWrapper.getChatRoomName()}));
+ return;
+ }
+
+ new JoinChatRoomTask(
+ (ChatRoomWrapperImpl)chatRoomWrapper, nickName, password)
+ .start();
+ }
+
+ /**
+ * Joins the given chat room with the given password and manages all the
+ * exceptions that could occur during the join process.
+ *
+ * @param chatRoomWrapper the chat room to join.
+ * @param nickName the nickname we choose for the given chat room.
+ * @param password the password.
+ * @param subject the subject which will be set to the room after the user
+ * join successful.
+ */
+ public void joinChatRoom( ChatRoomWrapper chatRoomWrapper,
+ String nickName,
+ byte[] password,
+ String subject)
+ {
+ if (chatRoomWrapper.getChatRoom() == null)
+ {
+ chatRoomWrapper = createChatRoom(
+ chatRoomWrapper.getChatRoomName(),
+ chatRoomWrapper.getParentProvider().getProtocolProvider(),
+ new ArrayList<String>(), "", false, false, true);
+ }
+
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+ if (chatRoom == null)
+ {
+ MUCActivator.getAlertUIService().showAlertDialog(
+ MUCActivator.getResources().getI18NString("service.gui.WARNING"),
+ MUCActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_NOT_CONNECTED",
+ new String[]{chatRoomWrapper.getChatRoomName()}));
+
+ return;
+ }
+
+ // join from add chat room dialog
+
+ new JoinChatRoomTask(
+ (ChatRoomWrapperImpl) chatRoomWrapper,
+ nickName,
+ password,
+ subject)
+ .start();
+ }
+
+
+ /**
+ * Join chat room.
+ * @param chatRoomWrapper
+ */
+ public void joinChatRoom(ChatRoomWrapper chatRoomWrapper)
+ {
+ if (chatRoomWrapper.getChatRoom() == null)
+ {
+ chatRoomWrapper = createChatRoom(
+ chatRoomWrapper.getChatRoomName(),
+ chatRoomWrapper.getParentProvider().getProtocolProvider(),
+ new ArrayList<String>(), "", false, false, true);
+ }
+
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+
+ if(chatRoom == null)
+ {
+ MUCActivator.getAlertUIService().showAlertDialog(
+ MUCActivator.getResources().getI18NString("service.gui.WARNING"),
+ MUCActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_NOT_CONNECTED",
+ new String[]{chatRoomWrapper.getChatRoomName()}));
+
+ return;
+ }
+
+ new JoinChatRoomTask((ChatRoomWrapperImpl)chatRoomWrapper, null, null)
+ .start();
+ }
+
+
+ /**
+ * Joins the given chat room and manages all the exceptions that could
+ * occur during the join process.
+ *
+ * @param chatRoom the chat room to join
+ * @param nickname the nickname we're using to join
+ * @param password the password we're using to join
+ */
+ public void joinChatRoom( ChatRoom chatRoom,
+ String nickname,
+ byte[] password)
+ {
+ ChatRoomWrapper chatRoomWrapper
+ = chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
+
+ if(chatRoomWrapper == null)
+ {
+ ChatRoomProviderWrapper parentProvider
+ = chatRoomList.findServerWrapperFromProvider(
+ chatRoom.getParentProvider());
+
+ chatRoomWrapper
+ = new ChatRoomWrapperImpl(parentProvider, chatRoom);
+
+ chatRoomList.addChatRoom(chatRoomWrapper);
+
+ }
+
+ this.joinChatRoom(chatRoomWrapper, nickname, password);
+ }
+
+ /**
+ * Joins the room with the given name though the given chat room provider.
+ *
+ * @param chatRoomName the name of the room to join.
+ * @param chatRoomProvider the chat room provider to join through.
+ */
+ public void joinChatRoom( String chatRoomName,
+ ChatRoomProviderWrapper chatRoomProvider)
+ {
+ OperationSetMultiUserChat groupChatOpSet
+ = chatRoomProvider
+ .getProtocolProvider().getOperationSet(
+ OperationSetMultiUserChat.class);
+
+ ChatRoom chatRoom = null;
+ try
+ {
+ chatRoom = groupChatOpSet.findRoom(chatRoomName);
+ }
+ catch (Exception e)
+ {
+ if (logger.isTraceEnabled())
+ logger.trace("Un exception occurred while searching for room:"
+ + chatRoomName, e);
+ }
+
+ if (chatRoom != null)
+ {
+ ChatRoomWrapper chatRoomWrapper
+ = chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
+
+ if(chatRoomWrapper == null)
+ {
+ ChatRoomProviderWrapper parentProvider
+ = chatRoomList
+ .findServerWrapperFromProvider(
+ chatRoom.getParentProvider());
+
+ chatRoomWrapper
+ = new ChatRoomWrapperImpl(parentProvider, chatRoom);
+
+ chatRoomList.addChatRoom(chatRoomWrapper);
+
+ fireChatRoomListChangedEvent(
+ chatRoomWrapper,
+ ChatRoomListChangeEvent.CHAT_ROOM_ADDED);
+ }
+ joinChatRoom(chatRoomWrapper);
+ }
+ else
+ MUCActivator.getAlertUIService().showAlertDialog(
+ MUCActivator.getResources().getI18NString("service.gui.ERROR"),
+ MUCActivator.getResources().getI18NString(
+ "service.gui.CHAT_ROOM_NOT_EXIST",
+ new String[]{chatRoomName,
+ chatRoomProvider.getProtocolProvider()
+ .getAccountID().getService()}));
+ }
+
+
+ /**
+ * Creates a chat room, by specifying the chat room name, the parent
+ * protocol provider and eventually, the contacts invited to participate in
+ * this chat room.
+ *
+ * @param roomName the name of the room
+ * @param protocolProvider the parent protocol provider.
+ * @param contacts the contacts invited when creating the chat room.
+ * @param reason
+ * @param persistent is the room persistent
+ * @param isPrivate whether the room will be private or public.
+ * @return the <tt>ChatRoomWrapper</tt> corresponding to the created room
+ */
+ public ChatRoomWrapper createChatRoom(
+ String roomName,
+ ProtocolProviderService protocolProvider,
+ Collection<String> contacts,
+ String reason,
+ boolean persistent,
+ boolean isPrivate)
+ {
+ return createChatRoom(
+ roomName, protocolProvider, contacts, reason, true, persistent,
+ isPrivate);
+ }
+
+ /**
+ * Creates a chat room, by specifying the chat room name, the parent
+ * protocol provider and eventually, the contacts invited to participate in
+ * this chat room.
+ *
+ * @param roomName the name of the room
+ * @param protocolProvider the parent protocol provider.
+ * @param contacts the contacts invited when creating the chat room.
+ * @param reason
+ * @param persistent is the room persistent
+ * @return the <tt>ChatRoomWrapper</tt> corresponding to the created room
+ */
+ public ChatRoomWrapper createChatRoom(
+ String roomName,
+ ProtocolProviderService protocolProvider,
+ Collection<String> contacts,
+ String reason,
+ boolean persistent)
+ {
+ return createChatRoom(
+ roomName, protocolProvider, contacts, reason, true, persistent,
+ false);
+ }
+
+ /**
+ * Creates a chat room, by specifying the chat room name, the parent
+ * protocol provider and eventually, the contacts invited to participate in
+ * this chat room.
+ *
+ * @param roomName the name of the room
+ * @param protocolProvider the parent protocol provider.
+ * @param contacts the contacts invited when creating the chat room.
+ * @param reason
+ * @param join whether we should join the room after creating it.
+ * @param persistent whether the newly created room will be persistent.
+ * @param isPrivate whether the room will be private or public.
+ * @return the <tt>ChatRoomWrapper</tt> corresponding to the created room or
+ * <tt>null</tt> if the protocol fails to create the chat room.
+ */
+ public ChatRoomWrapper createChatRoom(
+ String roomName,
+ ProtocolProviderService protocolProvider,
+ Collection<String> contacts,
+ String reason,
+ boolean join,
+ boolean persistent,
+ boolean isPrivate)
+ {
+ ChatRoomWrapper chatRoomWrapper = null;
+ OperationSetMultiUserChat groupChatOpSet
+ = protocolProvider.getOperationSet(OperationSetMultiUserChat.class);
+
+ // If there's no group chat operation set we have nothing to do here.
+ if (groupChatOpSet == null)
+ return null;
+
+ ChatRoom chatRoom = null;
+ try
+ {
+
+
+ HashMap<String, Object> roomProperties =
+ new HashMap<String, Object>();
+ roomProperties.put("isPrivate", isPrivate);
+ chatRoom = groupChatOpSet.createChatRoom(roomName, roomProperties);
+
+ if(join)
+ {
+ chatRoom.join();
+
+ for(String contact : contacts)
+ chatRoom.invite(contact, reason);
+ }
+ }
+ catch (OperationFailedException ex)
+ {
+ logger.error("Failed to create chat room.", ex);
+
+ MUCActivator.getAlertUIService().showAlertDialog(
+ MUCActivator.getResources().getI18NString("service.gui.ERROR"),
+ MUCActivator.getResources().getI18NString(
+ "service.gui.CREATE_CHAT_ROOM_ERROR",
+ new String[]{protocolProvider.getProtocolDisplayName()}),
+ ex);
+ }
+ catch (OperationNotSupportedException ex)
+ {
+ logger.error("Failed to create chat room.", ex);
+
+ MUCActivator.getAlertUIService().showAlertDialog(
+ MUCActivator.getResources().getI18NString("service.gui.ERROR"),
+ MUCActivator.getResources().getI18NString(
+ "service.gui.CREATE_CHAT_ROOM_ERROR",
+ new String[]{protocolProvider.getProtocolDisplayName()}),
+ ex);
+ }
+
+ if(chatRoom != null)
+ {
+ ChatRoomProviderWrapper parentProvider
+ = chatRoomList.findServerWrapperFromProvider(protocolProvider);
+
+ // if there is the same room ids don't add new wrapper as old one
+ // maybe already created
+ chatRoomWrapper =
+ chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
+
+ if(chatRoomWrapper == null)
+ {
+ chatRoomWrapper
+ = new ChatRoomWrapperImpl(parentProvider, chatRoom);
+ chatRoomWrapper.setPersistent(persistent);
+ chatRoomList.addChatRoom(chatRoomWrapper);
+ }
+ }
+
+ return chatRoomWrapper;
+ }
+
+ /**
+ * Creates a private chat room, by specifying the parent
+ * protocol provider and eventually, the contacts invited to participate in
+ * this chat room.
+ *
+ * @param protocolProvider the parent protocol provider.
+ * @param contacts the contacts invited when creating the chat room.
+ * @param reason
+ * @param persistent is the room persistent
+ * @return the <tt>ChatRoomWrapper</tt> corresponding to the created room
+ */
+ public ChatRoomWrapper createPrivateChatRoom(
+ ProtocolProviderService protocolProvider,
+ Collection<String> contacts,
+ String reason,
+ boolean persistent)
+ {
+ return this.createChatRoom(
+ null, protocolProvider, contacts, reason, persistent, true);
+ }
+
+
+ /**
+ * Returns existing chat rooms for the given <tt>chatRoomProvider</tt>.
+ * @param chatRoomProvider the <tt>ChatRoomProviderWrapper</tt>, which
+ * chat rooms we're looking for
+ * @return existing chat rooms for the given <tt>chatRoomProvider</tt>
+ */
+ public List<String> getExistingChatRooms(
+ ChatRoomProviderWrapper chatRoomProvider)
+ {
+ if (chatRoomProvider == null)
+ return null;
+
+ ProtocolProviderService protocolProvider
+ = chatRoomProvider.getProtocolProvider();
+
+ if (protocolProvider == null)
+ return null;
+
+ OperationSetMultiUserChat groupChatOpSet
+ = protocolProvider
+ .getOperationSet(OperationSetMultiUserChat.class);
+
+ if (groupChatOpSet == null)
+ return null;
+
+ List<String> chatRooms = null;
+ try
+ {
+ chatRooms = groupChatOpSet.getExistingChatRooms();
+ }
+ catch (OperationFailedException e)
+ {
+ if (logger.isTraceEnabled())
+ logger.trace("Failed to obtain existing chat rooms for server: "
+ + protocolProvider.getAccountID().getService(), e);
+ }
+ catch (OperationNotSupportedException e)
+ {
+ if (logger.isTraceEnabled())
+ logger.trace("Failed to obtain existing chat rooms for server: "
+ + protocolProvider.getAccountID().getService(), e);
+ }
+
+ return chatRooms;
+ }
+
+ /**
+ * Rejects the given invitation with the specified reason.
+ *
+ * @param multiUserChatOpSet the operation set to use for rejecting the
+ * invitation
+ * @param invitation the invitation to reject
+ * @param reason the reason for the rejection
+ */
+ public void rejectInvitation( OperationSetMultiUserChat multiUserChatOpSet,
+ ChatRoomInvitation invitation,
+ String reason)
+ {
+ multiUserChatOpSet.rejectInvitation(invitation, reason);
+ }
+
+ /**
+ * Leaves the given chat room.
+ *
+ * @param chatRoomWrapper the chat room to leave.
+ * @return <tt>ChatRoomWrapper</tt> instance associated with the chat room.
+ */
+ public ChatRoomWrapper leaveChatRoom(ChatRoomWrapper chatRoomWrapper)
+ {
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+
+ if (chatRoom == null)
+ {
+ ResourceManagementService resources = MUCActivator.getResources();
+
+ MUCActivator.getAlertUIService().showAlertDialog(
+ resources.getI18NString("service.gui.WARNING"),
+ resources
+ .getI18NString(
+ "service.gui.CHAT_ROOM_LEAVE_NOT_CONNECTED"));
+
+ return null;
+ }
+
+ if (chatRoom.isJoined())
+ chatRoom.leave();
+
+ ChatRoomWrapper existChatRoomWrapper
+ = chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
+
+ if(existChatRoomWrapper == null)
+ return null;
+
+ // We save the choice of the user, before the chat room is really
+ // joined, because even the join fails we want the next time when
+ // we login to join this chat room automatically.
+ ConfigurationUtils.updateChatRoomStatus(
+ chatRoomWrapper.getParentProvider().getProtocolProvider(),
+ chatRoomWrapper.getChatRoomID(),
+ GlobalStatusEnum.OFFLINE_STATUS);
+
+ return existChatRoomWrapper;
+ }
+
+ /**
+ * Joins a chat room in an asynchronous way.
+ */
+ private class JoinChatRoomTask
+ extends Thread
+ {
+ private final ChatRoomWrapperImpl chatRoomWrapper;
+
+ private final String nickName;
+
+ private final byte[] password;
+
+ private final boolean rememberPassword;
+
+ private final boolean isFirstAttempt;
+
+ private final String subject;
+
+ private ResourceManagementService resources
+ = MUCActivator.getResources();
+
+ JoinChatRoomTask( ChatRoomWrapperImpl chatRoomWrapper,
+ String nickName,
+ byte[] password,
+ boolean rememberPassword,
+ boolean isFirstAttempt,
+ String subject)
+ {
+ this.chatRoomWrapper = chatRoomWrapper;
+ this.nickName = nickName;
+ this.isFirstAttempt = isFirstAttempt;
+ this.subject = subject;
+
+ if(password == null)
+ {
+ String passString = chatRoomWrapper.loadPassword();
+ if(passString != null)
+ {
+ this.password = passString.getBytes();
+ }
+ else
+ {
+ this.password = null;
+ }
+ }
+ else
+ {
+ this.password = password;
+ }
+ this.rememberPassword = rememberPassword;
+ }
+
+ JoinChatRoomTask( ChatRoomWrapperImpl chatRoomWrapper,
+ String nickName,
+ byte[] password)
+ {
+ this(chatRoomWrapper, nickName, password, false, true, null);
+ }
+
+ JoinChatRoomTask( ChatRoomWrapperImpl chatRoomWrapper,
+ String nickName,
+ byte[] password,
+ String subject)
+ {
+ this(chatRoomWrapper, nickName, password, false, true, subject);
+ }
+
+ /**
+ * @override {@link Thread}{@link #run()} to perform all asynchronous
+ * tasks.
+ */
+ @Override
+ public void run()
+ {
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+
+ try
+ {
+ if(password != null && password.length > 0)
+ chatRoom.joinAs(nickName, password);
+ else if (nickName != null)
+ chatRoom.joinAs(nickName);
+ else
+ chatRoom.join();
+
+ done(JOIN_SUCCESS_PROP);
+ }
+ catch (OperationFailedException e)
+ {
+ if (logger.isTraceEnabled())
+ logger.trace("Failed to join chat room: "
+ + chatRoom.getName(), e);
+
+ switch (e.getErrorCode())
+ {
+ case OperationFailedException.AUTHENTICATION_FAILED:
+ done(JOIN_AUTHENTICATION_FAILED_PROP);
+ break;
+ case OperationFailedException.REGISTRATION_REQUIRED:
+ done(JOIN_REGISTRATION_REQUIRED_PROP);
+ break;
+ case OperationFailedException.PROVIDER_NOT_REGISTERED:
+ done(JOIN_PROVIDER_NOT_REGISTERED_PROP);
+ break;
+ case OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS:
+ done(JOIN_SUBSCRIPTION_ALREADY_EXISTS_PROP);
+ break;
+ default:
+ done(JOIN_UNKNOWN_ERROR_PROP);
+ }
+ }
+ }
+
+ /**
+ * Performs UI changes after the chat room join task has finished.
+ * @param returnCode the result code from the chat room join task.
+ */
+ private void done(String returnCode)
+ {
+ ConfigurationUtils.updateChatRoomStatus(
+ chatRoomWrapper.getParentProvider().getProtocolProvider(),
+ chatRoomWrapper.getChatRoomID(),
+ GlobalStatusEnum.ONLINE_STATUS);
+
+ String errorMessage = null;
+ if(JOIN_AUTHENTICATION_FAILED_PROP.equals(returnCode))
+ {
+ chatRoomWrapper.removePassword();
+
+ AuthenticationWindowService authWindowsService
+ = ServiceUtils.getService(
+ MUCActivator.bundleContext,
+ AuthenticationWindowService.class);
+
+ AuthenticationWindowService.AuthenticationWindow authWindow =
+ authWindowsService.create(
+ null, null, null, false,
+ chatRoomWrapper.isPersistent(),
+ AuthenticationWindow.getAuthenticationWindowIcon(
+ chatRoomWrapper.getParentProvider()
+ .getProtocolProvider()),
+ resources.getI18NString(
+ "service.gui.AUTHENTICATION_WINDOW_TITLE",
+ new String[]{chatRoomWrapper.getParentProvider()
+ .getName()}),
+ resources.getI18NString(
+ "service.gui.CHAT_ROOM_REQUIRES_PASSWORD",
+ new String[]{
+ chatRoomWrapper.getChatRoomName()}),
+ "", null,
+ isFirstAttempt ?
+ null :
+ resources.getI18NString(
+ "service.gui.AUTHENTICATION_FAILED",
+ new String[]{chatRoomWrapper.getChatRoomName()}),
+ null);
+
+ authWindow.setVisible(true);
+
+ if (!authWindow.isCanceled())
+ {
+ joinChatRoom(
+ chatRoomWrapper,
+ nickName,
+ new String(authWindow.getPassword()).getBytes(),
+ authWindow.isRememberPassword(),
+ false,
+ subject);
+ }
+ }
+ else if(JOIN_REGISTRATION_REQUIRED_PROP.equals(returnCode))
+ {
+ errorMessage
+ = resources
+ .getI18NString(
+ "service.gui.CHAT_ROOM_REGISTRATION_REQUIRED",
+ new String[]{chatRoomWrapper.getChatRoomName()});
+ }
+ else if(JOIN_PROVIDER_NOT_REGISTERED_PROP.equals(returnCode))
+ {
+ errorMessage
+ = resources
+ .getI18NString("service.gui.CHAT_ROOM_NOT_CONNECTED",
+ new String[]{chatRoomWrapper.getChatRoomName()});
+ }
+ else if(JOIN_SUBSCRIPTION_ALREADY_EXISTS_PROP.equals(returnCode))
+ {
+ errorMessage
+ = resources
+ .getI18NString("service.gui.CHAT_ROOM_ALREADY_JOINED",
+ new String[]{chatRoomWrapper.getChatRoomName()});
+ }
+ else
+ {
+ errorMessage
+ = resources
+ .getI18NString("service.gui.FAILED_TO_JOIN_CHAT_ROOM",
+ new String[]{chatRoomWrapper.getChatRoomName()});
+ }
+
+ if (!JOIN_SUCCESS_PROP.equals(returnCode) &&
+ !JOIN_AUTHENTICATION_FAILED_PROP.equals(returnCode))
+ {
+ MUCActivator.getAlertUIService().showAlertPopup(
+ resources.getI18NString("service.gui.ERROR"), errorMessage);
+ }
+
+ if (JOIN_SUCCESS_PROP.equals(returnCode))
+ {
+ if(rememberPassword)
+ {
+ chatRoomWrapper.savePassword(new String(password));
+ }
+
+ if(subject != null)
+ {
+ try
+ {
+ chatRoomWrapper.getChatRoom().setSubject(subject);
+ }
+ catch(OperationFailedException ex)
+ {
+ logger.warn("Failed to set subject.");
+ }
+ }
+ }
+
+ chatRoomWrapper.firePropertyChange(returnCode);
+ }
+ }
+
+ /**
+ * Finds the <tt>ChatRoomWrapper</tt> instance associated with the
+ * source contact.
+ * @param contact the source contact.
+ * @return the <tt>ChatRoomWrapper</tt> instance.
+ */
+ public ChatRoomWrapper findChatRoomWrapperFromSourceContact(
+ SourceContact contact)
+ {
+ if(!(contact instanceof ChatRoomSourceContact))
+ return null;
+ ChatRoomSourceContact chatRoomContact = (ChatRoomSourceContact) contact;
+ return chatRoomList.findChatRoomWrapperFromChatRoomID(
+ chatRoomContact.getChatRoomID(), chatRoomContact.getProvider());
+ }
+
+ /**
+ * Finds the <tt>ChatRoomWrapper</tt> instance associated with the
+ * chat room.
+ * @param chatRoomID the id of the chat room.
+ * @param pps the provider of the chat room.
+ * @return the <tt>ChatRoomWrapper</tt> instance.
+ */
+ public ChatRoomWrapper findChatRoomWrapperFromChatRoomID(String chatRoomID,
+ ProtocolProviderService pps)
+ {
+ return chatRoomList.findChatRoomWrapperFromChatRoomID(chatRoomID, pps);
+ }
+
+ /**
+ * Searches for chat room wrapper in chat room list by chat room.
+ *
+ * @param chatRoom the chat room.
+ * @param create if <tt>true</tt> and the chat room wrapper is not found new
+ * chatRoomWrapper is created.
+ * @return found chat room wrapper or the created chat room wrapper.
+ */
+ @Override
+ public ChatRoomWrapper getChatRoomWrapperByChatRoom(ChatRoom chatRoom,
+ boolean create)
+ {
+ ChatRoomWrapper chatRoomWrapper
+ = chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
+
+ if ((chatRoomWrapper == null) && create)
+ {
+ ChatRoomProviderWrapper parentProvider
+ = chatRoomList.findServerWrapperFromProvider(
+ chatRoom.getParentProvider());
+
+ chatRoomWrapper
+ = new ChatRoomWrapperImpl(
+ parentProvider, chatRoom);
+
+ chatRoomList.addChatRoom(chatRoomWrapper);
+ }
+ return chatRoomWrapper;
+ }
+
+ /**
+ * Goes through the locally stored chat rooms list and for each
+ * {@link ChatRoomWrapper} tries to find the corresponding server stored
+ * {@link ChatRoom} in the specified operation set. Joins automatically all
+ * found chat rooms.
+ *
+ * @param protocolProvider the protocol provider for the account to
+ * synchronize
+ * @param opSet the multi user chat operation set, which give us access to
+ * chat room server
+ */
+ public void synchronizeOpSetWithLocalContactList(
+ ProtocolProviderService protocolProvider,
+ final OperationSetMultiUserChat opSet)
+ {
+ ChatRoomProviderWrapper chatRoomProvider
+ = findServerWrapperFromProvider(protocolProvider);
+
+ if(chatRoomProvider == null)
+ {
+ chatRoomProvider = chatRoomList.addRegisteredChatProvider(protocolProvider);
+ }
+
+ if (chatRoomProvider != null)
+ {
+ chatRoomProvider.synchronizeProvider();
+ }
+ }
+
+ /**
+ * Returns an iterator to the list of chat room providers.
+ *
+ * @return an iterator to the list of chat room providers.
+ */
+ public Iterator<ChatRoomProviderWrapper> getChatRoomProviders()
+ {
+ return chatRoomList.getChatRoomProviders();
+ }
+
+ /**
+ * Removes the given <tt>ChatRoom</tt> from the list of all chat rooms.
+ *
+ * @param chatRoomWrapper the <tt>ChatRoomWrapper</tt> to remove
+ */
+ public void removeChatRoom(ChatRoomWrapper chatRoomWrapper)
+ {
+ chatRoomList.removeChatRoom(chatRoomWrapper);
+ }
+
+ /**
+ * Destroys the given <tt>ChatRoom</tt> from the list of all chat rooms.
+ *
+ * @param chatRoomWrapper the <tt>ChatRoomWrapper</tt> to be destroyed.
+ * @param reason the reason for destroying.
+ * @param alternateAddress the alternate address.
+ */
+ public void destroyChatRoom(ChatRoomWrapper chatRoomWrapper,
+ String reason, String alternateAddress)
+ {
+ if(chatRoomWrapper.getChatRoom().destroy(reason, alternateAddress))
+ {
+ MUCActivator.getUIService().closeChatRoomWindow(
+ chatRoomWrapper);
+ chatRoomList.removeChatRoom(chatRoomWrapper);
+ }
+ else
+ {
+ // if we leave a chat room which is not persistent
+ // the room can be destroyed on the server, and error is returned
+ // when we try to destroy it not-authorized(401)
+ if(!chatRoomWrapper.getChatRoom().isPersistent()
+ && !chatRoomWrapper.getChatRoom().isJoined())
+ {
+ chatRoomList.removeChatRoom(chatRoomWrapper);
+ }
+ }
+
+ }
+
+ /**
+ * Adds a ChatRoomProviderWrapperListener to the listener list.
+ *
+ * @param listener the ChatRoomProviderWrapperListener to be added
+ */
+ public void addChatRoomProviderWrapperListener(
+ ChatRoomProviderWrapperListener listener)
+ {
+ chatRoomList.addChatRoomProviderWrapperListener(listener);
+ }
+
+ /**
+ * Removes the ChatRoomProviderWrapperListener to the listener list.
+ *
+ * @param listener the ChatRoomProviderWrapperListener to be removed
+ */
+ public void removeChatRoomProviderWrapperListener(
+ ChatRoomProviderWrapperListener listener)
+ {
+ chatRoomList.removeChatRoomProviderWrapperListener(listener);
+ }
+
+ /**
+ * Returns the <tt>ChatRoomProviderWrapper</tt> that correspond to the
+ * given <tt>ProtocolProviderService</tt>. If the list doesn't contain a
+ * corresponding wrapper - returns null.
+ *
+ * @param protocolProvider the protocol provider that we're looking for
+ * @return the <tt>ChatRoomProvider</tt> object corresponding to
+ * the given <tt>ProtocolProviderService</tt>
+ */
+ public ChatRoomProviderWrapper findServerWrapperFromProvider(
+ ProtocolProviderService protocolProvider)
+ {
+ return chatRoomList.findServerWrapperFromProvider(protocolProvider);
+ }
+
+ /**
+ * Returns the <tt>ChatRoomWrapper</tt> that correspond to the given
+ * <tt>ChatRoom</tt>. If the list of chat rooms doesn't contain a
+ * corresponding wrapper - returns null.
+ *
+ * @param chatRoom the <tt>ChatRoom</tt> that we're looking for
+ * @return the <tt>ChatRoomWrapper</tt> object corresponding to the given
+ * <tt>ChatRoom</tt>
+ */
+ public ChatRoomWrapper findChatRoomWrapperFromChatRoom(ChatRoom chatRoom)
+ {
+ return chatRoomList.findChatRoomWrapperFromChatRoom(chatRoom);
+ }
+
+ /**
+ * Opens a chat window for the chat room.
+ *
+ * @param room the chat room.
+ */
+ public void openChatRoom(ChatRoomWrapper room)
+ {
+ if (room.getChatRoom() == null)
+ {
+ room = createChatRoom(
+ room.getChatRoomName(),
+ room.getParentProvider().getProtocolProvider(),
+ new ArrayList<String>(),"", false, false, true);
+
+ // leave the chatroom because getChatRoom().isJoined() returns true
+ // otherwise
+ if (room.getChatRoom().isJoined())
+ room.getChatRoom().leave();
+
+ }
+
+ if(!room.getChatRoom().isJoined())
+ {
+ String savedNick =
+ ConfigurationUtils.getChatRoomProperty(room
+ .getParentProvider().getProtocolProvider(), room
+ .getChatRoomID(), "userNickName");
+ String subject = null;
+
+ if (savedNick == null)
+ {
+ String[] joinOptions = ChatRoomJoinOptionsDialog.getJoinOptions(
+ room.getParentProvider().getProtocolProvider(),
+ room.getChatRoomID(),
+ MUCActivator.getGlobalDisplayDetailsService()
+ .getDisplayName(
+ room.getParentProvider().getProtocolProvider()));
+ savedNick = joinOptions[0];
+ subject = joinOptions[1];
+
+ }
+
+ if (savedNick != null)
+ {
+ joinChatRoom(room, savedNick, null,
+ subject);
+ }
+ else
+ return;
+ }
+
+ MUCActivator.getUIService().openChatRoomWindow(room);
+ }
+
+ /**
+ * Returns instance of the <tt>ServerChatRoomContactSourceService</tt>
+ * contact source.
+ * @return instance of the <tt>ServerChatRoomContactSourceService</tt>
+ * contact source.
+ */
+ public ContactSourceService getServerChatRoomsContactSourceForProvider(
+ ChatRoomProviderWrapper pps)
+ {
+ return new ServerChatRoomContactSourceService(pps);
+ }
+
+ /**
+ * Returns <tt>true</tt> if the contact is <tt>ChatRoomSourceContact</tt>
+ *
+ * @param contact the contact
+ * @return <tt>true</tt> if the contact is <tt>ChatRoomSourceContact</tt>
+ */
+ public boolean isMUCSourceContact(SourceContact contact)
+ {
+ return (contact instanceof ChatRoomSourceContact);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/muc/ServerChatRoomQuery.java b/src/net/java/sip/communicator/impl/muc/ServerChatRoomQuery.java
index 47785ee..a98e5ed 100644
--- a/src/net/java/sip/communicator/impl/muc/ServerChatRoomQuery.java
+++ b/src/net/java/sip/communicator/impl/muc/ServerChatRoomQuery.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,331 +15,331 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.muc;
-
-import java.util.*;
-import java.util.regex.*;
-
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.muc.*;
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * The <tt>ServerChatRoomQuery</tt> is a query over the
- * <tt>ServerChatRoomContactSourceService</tt>.
- *
- * @author Hristo Terezov
- */
-public class ServerChatRoomQuery
- extends AsyncContactQuery<ContactSourceService>
- implements ChatRoomProviderWrapperListener
-{
- /**
- * The query string.
- */
- private String queryString;
-
- /**
- * List with the current results for the query.
- */
- private Set<BaseChatRoomSourceContact> contactResults
- = new TreeSet<BaseChatRoomSourceContact>();
-
- /**
- * MUC service.
- */
- private MUCServiceImpl mucService;
-
- /**
- * The number of contact query listeners.
- */
- private int contactQueryListenersCount = 0;
-
- /**
- * The provider associated with the query.
- */
- private ChatRoomProviderWrapper provider = null;
-
- /**
- * Creates an instance of <tt>ChatRoomQuery</tt> by specifying
- * the parent contact source, the query string to match and the maximum
- * result contacts to return.
- *
- * @param contactSource the parent contact source
- * @param queryString the query string to match
- * @param provider the provider associated with the query
- */
- public ServerChatRoomQuery(String queryString,
- ServerChatRoomContactSourceService contactSource,
- ChatRoomProviderWrapper provider)
- {
- super(contactSource,
- Pattern.compile(queryString, Pattern.CASE_INSENSITIVE
- | Pattern.LITERAL), true);
- this.queryString = queryString;
-
- mucService = MUCActivator.getMUCService();
-
- this.provider = provider;
- }
-
- /**
- * Adds listeners for the query
- */
- private void initListeners()
- {
- mucService.addChatRoomProviderWrapperListener(this);
- }
-
- @Override
- protected void run()
- {
- if(provider == null)
- {
- Iterator<ChatRoomProviderWrapper> chatRoomProviders
- = mucService.getChatRoomProviders();
- while (chatRoomProviders.hasNext())
- {
- ChatRoomProviderWrapper provider = chatRoomProviders.next();
- providerAdded(provider, true);
- }
- }
- else
- {
- providerAdded(provider, true);
- }
-
- if (getStatus() != QUERY_CANCELED)
- setStatus(QUERY_COMPLETED);
- }
-
- /**
- * Handles adding a chat room provider.
- * @param provider the provider.
- * @param addQueryResult indicates whether we should add the chat room to
- * the query results or fire an event without adding it to the results.
- */
- private void providerAdded(final ChatRoomProviderWrapper provider,
- final boolean addQueryResult)
- {
- final ProtocolProviderService pps = provider.getProtocolProvider();
- List<String> chatRoomNames =
- MUCActivator.getMUCService().getExistingChatRooms(provider);
- if (chatRoomNames == null)
- {
- return;
- }
-
- // Already create all the BaseChatRoomSourceContact instances since all
- // the data is already available.
- final Set<BaseChatRoomSourceContact> chatRooms =
- new HashSet<BaseChatRoomSourceContact>(chatRoomNames.size());
- for (final String name : chatRoomNames)
- {
- chatRooms.add(new BaseChatRoomSourceContact(name, name, this, pps));
- }
- addChatRooms(pps, chatRooms, addQueryResult);
- }
-
-
- /**
- * Adds found result to the query results.
- *
- * @param pps the protocol provider associated with the found chat room.
- * @param chatRoomName the name of the chat room.
- * @param chatRoomID the id of the chat room.
- * @param addQueryResult indicates whether we should add the chat room to
- * the query results or fire an event without adding it to the results.
- */
- private void addChatRoom(ProtocolProviderService pps,
- String chatRoomName, String chatRoomID, boolean addQueryResult)
- {
- if((queryString == null
- || ((chatRoomName.contains(
- queryString)
- || chatRoomID.contains(queryString)
- ))) && isMatching(chatRoomID, pps))
- {
- BaseChatRoomSourceContact contact
- = new BaseChatRoomSourceContact(chatRoomName, chatRoomID, this,
- pps);
- synchronized (contactResults)
- {
- contactResults.add(contact);
- }
-
- if(addQueryResult)
- {
- addQueryResult(contact, false);
- }
- else
- {
- fireContactReceived(contact, false);
- }
- }
- }
-
- /**
- * Adds found results to the query results.
- *
- * @param pps the protocol provider associated with the found chat room.
- * @param chatRooms The set of chat rooms based on
- * BaseChatRoomSourceContact. This is the full set and it will be
- * filtered according to demands of the queryString.
- * @param addQueryResult indicates whether we should add the chat room to
- * the query results or fire an event without adding it to the
- * results.
- */
- private void addChatRooms(final ProtocolProviderService pps,
- final Set<BaseChatRoomSourceContact> chatRooms,
- final boolean addQueryResult)
- {
- BaseChatRoomSourceContact room;
- Iterator<BaseChatRoomSourceContact> iterator = chatRooms.iterator();
- while (iterator.hasNext())
- {
- room = iterator.next();
-
- // Notice the NOT operator at the start ...
- if (!((queryString == null || (room.getChatRoomName().contains(
- queryString) || room.getChatRoomID().contains(queryString)))
- && isMatching(room.getChatRoomID(), pps)))
- {
- iterator.remove();
- }
- }
-
- synchronized (contactResults)
- {
- contactResults.addAll(chatRooms);
- }
-
- if (addQueryResult)
- {
- addQueryResults(chatRooms);
- }
- else
- {
- // TODO Need something to fire one event for multiple contacts.
- for (SourceContact contact : chatRooms)
- {
- fireContactReceived(contact, false);
- }
- }
- }
-
- @Override
- public void chatRoomProviderWrapperAdded(ChatRoomProviderWrapper provider)
- {
- providerAdded(provider, false);
- }
-
- @Override
- public void chatRoomProviderWrapperRemoved(ChatRoomProviderWrapper provider)
- {
- LinkedList<BaseChatRoomSourceContact> tmpContactResults;
- synchronized (contactResults)
- {
- tmpContactResults
- = new LinkedList<BaseChatRoomSourceContact>(contactResults);
-
- for(BaseChatRoomSourceContact contact : tmpContactResults)
- {
- if(contact.getProvider().equals(provider.getProtocolProvider()))
- {
- contactResults.remove(contact);
- fireContactRemoved(contact);
- }
- }
- }
- }
-
-
- /**
- * Clears any listener we used.
- */
- private void clearListeners()
- {
- mucService.removeChatRoomProviderWrapperListener(this);
- }
-
- /**
- * Cancels this <tt>ContactQuery</tt>.
- *
- * @see ContactQuery#cancel()
- */
- public void cancel()
- {
- clearListeners();
-
- super.cancel();
- }
-
- /**
- * If query has status changed to cancel, let's clear listeners.
- * @param status {@link ContactQuery#QUERY_CANCELED},
- * {@link ContactQuery#QUERY_COMPLETED}
- */
- public void setStatus(int status)
- {
- if(status == QUERY_CANCELED)
- clearListeners();
-
- super.setStatus(status);
- }
-
- @Override
- public void addContactQueryListener(ContactQueryListener l)
- {
- super.addContactQueryListener(l);
- contactQueryListenersCount++;
- if(contactQueryListenersCount == 1)
- {
- initListeners();
- }
- }
-
- @Override
- public void removeContactQueryListener(ContactQueryListener l)
- {
- super.removeContactQueryListener(l);
- contactQueryListenersCount--;
- if(contactQueryListenersCount == 0)
- {
- clearListeners();
- }
- }
-
- /**
- * Checks if the contact should be added to results or not.
- * @param chatRoomID the chat room id associated with the contact.
- * @param pps the provider of the chat room contact.
- * @return <tt>true</tt> if the result should be added to the results and
- * <tt>false</tt> if not.
- */
- public boolean isMatching(String chatRoomID, ProtocolProviderService pps)
- {
- return (MUCActivator.getMUCService().findChatRoomWrapperFromChatRoomID(
- chatRoomID, pps) == null);
- }
-
- /**
- * Returns the index of the contact in the contact results list.
- * @param contact the contact.
- * @return the index of the contact in the contact results list.
- */
- public int indexOf(BaseChatRoomSourceContact contact)
- {
- Iterator<BaseChatRoomSourceContact> it = contactResults.iterator();
- int i = 0;
- while(it.hasNext())
- {
- if(contact.equals(it.next()))
- {
- return i;
- }
- i++;
- }
- return -1;
- }
-}
+package net.java.sip.communicator.impl.muc;
+
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.muc.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The <tt>ServerChatRoomQuery</tt> is a query over the
+ * <tt>ServerChatRoomContactSourceService</tt>.
+ *
+ * @author Hristo Terezov
+ */
+public class ServerChatRoomQuery
+ extends AsyncContactQuery<ContactSourceService>
+ implements ChatRoomProviderWrapperListener
+{
+ /**
+ * The query string.
+ */
+ private String queryString;
+
+ /**
+ * List with the current results for the query.
+ */
+ private Set<BaseChatRoomSourceContact> contactResults
+ = new TreeSet<BaseChatRoomSourceContact>();
+
+ /**
+ * MUC service.
+ */
+ private MUCServiceImpl mucService;
+
+ /**
+ * The number of contact query listeners.
+ */
+ private int contactQueryListenersCount = 0;
+
+ /**
+ * The provider associated with the query.
+ */
+ private ChatRoomProviderWrapper provider = null;
+
+ /**
+ * Creates an instance of <tt>ChatRoomQuery</tt> by specifying
+ * the parent contact source, the query string to match and the maximum
+ * result contacts to return.
+ *
+ * @param contactSource the parent contact source
+ * @param queryString the query string to match
+ * @param provider the provider associated with the query
+ */
+ public ServerChatRoomQuery(String queryString,
+ ServerChatRoomContactSourceService contactSource,
+ ChatRoomProviderWrapper provider)
+ {
+ super(contactSource,
+ Pattern.compile(queryString, Pattern.CASE_INSENSITIVE
+ | Pattern.LITERAL), true);
+ this.queryString = queryString;
+
+ mucService = MUCActivator.getMUCService();
+
+ this.provider = provider;
+ }
+
+ /**
+ * Adds listeners for the query
+ */
+ private void initListeners()
+ {
+ mucService.addChatRoomProviderWrapperListener(this);
+ }
+
+ @Override
+ protected void run()
+ {
+ if(provider == null)
+ {
+ Iterator<ChatRoomProviderWrapper> chatRoomProviders
+ = mucService.getChatRoomProviders();
+ while (chatRoomProviders.hasNext())
+ {
+ ChatRoomProviderWrapper provider = chatRoomProviders.next();
+ providerAdded(provider, true);
+ }
+ }
+ else
+ {
+ providerAdded(provider, true);
+ }
+
+ if (getStatus() != QUERY_CANCELED)
+ setStatus(QUERY_COMPLETED);
+ }
+
+ /**
+ * Handles adding a chat room provider.
+ * @param provider the provider.
+ * @param addQueryResult indicates whether we should add the chat room to
+ * the query results or fire an event without adding it to the results.
+ */
+ private void providerAdded(final ChatRoomProviderWrapper provider,
+ final boolean addQueryResult)
+ {
+ final ProtocolProviderService pps = provider.getProtocolProvider();
+ List<String> chatRoomNames =
+ MUCActivator.getMUCService().getExistingChatRooms(provider);
+ if (chatRoomNames == null)
+ {
+ return;
+ }
+
+ // Already create all the BaseChatRoomSourceContact instances since all
+ // the data is already available.
+ final Set<BaseChatRoomSourceContact> chatRooms =
+ new HashSet<BaseChatRoomSourceContact>(chatRoomNames.size());
+ for (final String name : chatRoomNames)
+ {
+ chatRooms.add(new BaseChatRoomSourceContact(name, name, this, pps));
+ }
+ addChatRooms(pps, chatRooms, addQueryResult);
+ }
+
+
+ /**
+ * Adds found result to the query results.
+ *
+ * @param pps the protocol provider associated with the found chat room.
+ * @param chatRoomName the name of the chat room.
+ * @param chatRoomID the id of the chat room.
+ * @param addQueryResult indicates whether we should add the chat room to
+ * the query results or fire an event without adding it to the results.
+ */
+ private void addChatRoom(ProtocolProviderService pps,
+ String chatRoomName, String chatRoomID, boolean addQueryResult)
+ {
+ if((queryString == null
+ || ((chatRoomName.contains(
+ queryString)
+ || chatRoomID.contains(queryString)
+ ))) && isMatching(chatRoomID, pps))
+ {
+ BaseChatRoomSourceContact contact
+ = new BaseChatRoomSourceContact(chatRoomName, chatRoomID, this,
+ pps);
+ synchronized (contactResults)
+ {
+ contactResults.add(contact);
+ }
+
+ if(addQueryResult)
+ {
+ addQueryResult(contact, false);
+ }
+ else
+ {
+ fireContactReceived(contact, false);
+ }
+ }
+ }
+
+ /**
+ * Adds found results to the query results.
+ *
+ * @param pps the protocol provider associated with the found chat room.
+ * @param chatRooms The set of chat rooms based on
+ * BaseChatRoomSourceContact. This is the full set and it will be
+ * filtered according to demands of the queryString.
+ * @param addQueryResult indicates whether we should add the chat room to
+ * the query results or fire an event without adding it to the
+ * results.
+ */
+ private void addChatRooms(final ProtocolProviderService pps,
+ final Set<BaseChatRoomSourceContact> chatRooms,
+ final boolean addQueryResult)
+ {
+ BaseChatRoomSourceContact room;
+ Iterator<BaseChatRoomSourceContact> iterator = chatRooms.iterator();
+ while (iterator.hasNext())
+ {
+ room = iterator.next();
+
+ // Notice the NOT operator at the start ...
+ if (!((queryString == null || (room.getChatRoomName().contains(
+ queryString) || room.getChatRoomID().contains(queryString)))
+ && isMatching(room.getChatRoomID(), pps)))
+ {
+ iterator.remove();
+ }
+ }
+
+ synchronized (contactResults)
+ {
+ contactResults.addAll(chatRooms);
+ }
+
+ if (addQueryResult)
+ {
+ addQueryResults(chatRooms);
+ }
+ else
+ {
+ // TODO Need something to fire one event for multiple contacts.
+ for (SourceContact contact : chatRooms)
+ {
+ fireContactReceived(contact, false);
+ }
+ }
+ }
+
+ @Override
+ public void chatRoomProviderWrapperAdded(ChatRoomProviderWrapper provider)
+ {
+ providerAdded(provider, false);
+ }
+
+ @Override
+ public void chatRoomProviderWrapperRemoved(ChatRoomProviderWrapper provider)
+ {
+ LinkedList<BaseChatRoomSourceContact> tmpContactResults;
+ synchronized (contactResults)
+ {
+ tmpContactResults
+ = new LinkedList<BaseChatRoomSourceContact>(contactResults);
+
+ for(BaseChatRoomSourceContact contact : tmpContactResults)
+ {
+ if(contact.getProvider().equals(provider.getProtocolProvider()))
+ {
+ contactResults.remove(contact);
+ fireContactRemoved(contact);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Clears any listener we used.
+ */
+ private void clearListeners()
+ {
+ mucService.removeChatRoomProviderWrapperListener(this);
+ }
+
+ /**
+ * Cancels this <tt>ContactQuery</tt>.
+ *
+ * @see ContactQuery#cancel()
+ */
+ public void cancel()
+ {
+ clearListeners();
+
+ super.cancel();
+ }
+
+ /**
+ * If query has status changed to cancel, let's clear listeners.
+ * @param status {@link ContactQuery#QUERY_CANCELED},
+ * {@link ContactQuery#QUERY_COMPLETED}
+ */
+ public void setStatus(int status)
+ {
+ if(status == QUERY_CANCELED)
+ clearListeners();
+
+ super.setStatus(status);
+ }
+
+ @Override
+ public void addContactQueryListener(ContactQueryListener l)
+ {
+ super.addContactQueryListener(l);
+ contactQueryListenersCount++;
+ if(contactQueryListenersCount == 1)
+ {
+ initListeners();
+ }
+ }
+
+ @Override
+ public void removeContactQueryListener(ContactQueryListener l)
+ {
+ super.removeContactQueryListener(l);
+ contactQueryListenersCount--;
+ if(contactQueryListenersCount == 0)
+ {
+ clearListeners();
+ }
+ }
+
+ /**
+ * Checks if the contact should be added to results or not.
+ * @param chatRoomID the chat room id associated with the contact.
+ * @param pps the provider of the chat room contact.
+ * @return <tt>true</tt> if the result should be added to the results and
+ * <tt>false</tt> if not.
+ */
+ public boolean isMatching(String chatRoomID, ProtocolProviderService pps)
+ {
+ return (MUCActivator.getMUCService().findChatRoomWrapperFromChatRoomID(
+ chatRoomID, pps) == null);
+ }
+
+ /**
+ * Returns the index of the contact in the contact results list.
+ * @param contact the contact.
+ * @return the index of the contact in the contact results list.
+ */
+ public int indexOf(BaseChatRoomSourceContact contact)
+ {
+ Iterator<BaseChatRoomSourceContact> it = contactResults.iterator();
+ int i = 0;
+ while(it.hasNext())
+ {
+ if(contact.equals(it.next()))
+ {
+ return i;
+ }
+ i++;
+ }
+ return -1;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/neomedia/AbstractDeviceConfigurationListener.java b/src/net/java/sip/communicator/impl/neomedia/AbstractDeviceConfigurationListener.java
index a1814e8..3c84aa2 100644
--- a/src/net/java/sip/communicator/impl/neomedia/AbstractDeviceConfigurationListener.java
+++ b/src/net/java/sip/communicator/impl/neomedia/AbstractDeviceConfigurationListener.java
@@ -187,8 +187,7 @@ public abstract class AbstractDeviceConfigurationListener
body
+ "\r\n\r\n"
+ NeomediaActivator.getResources().getI18NString(
- "impl.media.configform"
- + ".AUDIO_DEVICE_CONFIG_MANAGMENT_CLICK"),
+ "impl.media.configform.AUDIO_DEVICE_CONFIG_MANAGMENT_CLICK"),
null,
extras);
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/AudioDeviceConfigurationListener.java b/src/net/java/sip/communicator/impl/neomedia/AudioDeviceConfigurationListener.java
index 75f1fc3..fe06f15 100644
--- a/src/net/java/sip/communicator/impl/neomedia/AudioDeviceConfigurationListener.java
+++ b/src/net/java/sip/communicator/impl/neomedia/AudioDeviceConfigurationListener.java
@@ -159,10 +159,8 @@ public class AudioDeviceConfigurationListener
capturePropertyChangeEvent.getOldValue()))
{
body.append("\r\n")
- .append(
- r.getI18NString(
- "impl.media.configform"
- + ".AUDIO_DEVICE_SELECTED_AUDIO_IN"))
+ .append(r.getI18NString(
+ "impl.media.configform.AUDIO_DEVICE_SELECTED_AUDIO_IN"))
.append("\r\n\t")
.append(cdi.getName());
selectedHasChanged = true;
@@ -179,10 +177,8 @@ public class AudioDeviceConfigurationListener
playbackPropertyChangeEvent.getOldValue()))
{
body.append("\r\n")
- .append(
- r.getI18NString(
- "impl.media.configform"
- + ".AUDIO_DEVICE_SELECTED_AUDIO_OUT"))
+ .append(r.getI18NString(
+ "impl.media.configform.AUDIO_DEVICE_SELECTED_AUDIO_OUT"))
.append("\r\n\t")
.append(cdi.getName());
selectedHasChanged = true;
@@ -199,10 +195,8 @@ public class AudioDeviceConfigurationListener
notifyPropertyChangeEvent.getOldValue()))
{
body.append("\r\n")
- .append(
- r.getI18NString(
- "impl.media.configform"
- + ".AUDIO_DEVICE_SELECTED_AUDIO_NOTIFICATIONS"))
+ .append(r.getI18NString(
+ "impl.media.configform.AUDIO_DEVICE_SELECTED_AUDIO_NOTIFICATIONS"))
.append("\r\n\t")
.append(cdi.getName());
selectedHasChanged = true;
diff --git a/src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java b/src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java
index 1c5bf93..9bacb4c 100644
--- a/src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java
+++ b/src/net/java/sip/communicator/impl/neomedia/DeviceConfigurationComboBoxModel.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,456 +15,456 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.neomedia;
-
-import java.beans.*;
-import java.util.*;
-
-import javax.media.*;
-import javax.swing.*;
-import javax.swing.event.*;
-
-import org.jitsi.impl.neomedia.device.*;
-import org.jitsi.service.neomedia.*;
-
-/**
- * Implements <tt>ComboBoxModel</tt> for a specific <tt>DeviceConfiguration</tt>
- * so that the latter may be displayed and manipulated in the user interface as
- * a combo box.
- *
- * @author Lyubomir Marinov
- * @author Damian Minkov
- */
-public class DeviceConfigurationComboBoxModel
- implements ComboBoxModel,
- ListModel,
- PropertyChangeListener
-{
- /**
- * Type of the model - audio.
- */
- public static final int AUDIO = 1;
-
- /**
- * Audio Capture Device.
- */
- public static final int AUDIO_CAPTURE = 3;
-
- /**
- * Audio device for notification sounds.
- */
- public static final int AUDIO_NOTIFY = 5;
-
- /**
- * Audio playback device.
- */
- public static final int AUDIO_PLAYBACK = 4;
-
- /**
- * Type of the model - video.
- */
- public static final int VIDEO = 2;
-
- private AudioSystem[] audioSystems;
-
- /**
- * The current device configuration.
- */
- private final DeviceConfiguration deviceConfiguration;
-
- /**
- * All the devices.
- */
- private CaptureDevice[] devices;
-
- /**
- * The <tt>ListDataListener</tt>s registered with this instance.
- */
- private final List<ListDataListener> listeners
- = new ArrayList<ListDataListener>();
-
- /**
- * The type of the media for this combo.
- */
- private final int type;
-
- /**
- * Creates device combobox model
- * @param parent the parent component
- * @param deviceConfiguration the current device configuration
- * @param type the device - audio/video
- */
- public DeviceConfigurationComboBoxModel(
- DeviceConfiguration deviceConfiguration,
- int type)
- {
- if (deviceConfiguration == null)
- throw new IllegalArgumentException("deviceConfiguration");
- if ((type != AUDIO)
- && (type != AUDIO_CAPTURE)
- && (type != AUDIO_NOTIFY)
- && (type != AUDIO_PLAYBACK)
- && (type != VIDEO))
- throw new IllegalArgumentException("type");
-
- this.deviceConfiguration = deviceConfiguration;
- this.type = type;
-
- if (type == AUDIO
- || type == AUDIO_CAPTURE
- || type == AUDIO_NOTIFY
- || type == AUDIO_PLAYBACK)
- {
- deviceConfiguration.addPropertyChangeListener(this);
- }
- }
-
- public void addListDataListener(ListDataListener listener)
- {
- if (listener == null)
- throw new IllegalArgumentException("listener");
-
- if (!listeners.contains(listener))
- listeners.add(listener);
- }
-
- /**
- * Change of the content.
- * @param index0 from index.
- * @param index1 to index.
- */
- protected void fireContentsChanged(int index0, int index1)
- {
- ListDataListener[] listeners
- = this.listeners.toArray(
- new ListDataListener[this.listeners.size()]);
- ListDataEvent event
- = new ListDataEvent(
- this,
- ListDataEvent.CONTENTS_CHANGED,
- index0,
- index1);
-
- for (ListDataListener listener : listeners)
- listener.contentsChanged(event);
- }
-
- private AudioSystem[] getAudioSystems()
- {
- if (type != AUDIO)
- throw new IllegalStateException("type");
-
- audioSystems = deviceConfiguration.getAvailableAudioSystems();
- return audioSystems;
- }
-
- /**
- * Extracts the devices for the current type.
- * @return the devices.
- */
- private CaptureDevice[] getDevices()
- {
- if (type == AUDIO)
- throw new IllegalStateException("type");
-
- if (devices != null)
- return devices;
-
- AudioSystem audioSystem;
- List<? extends CaptureDeviceInfo> infos = null;
-
- switch (type)
- {
- case AUDIO_CAPTURE:
- audioSystem = deviceConfiguration.getAudioSystem();
- infos = (audioSystem == null)
- ? null
- : audioSystem.getDevices(AudioSystem.DataFlow.CAPTURE);
- break;
- case AUDIO_NOTIFY:
- audioSystem = deviceConfiguration.getAudioSystem();
- infos = (audioSystem == null)
- ? null
- : audioSystem.getDevices(AudioSystem.DataFlow.NOTIFY);
- break;
- case AUDIO_PLAYBACK:
- audioSystem = deviceConfiguration.getAudioSystem();
- infos = (audioSystem == null)
- ? null
- : audioSystem.getDevices(AudioSystem.DataFlow.PLAYBACK);
- break;
- case VIDEO:
- infos = deviceConfiguration.getAvailableVideoCaptureDevices(
- MediaUseCase.CALL);
- break;
- default:
- throw new IllegalStateException("type");
- }
-
- final int deviceCount = (infos == null) ? 0 : infos.size();
- devices = new CaptureDevice[deviceCount + 1];
-
- if (deviceCount > 0)
- {
- for (int i = 0; i < deviceCount; i++)
- devices[i] = new CaptureDevice(infos.get(i));
- }
- devices[deviceCount] = new CaptureDevice(null);
-
- return devices;
- }
-
- public Object getElementAt(int index)
- {
- if (type == AUDIO)
- return getAudioSystems()[index];
- else
- return getDevices()[index];
- }
-
- /**
- * Extracts the devices selected by the configuration.
- * @return <tt>CaptureDevice</tt> selected
- */
- private CaptureDevice getSelectedDevice()
- {
- AudioSystem audioSystem;
- CaptureDeviceInfo info;
-
- switch (type)
- {
- case AUDIO_CAPTURE:
- audioSystem = deviceConfiguration.getAudioSystem();
- info = (audioSystem == null)
- ? null
- : audioSystem.getSelectedDevice(AudioSystem.DataFlow.CAPTURE);
- break;
- case AUDIO_NOTIFY:
- audioSystem = deviceConfiguration.getAudioSystem();
- info = (audioSystem == null)
- ? null
- : audioSystem.getSelectedDevice(AudioSystem.DataFlow.NOTIFY);
- break;
- case AUDIO_PLAYBACK:
- audioSystem = deviceConfiguration.getAudioSystem();
- info = (audioSystem == null)
- ? null
- : audioSystem.getSelectedDevice(AudioSystem.DataFlow.PLAYBACK);
- break;
- case VIDEO:
- info = deviceConfiguration.getVideoCaptureDevice(MediaUseCase.ANY);
- break;
- default:
- throw new IllegalStateException("type");
- }
-
- for (CaptureDevice device : getDevices())
- {
- if (device.equals(info))
- return device;
- }
- return null;
- }
-
- public Object getSelectedItem()
- {
- if (type == AUDIO)
- return deviceConfiguration.getAudioSystem();
- else
- return getSelectedDevice();
- }
-
- public int getSize()
- {
- if (type == AUDIO)
- return getAudioSystems().length;
- else
- return getDevices().length;
- }
-
- /**
- * Notifies this instance about changes in the values of the properties of
- * {@link #deviceConfiguration} so that this instance keeps itself
- * up-to-date with respect to the list of devices.
- *
- * @param ev a <tt>PropertyChangeEvent</tt> which describes the name of the
- * property whose value has changed and the old and new values of that
- * property
- */
- public void propertyChange(final PropertyChangeEvent ev)
- {
- if (DeviceConfiguration.PROP_AUDIO_SYSTEM_DEVICES.equals(
- ev.getPropertyName()))
- {
- if (SwingUtilities.isEventDispatchThread())
- {
- audioSystems = null;
- devices = null;
- fireContentsChanged(0, getSize() - 1);
- }
- else
- {
- SwingUtilities.invokeLater(
- new Runnable()
- {
- public void run()
- {
- propertyChange(ev);
- }
- });
- }
- }
- }
-
- public void removeListDataListener(ListDataListener listener)
- {
- if (listener == null)
- throw new IllegalArgumentException("listener");
-
- listeners.remove(listener);
- }
-
- /**
- * Selects and saves the new choice.
- * @param device the device we choose.
- */
- private void setSelectedDevice(CaptureDevice device)
- {
- // We cannot clear the selection of DeviceConfiguration.
- if (device == null)
- return;
-
- CaptureDevice selectedDevice = getSelectedDevice();
-
- if (selectedDevice != device)
- {
- AudioSystem audioSystem;
-
- switch (type)
- {
- case AUDIO_CAPTURE:
- audioSystem = deviceConfiguration.getAudioSystem();
- if (audioSystem != null)
- {
- audioSystem.setDevice(
- AudioSystem.DataFlow.CAPTURE,
- ((CaptureDeviceInfo2) device.info),
- true);
- }
- break;
- case AUDIO_NOTIFY:
- audioSystem = deviceConfiguration.getAudioSystem();
- if (audioSystem != null)
- {
- audioSystem.setDevice(
- AudioSystem.DataFlow.NOTIFY,
- ((CaptureDeviceInfo2) device.info),
- true);
- }
- break;
- case AUDIO_PLAYBACK:
- audioSystem = deviceConfiguration.getAudioSystem();
- if (audioSystem != null)
- {
- audioSystem.setDevice(
- AudioSystem.DataFlow.PLAYBACK,
- ((CaptureDeviceInfo2) device.info),
- true);
- }
- break;
- case VIDEO:
- deviceConfiguration.setVideoCaptureDevice(device.info, true);
- break;
- }
-
- fireContentsChanged(-1, -1);
- }
- }
-
- public void setSelectedItem(Object item)
- {
- if (type == AUDIO)
- {
- AudioSystem audioSystem = (AudioSystem) item;
-
- if(!audioSystem.equals(deviceConfiguration.getAudioSystem()))
- {
- deviceConfiguration.setAudioSystem(audioSystem, true);
- fireContentsChanged(-1, -1);
- }
- }
- else
- setSelectedDevice((CaptureDevice) item);
- }
-
- /**
- * Encapsulates a <tt>CaptureDeviceInfo</tt> for the purposes of its display
- * in the user interface.
- */
- public static class CaptureDevice
- {
- /**
- * The encapsulated info.
- */
- public final CaptureDeviceInfo info;
-
- /**
- * Creates the wrapper.
- * @param info the info object we wrap.
- */
- public CaptureDevice(CaptureDeviceInfo info)
- {
- this.info = info;
- }
-
- /**
- * Determines whether the <tt>CaptureDeviceInfo</tt> encapsulated by
- * this instance is equal (by value) to a specific
- * <tt>CaptureDeviceInfo</tt>.
- *
- * @param cdi the <tt>CaptureDeviceInfo</tt> to be determined whether it
- * is equal (by value) to the <tt>CaptureDeviceInfo</tt> encapsulated by
- * this instance
- * @return <tt>true</tt> if the <tt>CaptureDeviceInfo</tt> encapsulated
- * by this instance is equal (by value) to the specified <tt>cdi</tt>;
- * otherwise, <tt>false</tt>
- */
- public boolean equals(CaptureDeviceInfo cdi)
- {
- return (info == null) ? (cdi == null) : info.equals(cdi);
- }
-
- /**
- * Gets a human-readable <tt>String</tt> representation of this
- * instance.
- *
- * @return a <tt>String</tt> value which is a human-readable
- * representation of this instance
- */
- @Override
- public String toString()
- {
- String s;
-
- if(info == null)
- {
- s
- = NeomediaActivator.getResources().getI18NString(
- "impl.media.configform.NO_DEVICE");
- }
- else
- {
- s = info.getName();
- if(info instanceof CaptureDeviceInfo2)
- {
- String transportType
- = ((CaptureDeviceInfo2) info).getTransportType();
-
- if(transportType != null)
- s += " (" + transportType + ")";
- }
- }
- return s;
- }
- }
-}
+package net.java.sip.communicator.impl.neomedia;
+
+import java.beans.*;
+import java.util.*;
+
+import javax.media.*;
+import javax.swing.*;
+import javax.swing.event.*;
+
+import org.jitsi.impl.neomedia.device.*;
+import org.jitsi.service.neomedia.*;
+
+/**
+ * Implements <tt>ComboBoxModel</tt> for a specific <tt>DeviceConfiguration</tt>
+ * so that the latter may be displayed and manipulated in the user interface as
+ * a combo box.
+ *
+ * @author Lyubomir Marinov
+ * @author Damian Minkov
+ */
+public class DeviceConfigurationComboBoxModel
+ implements ComboBoxModel,
+ ListModel,
+ PropertyChangeListener
+{
+ /**
+ * Type of the model - audio.
+ */
+ public static final int AUDIO = 1;
+
+ /**
+ * Audio Capture Device.
+ */
+ public static final int AUDIO_CAPTURE = 3;
+
+ /**
+ * Audio device for notification sounds.
+ */
+ public static final int AUDIO_NOTIFY = 5;
+
+ /**
+ * Audio playback device.
+ */
+ public static final int AUDIO_PLAYBACK = 4;
+
+ /**
+ * Type of the model - video.
+ */
+ public static final int VIDEO = 2;
+
+ private AudioSystem[] audioSystems;
+
+ /**
+ * The current device configuration.
+ */
+ private final DeviceConfiguration deviceConfiguration;
+
+ /**
+ * All the devices.
+ */
+ private CaptureDevice[] devices;
+
+ /**
+ * The <tt>ListDataListener</tt>s registered with this instance.
+ */
+ private final List<ListDataListener> listeners
+ = new ArrayList<ListDataListener>();
+
+ /**
+ * The type of the media for this combo.
+ */
+ private final int type;
+
+ /**
+ * Creates device combobox model
+ * @param parent the parent component
+ * @param deviceConfiguration the current device configuration
+ * @param type the device - audio/video
+ */
+ public DeviceConfigurationComboBoxModel(
+ DeviceConfiguration deviceConfiguration,
+ int type)
+ {
+ if (deviceConfiguration == null)
+ throw new IllegalArgumentException("deviceConfiguration");
+ if ((type != AUDIO)
+ && (type != AUDIO_CAPTURE)
+ && (type != AUDIO_NOTIFY)
+ && (type != AUDIO_PLAYBACK)
+ && (type != VIDEO))
+ throw new IllegalArgumentException("type");
+
+ this.deviceConfiguration = deviceConfiguration;
+ this.type = type;
+
+ if (type == AUDIO
+ || type == AUDIO_CAPTURE
+ || type == AUDIO_NOTIFY
+ || type == AUDIO_PLAYBACK)
+ {
+ deviceConfiguration.addPropertyChangeListener(this);
+ }
+ }
+
+ public void addListDataListener(ListDataListener listener)
+ {
+ if (listener == null)
+ throw new IllegalArgumentException("listener");
+
+ if (!listeners.contains(listener))
+ listeners.add(listener);
+ }
+
+ /**
+ * Change of the content.
+ * @param index0 from index.
+ * @param index1 to index.
+ */
+ protected void fireContentsChanged(int index0, int index1)
+ {
+ ListDataListener[] listeners
+ = this.listeners.toArray(
+ new ListDataListener[this.listeners.size()]);
+ ListDataEvent event
+ = new ListDataEvent(
+ this,
+ ListDataEvent.CONTENTS_CHANGED,
+ index0,
+ index1);
+
+ for (ListDataListener listener : listeners)
+ listener.contentsChanged(event);
+ }
+
+ private AudioSystem[] getAudioSystems()
+ {
+ if (type != AUDIO)
+ throw new IllegalStateException("type");
+
+ audioSystems = deviceConfiguration.getAvailableAudioSystems();
+ return audioSystems;
+ }
+
+ /**
+ * Extracts the devices for the current type.
+ * @return the devices.
+ */
+ private CaptureDevice[] getDevices()
+ {
+ if (type == AUDIO)
+ throw new IllegalStateException("type");
+
+ if (devices != null)
+ return devices;
+
+ AudioSystem audioSystem;
+ List<? extends CaptureDeviceInfo> infos = null;
+
+ switch (type)
+ {
+ case AUDIO_CAPTURE:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ infos = (audioSystem == null)
+ ? null
+ : audioSystem.getDevices(AudioSystem.DataFlow.CAPTURE);
+ break;
+ case AUDIO_NOTIFY:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ infos = (audioSystem == null)
+ ? null
+ : audioSystem.getDevices(AudioSystem.DataFlow.NOTIFY);
+ break;
+ case AUDIO_PLAYBACK:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ infos = (audioSystem == null)
+ ? null
+ : audioSystem.getDevices(AudioSystem.DataFlow.PLAYBACK);
+ break;
+ case VIDEO:
+ infos = deviceConfiguration.getAvailableVideoCaptureDevices(
+ MediaUseCase.CALL);
+ break;
+ default:
+ throw new IllegalStateException("type");
+ }
+
+ final int deviceCount = (infos == null) ? 0 : infos.size();
+ devices = new CaptureDevice[deviceCount + 1];
+
+ if (deviceCount > 0)
+ {
+ for (int i = 0; i < deviceCount; i++)
+ devices[i] = new CaptureDevice(infos.get(i));
+ }
+ devices[deviceCount] = new CaptureDevice(null);
+
+ return devices;
+ }
+
+ public Object getElementAt(int index)
+ {
+ if (type == AUDIO)
+ return getAudioSystems()[index];
+ else
+ return getDevices()[index];
+ }
+
+ /**
+ * Extracts the devices selected by the configuration.
+ * @return <tt>CaptureDevice</tt> selected
+ */
+ private CaptureDevice getSelectedDevice()
+ {
+ AudioSystem audioSystem;
+ CaptureDeviceInfo info;
+
+ switch (type)
+ {
+ case AUDIO_CAPTURE:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ info = (audioSystem == null)
+ ? null
+ : audioSystem.getSelectedDevice(AudioSystem.DataFlow.CAPTURE);
+ break;
+ case AUDIO_NOTIFY:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ info = (audioSystem == null)
+ ? null
+ : audioSystem.getSelectedDevice(AudioSystem.DataFlow.NOTIFY);
+ break;
+ case AUDIO_PLAYBACK:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ info = (audioSystem == null)
+ ? null
+ : audioSystem.getSelectedDevice(AudioSystem.DataFlow.PLAYBACK);
+ break;
+ case VIDEO:
+ info = deviceConfiguration.getVideoCaptureDevice(MediaUseCase.ANY);
+ break;
+ default:
+ throw new IllegalStateException("type");
+ }
+
+ for (CaptureDevice device : getDevices())
+ {
+ if (device.equals(info))
+ return device;
+ }
+ return null;
+ }
+
+ public Object getSelectedItem()
+ {
+ if (type == AUDIO)
+ return deviceConfiguration.getAudioSystem();
+ else
+ return getSelectedDevice();
+ }
+
+ public int getSize()
+ {
+ if (type == AUDIO)
+ return getAudioSystems().length;
+ else
+ return getDevices().length;
+ }
+
+ /**
+ * Notifies this instance about changes in the values of the properties of
+ * {@link #deviceConfiguration} so that this instance keeps itself
+ * up-to-date with respect to the list of devices.
+ *
+ * @param ev a <tt>PropertyChangeEvent</tt> which describes the name of the
+ * property whose value has changed and the old and new values of that
+ * property
+ */
+ public void propertyChange(final PropertyChangeEvent ev)
+ {
+ if (DeviceConfiguration.PROP_AUDIO_SYSTEM_DEVICES.equals(
+ ev.getPropertyName()))
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ {
+ audioSystems = null;
+ devices = null;
+ fireContentsChanged(0, getSize() - 1);
+ }
+ else
+ {
+ SwingUtilities.invokeLater(
+ new Runnable()
+ {
+ public void run()
+ {
+ propertyChange(ev);
+ }
+ });
+ }
+ }
+ }
+
+ public void removeListDataListener(ListDataListener listener)
+ {
+ if (listener == null)
+ throw new IllegalArgumentException("listener");
+
+ listeners.remove(listener);
+ }
+
+ /**
+ * Selects and saves the new choice.
+ * @param device the device we choose.
+ */
+ private void setSelectedDevice(CaptureDevice device)
+ {
+ // We cannot clear the selection of DeviceConfiguration.
+ if (device == null)
+ return;
+
+ CaptureDevice selectedDevice = getSelectedDevice();
+
+ if (selectedDevice != device)
+ {
+ AudioSystem audioSystem;
+
+ switch (type)
+ {
+ case AUDIO_CAPTURE:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ if (audioSystem != null)
+ {
+ audioSystem.setDevice(
+ AudioSystem.DataFlow.CAPTURE,
+ ((CaptureDeviceInfo2) device.info),
+ true);
+ }
+ break;
+ case AUDIO_NOTIFY:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ if (audioSystem != null)
+ {
+ audioSystem.setDevice(
+ AudioSystem.DataFlow.NOTIFY,
+ ((CaptureDeviceInfo2) device.info),
+ true);
+ }
+ break;
+ case AUDIO_PLAYBACK:
+ audioSystem = deviceConfiguration.getAudioSystem();
+ if (audioSystem != null)
+ {
+ audioSystem.setDevice(
+ AudioSystem.DataFlow.PLAYBACK,
+ ((CaptureDeviceInfo2) device.info),
+ true);
+ }
+ break;
+ case VIDEO:
+ deviceConfiguration.setVideoCaptureDevice(device.info, true);
+ break;
+ }
+
+ fireContentsChanged(-1, -1);
+ }
+ }
+
+ public void setSelectedItem(Object item)
+ {
+ if (type == AUDIO)
+ {
+ AudioSystem audioSystem = (AudioSystem) item;
+
+ if(!audioSystem.equals(deviceConfiguration.getAudioSystem()))
+ {
+ deviceConfiguration.setAudioSystem(audioSystem, true);
+ fireContentsChanged(-1, -1);
+ }
+ }
+ else
+ setSelectedDevice((CaptureDevice) item);
+ }
+
+ /**
+ * Encapsulates a <tt>CaptureDeviceInfo</tt> for the purposes of its display
+ * in the user interface.
+ */
+ public static class CaptureDevice
+ {
+ /**
+ * The encapsulated info.
+ */
+ public final CaptureDeviceInfo info;
+
+ /**
+ * Creates the wrapper.
+ * @param info the info object we wrap.
+ */
+ public CaptureDevice(CaptureDeviceInfo info)
+ {
+ this.info = info;
+ }
+
+ /**
+ * Determines whether the <tt>CaptureDeviceInfo</tt> encapsulated by
+ * this instance is equal (by value) to a specific
+ * <tt>CaptureDeviceInfo</tt>.
+ *
+ * @param cdi the <tt>CaptureDeviceInfo</tt> to be determined whether it
+ * is equal (by value) to the <tt>CaptureDeviceInfo</tt> encapsulated by
+ * this instance
+ * @return <tt>true</tt> if the <tt>CaptureDeviceInfo</tt> encapsulated
+ * by this instance is equal (by value) to the specified <tt>cdi</tt>;
+ * otherwise, <tt>false</tt>
+ */
+ public boolean equals(CaptureDeviceInfo cdi)
+ {
+ return (info == null) ? (cdi == null) : info.equals(cdi);
+ }
+
+ /**
+ * Gets a human-readable <tt>String</tt> representation of this
+ * instance.
+ *
+ * @return a <tt>String</tt> value which is a human-readable
+ * representation of this instance
+ */
+ @Override
+ public String toString()
+ {
+ String s;
+
+ if(info == null)
+ {
+ s
+ = NeomediaActivator.getResources().getI18NString(
+ "impl.media.configform.NO_DEVICE");
+ }
+ else
+ {
+ s = info.getName();
+ if(info instanceof CaptureDeviceInfo2)
+ {
+ String transportType
+ = ((CaptureDeviceInfo2) info).getTransportType();
+
+ if(transportType != null)
+ s += " (" + transportType + ")";
+ }
+ }
+ return s;
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/neomedia/EncodingConfigurationTableModel.java b/src/net/java/sip/communicator/impl/neomedia/EncodingConfigurationTableModel.java
index 925184c..f8cf447 100644
--- a/src/net/java/sip/communicator/impl/neomedia/EncodingConfigurationTableModel.java
+++ b/src/net/java/sip/communicator/impl/neomedia/EncodingConfigurationTableModel.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,272 +15,272 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.neomedia;
-
-import java.util.*;
-
-import javax.swing.table.*;
-
-import net.java.sip.communicator.plugin.desktoputil.*;
-
-import org.jitsi.impl.neomedia.*;
-import org.jitsi.impl.neomedia.format.*;
-import org.jitsi.service.neomedia.*;
-import org.jitsi.service.neomedia.codec.*;
-import org.jitsi.service.neomedia.format.*;
-
-/**
- * Implements {@link TableModel} for {@link EncodingConfiguration}.
- *
- * @author Lyubomir Marinov
- */
-public class EncodingConfigurationTableModel
- extends MoveableTableModel
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- private final EncodingConfiguration encodingConfiguration;
-
- private MediaFormat[] encodings;
-
- private final MediaType type;
-
- /**
- * Constructor.
- *
- * @param encodingConfiguration the encoding configuration
- * @param type media type
- */
- public EncodingConfigurationTableModel(int type,
- EncodingConfiguration encodingConfiguration)
- {
- if (encodingConfiguration == null)
- throw new IllegalArgumentException("encodingConfiguration");
- this.encodingConfiguration = encodingConfiguration;
-
- switch (type)
- {
- case DeviceConfigurationComboBoxModel.AUDIO:
- this.type = MediaType.AUDIO;
- break;
- case DeviceConfigurationComboBoxModel.VIDEO:
- this.type = MediaType.VIDEO;
- break;
- default:
- throw new IllegalArgumentException("type");
- }
- }
-
- @Override
- public Class<?> getColumnClass(int columnIndex)
- {
- return
- (columnIndex == 0)
- ? Boolean.class
- : super.getColumnClass(columnIndex);
- }
-
- public int getColumnCount()
- {
- return 2;
- }
-
- private MediaFormat[] getEncodings()
- {
- if (encodings != null)
- return encodings;
-
- MediaFormat[] availableEncodings
- = encodingConfiguration.getAllEncodings(type);
- int encodingCount = availableEncodings.length;
-
- if (encodingCount < 1)
- encodings = MediaUtils.EMPTY_MEDIA_FORMATS;
- else
- {
- /*
- * The MediaFormats will be displayed by encoding (name) and clock
- * rate and EncodingConfiguration will store them that way so this
- * TableModel should better display unique encoding-clock rate
- * pairs.
- */
- HashMap<String, MediaFormat> availableEncodingSet
- = new HashMap<String, MediaFormat>();
-
- for (MediaFormat availableEncoding : availableEncodings)
- {
- availableEncodingSet.put(
- availableEncoding.getEncoding()
- + "/"
- + availableEncoding.getClockRateString(),
- availableEncoding);
- }
- availableEncodings
- = availableEncodingSet.values().toArray(
- MediaUtils.EMPTY_MEDIA_FORMATS);
- encodingCount = availableEncodings.length;
-
- encodings = new MediaFormat[encodingCount];
- System
- .arraycopy(availableEncodings, 0, encodings, 0, encodingCount);
- // Display the encodings in decreasing priority.
- Arrays
- .sort(encodings, 0, encodingCount, new Comparator<MediaFormat>()
- {
- public int compare(MediaFormat format0, MediaFormat format1)
- {
- int ret
- = encodingConfiguration.getPriority(format1)
- - encodingConfiguration.getPriority(format0);
-
- if (ret == 0)
- {
- /*
- * In the cases of equal priorities, display them
- * sorted by encoding name in increasing order.
- */
- ret
- = format0.getEncoding().compareToIgnoreCase(
- format1.getEncoding());
- if (ret == 0)
- {
- /*
- * In the cases of equal priorities and equal
- * encoding names, display them sorted by clock
- * rate in decreasing order.
- */
- ret
- = Double.compare(
- format1.getClockRate(),
- format0.getClockRate());
- }
- }
- return ret;
- }
- });
- }
- return encodings;
- }
-
- private int[] getPriorities()
- {
- MediaFormat[] encodings = getEncodings();
- final int count = encodings.length;
- int[] priorities = new int[count];
-
- for (int i = 0; i < count; i++)
- {
- int priority = encodingConfiguration.getPriority(encodings[i]);
-
- priorities[i] = (priority > 0) ? (count - i) : 0;
- }
- return priorities;
- }
-
- public int getRowCount()
- {
- return getEncodings().length;
- }
-
- public Object getValueAt(int rowIndex, int columnIndex)
- {
- MediaFormat encoding = getEncodings()[rowIndex];
-
- switch (columnIndex)
- {
- case 0:
- return (encodingConfiguration.getPriority(encoding) > 0);
- case 1:
- if (MediaType.VIDEO.equals(encoding.getMediaType())
- && (VideoMediaFormatImpl.DEFAULT_CLOCK_RATE
- == encoding.getClockRate()))
- return encoding.getEncoding();
- else
- {
- return encoding.getEncoding()
- + "/"
- + encoding.getRealUsedClockRateString();
- }
- default:
- return null;
- }
- }
-
- @Override
- public boolean isCellEditable(int rowIndex, int columnIndex)
- {
- return (columnIndex == 0);
- }
-
- /**
- * Move the row.
- *
- * @param rowIndex index of the row
- * @param up true to move up, false to move down
- * @return the next row index
- */
- @Override
- public int move(int rowIndex, boolean up)
- {
- if (up)
- {
- if (rowIndex <= 0)
- throw new IllegalArgumentException("rowIndex");
-
- return move(rowIndex - 1, false) - 1;
- }
-
- if (rowIndex >= (getRowCount() - 1))
- throw new IllegalArgumentException("rowIndex");
-
- int[] priorities = getPriorities();
- final int nextRowIndex = rowIndex + 1;
-
- if (priorities[rowIndex] > 0)
- priorities[rowIndex] = priorities.length - nextRowIndex;
- if (priorities[nextRowIndex] > 0)
- priorities[nextRowIndex] = priorities.length - rowIndex;
- setPriorities(priorities);
-
- MediaFormat swap = encodings[rowIndex];
-
- encodings[rowIndex] = encodings[nextRowIndex];
- encodings[nextRowIndex] = swap;
-
- fireTableRowsUpdated(rowIndex, nextRowIndex);
- return nextRowIndex;
- }
-
- private void setPriorities(int[] priorities)
- {
- final int count = encodings.length;
-
- if (priorities.length != count)
- throw new IllegalArgumentException("priorities");
- for (int i = 0; i < count; i++)
- {
- encodingConfiguration.setPriority(encodings[i], priorities[i]);
- }
- }
-
- @Override
- public void setValueAt(Object value, int rowIndex, int columnIndex)
- {
- if ((columnIndex == 0) && (value instanceof Boolean))
- {
- int priority
- = ((Boolean) value) ? (getPriorities().length - rowIndex) : 0;
- MediaFormat encoding = encodings[rowIndex];
-
-
- // We fire the update event before setting the configuration
- // property in order to have more reactive user interface.
- fireTableCellUpdated(rowIndex, columnIndex);
-
- encodingConfiguration.setPriority(encoding, priority);
- }
- }
-}
+package net.java.sip.communicator.impl.neomedia;
+
+import java.util.*;
+
+import javax.swing.table.*;
+
+import net.java.sip.communicator.plugin.desktoputil.*;
+
+import org.jitsi.impl.neomedia.*;
+import org.jitsi.impl.neomedia.format.*;
+import org.jitsi.service.neomedia.*;
+import org.jitsi.service.neomedia.codec.*;
+import org.jitsi.service.neomedia.format.*;
+
+/**
+ * Implements {@link TableModel} for {@link EncodingConfiguration}.
+ *
+ * @author Lyubomir Marinov
+ */
+public class EncodingConfigurationTableModel
+ extends MoveableTableModel
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ private final EncodingConfiguration encodingConfiguration;
+
+ private MediaFormat[] encodings;
+
+ private final MediaType type;
+
+ /**
+ * Constructor.
+ *
+ * @param encodingConfiguration the encoding configuration
+ * @param type media type
+ */
+ public EncodingConfigurationTableModel(int type,
+ EncodingConfiguration encodingConfiguration)
+ {
+ if (encodingConfiguration == null)
+ throw new IllegalArgumentException("encodingConfiguration");
+ this.encodingConfiguration = encodingConfiguration;
+
+ switch (type)
+ {
+ case DeviceConfigurationComboBoxModel.AUDIO:
+ this.type = MediaType.AUDIO;
+ break;
+ case DeviceConfigurationComboBoxModel.VIDEO:
+ this.type = MediaType.VIDEO;
+ break;
+ default:
+ throw new IllegalArgumentException("type");
+ }
+ }
+
+ @Override
+ public Class<?> getColumnClass(int columnIndex)
+ {
+ return
+ (columnIndex == 0)
+ ? Boolean.class
+ : super.getColumnClass(columnIndex);
+ }
+
+ public int getColumnCount()
+ {
+ return 2;
+ }
+
+ private MediaFormat[] getEncodings()
+ {
+ if (encodings != null)
+ return encodings;
+
+ MediaFormat[] availableEncodings
+ = encodingConfiguration.getAllEncodings(type);
+ int encodingCount = availableEncodings.length;
+
+ if (encodingCount < 1)
+ encodings = MediaUtils.EMPTY_MEDIA_FORMATS;
+ else
+ {
+ /*
+ * The MediaFormats will be displayed by encoding (name) and clock
+ * rate and EncodingConfiguration will store them that way so this
+ * TableModel should better display unique encoding-clock rate
+ * pairs.
+ */
+ HashMap<String, MediaFormat> availableEncodingSet
+ = new HashMap<String, MediaFormat>();
+
+ for (MediaFormat availableEncoding : availableEncodings)
+ {
+ availableEncodingSet.put(
+ availableEncoding.getEncoding()
+ + "/"
+ + availableEncoding.getClockRateString(),
+ availableEncoding);
+ }
+ availableEncodings
+ = availableEncodingSet.values().toArray(
+ MediaUtils.EMPTY_MEDIA_FORMATS);
+ encodingCount = availableEncodings.length;
+
+ encodings = new MediaFormat[encodingCount];
+ System
+ .arraycopy(availableEncodings, 0, encodings, 0, encodingCount);
+ // Display the encodings in decreasing priority.
+ Arrays
+ .sort(encodings, 0, encodingCount, new Comparator<MediaFormat>()
+ {
+ public int compare(MediaFormat format0, MediaFormat format1)
+ {
+ int ret
+ = encodingConfiguration.getPriority(format1)
+ - encodingConfiguration.getPriority(format0);
+
+ if (ret == 0)
+ {
+ /*
+ * In the cases of equal priorities, display them
+ * sorted by encoding name in increasing order.
+ */
+ ret
+ = format0.getEncoding().compareToIgnoreCase(
+ format1.getEncoding());
+ if (ret == 0)
+ {
+ /*
+ * In the cases of equal priorities and equal
+ * encoding names, display them sorted by clock
+ * rate in decreasing order.
+ */
+ ret
+ = Double.compare(
+ format1.getClockRate(),
+ format0.getClockRate());
+ }
+ }
+ return ret;
+ }
+ });
+ }
+ return encodings;
+ }
+
+ private int[] getPriorities()
+ {
+ MediaFormat[] encodings = getEncodings();
+ final int count = encodings.length;
+ int[] priorities = new int[count];
+
+ for (int i = 0; i < count; i++)
+ {
+ int priority = encodingConfiguration.getPriority(encodings[i]);
+
+ priorities[i] = (priority > 0) ? (count - i) : 0;
+ }
+ return priorities;
+ }
+
+ public int getRowCount()
+ {
+ return getEncodings().length;
+ }
+
+ public Object getValueAt(int rowIndex, int columnIndex)
+ {
+ MediaFormat encoding = getEncodings()[rowIndex];
+
+ switch (columnIndex)
+ {
+ case 0:
+ return (encodingConfiguration.getPriority(encoding) > 0);
+ case 1:
+ if (MediaType.VIDEO.equals(encoding.getMediaType())
+ && (VideoMediaFormatImpl.DEFAULT_CLOCK_RATE
+ == encoding.getClockRate()))
+ return encoding.getEncoding();
+ else
+ {
+ return encoding.getEncoding()
+ + "/"
+ + encoding.getRealUsedClockRateString();
+ }
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isCellEditable(int rowIndex, int columnIndex)
+ {
+ return (columnIndex == 0);
+ }
+
+ /**
+ * Move the row.
+ *
+ * @param rowIndex index of the row
+ * @param up true to move up, false to move down
+ * @return the next row index
+ */
+ @Override
+ public int move(int rowIndex, boolean up)
+ {
+ if (up)
+ {
+ if (rowIndex <= 0)
+ throw new IllegalArgumentException("rowIndex");
+
+ return move(rowIndex - 1, false) - 1;
+ }
+
+ if (rowIndex >= (getRowCount() - 1))
+ throw new IllegalArgumentException("rowIndex");
+
+ int[] priorities = getPriorities();
+ final int nextRowIndex = rowIndex + 1;
+
+ if (priorities[rowIndex] > 0)
+ priorities[rowIndex] = priorities.length - nextRowIndex;
+ if (priorities[nextRowIndex] > 0)
+ priorities[nextRowIndex] = priorities.length - rowIndex;
+ setPriorities(priorities);
+
+ MediaFormat swap = encodings[rowIndex];
+
+ encodings[rowIndex] = encodings[nextRowIndex];
+ encodings[nextRowIndex] = swap;
+
+ fireTableRowsUpdated(rowIndex, nextRowIndex);
+ return nextRowIndex;
+ }
+
+ private void setPriorities(int[] priorities)
+ {
+ final int count = encodings.length;
+
+ if (priorities.length != count)
+ throw new IllegalArgumentException("priorities");
+ for (int i = 0; i < count; i++)
+ {
+ encodingConfiguration.setPriority(encodings[i], priorities[i]);
+ }
+ }
+
+ @Override
+ public void setValueAt(Object value, int rowIndex, int columnIndex)
+ {
+ if ((columnIndex == 0) && (value instanceof Boolean))
+ {
+ int priority
+ = ((Boolean) value) ? (getPriorities().length - rowIndex) : 0;
+ MediaFormat encoding = encodings[rowIndex];
+
+
+ // We fire the update event before setting the configuration
+ // property in order to have more reactive user interface.
+ fireTableCellUpdated(rowIndex, columnIndex);
+
+ encodingConfiguration.setPriority(encoding, priority);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaConfigurationImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaConfigurationImpl.java
index d561c92..00e8aac 100644
--- a/src/net/java/sip/communicator/impl/neomedia/MediaConfigurationImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/MediaConfigurationImpl.java
@@ -1773,8 +1773,7 @@ public class MediaConfigurationImpl
{
String noAvailableAudioDevice
= NeomediaActivator.getResources().getI18NString(
- "impl.media.configform"
- + ".NO_AVAILABLE_AUDIO_DEVICE");
+ "impl.media.configform.NO_AVAILABLE_AUDIO_DEVICE");
preview = new TransparentPanel(new GridBagLayout());
preview.add(new JLabel(noAvailableAudioDevice));
diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/ConfigurationPanel.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/ConfigurationPanel.java
index 1381d21..a3ea318 100644
--- a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/ConfigurationPanel.java
+++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/ConfigurationPanel.java
@@ -150,8 +150,7 @@ public class ConfigurationPanel
JCheckBox defaultIntraRefreshCheckBox
= new SIPCommCheckBox(
r.getI18NString(
- "impl.neomedia.configform.H264"
- + ".defaultIntraRefresh"));
+ "impl.neomedia.configform.H264.defaultIntraRefresh"));
cnstrnts.gridwidth = GridBagConstraints.REMAINDER;
cnstrnts.gridx = 0;
cnstrnts.gridy = 3;
diff --git a/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf b/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf
index f02d7e4..935ff09 100644
--- a/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf
+++ b/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf
@@ -12,21 +12,16 @@ Import-Package: org.jitsi.service.configuration,
net.java.sip.communicator.service.sysactivity,
net.java.sip.communicator.service.sysactivity.event,
org.osgi.framework,
+ org.ice4j,
+ org.ice4j.ice,
+ org.ice4j.ice.harvest,
+ org.ice4j.security,
org.ice4j.stack,
org.xml.sax,
org.xml.sax.helpers,
javax.crypto,
javax.crypto.spec,
- javax.sdp,
- gov.nist.javax.sdp.fields,
com.sun.jna,
com.sun.jna.ptr
Export-Package: net.java.sip.communicator.service.netaddr,
- net.java.sip.communicator.service.netaddr.event,
- org.ice4j,
- org.ice4j.socket,
- org.ice4j.stack,
- org.ice4j.ice,
- org.ice4j.ice.harvest,
- org.ice4j.ice.sdp,
- org.ice4j.security
+ net.java.sip.communicator.service.netaddr.event
diff --git a/src/net/java/sip/communicator/impl/osdependent/Desktop.java b/src/net/java/sip/communicator/impl/osdependent/Desktop.java
index 885dd02..53728e8 100644
--- a/src/net/java/sip/communicator/impl/osdependent/Desktop.java
+++ b/src/net/java/sip/communicator/impl/osdependent/Desktop.java
@@ -22,6 +22,7 @@ import java.io.*;
import java.lang.reflect.*;
import java.net.*;
+import net.java.sip.communicator.impl.osdependent.systemtray.SystemTray;
import net.java.sip.communicator.util.Logger;
/**
diff --git a/src/net/java/sip/communicator/impl/osdependent/PopupMessageHandlerTrayIconImpl.java b/src/net/java/sip/communicator/impl/osdependent/PopupMessageHandlerTrayIconImpl.java
index 7416b75..592e5af 100644
--- a/src/net/java/sip/communicator/impl/osdependent/PopupMessageHandlerTrayIconImpl.java
+++ b/src/net/java/sip/communicator/impl/osdependent/PopupMessageHandlerTrayIconImpl.java
@@ -19,6 +19,7 @@ package net.java.sip.communicator.impl.osdependent;
import java.awt.event.*;
+import net.java.sip.communicator.impl.osdependent.systemtray.TrayIcon;
import net.java.sip.communicator.service.systray.*;
import net.java.sip.communicator.service.systray.event.*;
diff --git a/src/net/java/sip/communicator/impl/osdependent/SystemTray.java b/src/net/java/sip/communicator/impl/osdependent/SystemTray.java
deleted file mode 100644
index 0c0a4a2..0000000
--- a/src/net/java/sip/communicator/impl/osdependent/SystemTray.java
+++ /dev/null
@@ -1,215 +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.osdependent;
-
-import java.awt.*;
-import java.lang.reflect.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.osdependent.TrayIcon.AWTTrayIconPeer;
-import net.java.sip.communicator.impl.osdependent.TrayIcon.TrayIconPeer;
-import net.java.sip.communicator.util.*;
-
-/**
- * @author Lubomir Marinov
- */
-public class SystemTray
-{
- /**
- * The <tt>Logger</tt> used by the <tt>SystemTray</tt> class and its
- * instances for logging output.
- */
- private static final Logger logger = Logger.getLogger(SystemTray.class);
-
- private static SystemTray defaultSystemTray;
-
- public static SystemTray getDefaultSystemTray()
- throws UnsupportedOperationException,
- HeadlessException,
- SecurityException
- {
- if (defaultSystemTray != null)
- return defaultSystemTray;
-
- Class<?> awtSystemTrayClass = null;
- try
- {
- awtSystemTrayClass = Class.forName("java.awt.SystemTray");
- }
- catch (ClassNotFoundException ex)
- {
- // We'll try org.jdesktop.jdic.tray then.
- }
- SystemTrayPeer peer = null;
- if (awtSystemTrayClass != null)
- try
- {
- peer = new AWTSystemTrayPeer(awtSystemTrayClass);
- }
- catch (Exception ex)
- {
- if(!GraphicsEnvironment.isHeadless())
- logger.error("Failed to initialize java.awt.SystemTray",
- ex);
-
- // We'll try org.jdesktop.jdic.tray then.
- }
- if (peer == null)
- {
- logger.error(
- "Failed to initialize the desktop.tray implementation.");
- throw new UnsupportedOperationException(
- "Failed to initialize the desktop.tray implementation.");
- }
- return (defaultSystemTray = new SystemTray(peer));
- }
-
- private final SystemTrayPeer peer;
-
- private SystemTray(SystemTrayPeer peer)
- {
- this.peer = peer;
- }
-
- public void addTrayIcon(TrayIcon trayIcon)
- throws NullPointerException,
- IllegalArgumentException
- {
- if (peer != null)
- peer.addTrayIcon(trayIcon.getPeer());
- }
-
- SystemTrayPeer getPeer()
- {
- return peer;
- }
-
- public boolean isSwing()
- {
- if (peer != null)
- return getPeer().isSwing();
- return false;
- }
-
- static interface SystemTrayPeer
- {
- void addTrayIcon(TrayIconPeer trayIconPeer)
- throws NullPointerException,
- IllegalArgumentException;
-
- TrayIconPeer createTrayIcon(ImageIcon icon,
- String tooltip,
- Object popup)
- throws IllegalArgumentException,
- UnsupportedOperationException,
- HeadlessException,
- SecurityException;
-
- boolean isSwing();
- }
-
- private static class AWTSystemTrayPeer
- implements SystemTrayPeer
- {
- private final Method addTrayIcon;
-
- private final Object impl;
-
- private final Class<?> trayIconClass;
-
- public AWTSystemTrayPeer(Class<?> clazz)
- throws UnsupportedOperationException,
- HeadlessException,
- SecurityException
- {
- Method getDefaultSystemTray;
- try
- {
- getDefaultSystemTray =
- clazz.getMethod("getSystemTray", (Class<?>[]) null);
- trayIconClass = Class.forName("java.awt.TrayIcon");
- addTrayIcon = clazz.getMethod("add", new Class<?>[]
- { trayIconClass });
- }
- catch (ClassNotFoundException ex)
- {
- throw new UnsupportedOperationException(ex);
- }
- catch (NoSuchMethodException ex)
- {
- throw new UnsupportedOperationException(ex);
- }
-
- try
- {
- impl = getDefaultSystemTray.invoke(null, (Object[]) null);
- }
- catch (IllegalAccessException ex)
- {
- throw new UnsupportedOperationException(ex);
- }
- catch (InvocationTargetException ex)
- {
- throw new UnsupportedOperationException(ex);
- }
- }
-
- public void addTrayIcon(TrayIconPeer trayIconPeer)
- throws NullPointerException,
- IllegalArgumentException
- {
- try
- {
- addTrayIcon.invoke(impl, new Object[]
- { ((AWTTrayIconPeer) trayIconPeer).getImpl() });
- }
- catch (IllegalAccessException ex)
- {
- throw new UndeclaredThrowableException(ex);
- }
- catch (InvocationTargetException ex)
- {
- Throwable cause = ex.getCause();
- if (cause == null)
- throw new UndeclaredThrowableException(ex);
- if (cause instanceof NullPointerException)
- throw (NullPointerException) cause;
- if (cause instanceof IllegalArgumentException)
- throw (IllegalArgumentException) cause;
- throw new UndeclaredThrowableException(cause);
- }
- }
-
- public TrayIconPeer createTrayIcon(ImageIcon icon, String tooltip,
- Object popup)
- throws IllegalArgumentException,
- UnsupportedOperationException,
- HeadlessException,
- SecurityException
- {
- return new AWTTrayIconPeer(trayIconClass, (icon == null) ? null
- : icon.getImage(), tooltip, popup);
- }
-
- public boolean isSwing()
- {
- return false;
- }
- }
-}
diff --git a/src/net/java/sip/communicator/impl/osdependent/TrayIcon.java b/src/net/java/sip/communicator/impl/osdependent/TrayIcon.java
deleted file mode 100644
index 974ed96..0000000
--- a/src/net/java/sip/communicator/impl/osdependent/TrayIcon.java
+++ /dev/null
@@ -1,412 +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.osdependent;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.lang.reflect.*;
-
-import javax.swing.*;
-import javax.swing.event.*;
-
-import net.java.sip.communicator.impl.osdependent.SystemTray.SystemTrayPeer;
-
-import org.jitsi.util.*;
-
-/**
- * @author Lubomir Marinov
- */
-public class TrayIcon
-{
- private final TrayIconPeer peer;
-
- public TrayIcon(ImageIcon icon, String tooltip, Object popup)
- throws IllegalArgumentException,
- UnsupportedOperationException,
- HeadlessException,
- SecurityException
- {
- SystemTrayPeer systemTrayPeer =
- SystemTray.getDefaultSystemTray().getPeer();
- if (systemTrayPeer != null)
- peer = systemTrayPeer.createTrayIcon(icon, tooltip, popup);
- else
- peer = null;
- }
-
- public void addActionListener(ActionListener listener)
- {
- if (peer != null)
- peer.addActionListener(listener);
- }
-
- public void addBalloonActionListener(ActionListener listener)
- {
- if (peer != null)
- peer.addBalloonActionListener(listener);
- }
-
- public void displayMessage(String caption, String text,
- java.awt.TrayIcon.MessageType messageType)
- throws NullPointerException
- {
- if (peer != null)
- peer.displayMessage(caption, text, messageType);
- }
-
- TrayIconPeer getPeer()
- {
- return peer;
- }
-
- public void setIcon(ImageIcon icon) throws NullPointerException
- {
- if (peer != null)
- peer.setIcon(icon);
- }
-
- public void setIconAutoSize(boolean autoSize)
- {
- if (peer != null)
- peer.setIconAutoSize(autoSize);
- }
-
- static interface TrayIconPeer
- {
- void addActionListener(ActionListener listener);
-
- void addBalloonActionListener(ActionListener listener);
-
- void displayMessage(String caption, String text,
- java.awt.TrayIcon.MessageType messageType)
- throws NullPointerException;
-
- void setIcon(ImageIcon icon) throws NullPointerException;
-
- void setIconAutoSize(boolean autoSize);
- }
-
- static class AWTTrayIconPeer
- implements TrayIconPeer
- {
- private final Method addActionListener;
-
- private final Method addMouseListener;
-
- private final Method displayMessage;
-
- private final Object impl;
-
- private final Class<?> messageTypeClass;
-
- private final Method setIcon;
-
- private final Method setIconAutoSize;
-
- public AWTTrayIconPeer(Class<?> clazz, Image image, String tooltip,
- Object popup)
- throws IllegalArgumentException,
- UnsupportedOperationException,
- HeadlessException,
- SecurityException
- {
- Constructor<?> constructor;
- try
- {
- if (popup instanceof JPopupMenu)
- {
- constructor = clazz.getConstructor(new Class<?>[]
- { Image.class, String.class });
- }
- else
- {
- constructor = clazz.getConstructor(new Class<?>[]
- { Image.class, String.class, PopupMenu.class });
- }
- addActionListener =
- clazz.getMethod("addActionListener", new Class<?>[]
- { ActionListener.class });
- addMouseListener =
- clazz.getMethod("addMouseListener", new Class<?>[]
- { MouseListener.class });
- messageTypeClass =
- Class.forName("java.awt.TrayIcon$MessageType");
- displayMessage =
- clazz.getMethod("displayMessage", new Class<?>[]
- { String.class, String.class, messageTypeClass });
- setIcon = clazz.getMethod("setImage", new Class<?>[]
- { Image.class });
- setIconAutoSize =
- clazz.getMethod("setImageAutoSize", new Class<?>[]
- { boolean.class });
- }
- catch (ClassNotFoundException ex)
- {
- throw new UnsupportedOperationException(ex);
- }
- catch (NoSuchMethodException ex)
- {
- throw new UnsupportedOperationException(ex);
- }
-
- try
- {
- if (popup instanceof JPopupMenu)
- {
- impl = constructor.newInstance(
- new Object[] { image, tooltip });
- addMouseListener(new AWTMouseAdapter((JPopupMenu) popup));
- }
- else
- {
- impl = constructor.newInstance(
- new Object[] { image, tooltip, popup });
- }
- }
- catch (IllegalAccessException ex)
- {
- throw new UnsupportedOperationException(ex);
- }
- catch (InstantiationException ex)
- {
- throw new UnsupportedOperationException(ex);
- }
- catch (InvocationTargetException ex)
- {
- Throwable cause = ex.getCause();
- if (cause == null)
- throw new UnsupportedOperationException(ex);
- if (cause instanceof IllegalArgumentException)
- throw (IllegalArgumentException) cause;
- if (cause instanceof UnsupportedOperationException)
- throw (UnsupportedOperationException) cause;
- if (cause instanceof HeadlessException)
- throw (HeadlessException) cause;
- if (cause instanceof SecurityException)
- throw (SecurityException) cause;
- throw new UnsupportedOperationException(cause);
- }
- }
-
- public void addActionListener(ActionListener listener)
- {
- try
- {
- addActionListener.invoke(getImpl(), new Object[]
- { listener });
- }
- catch (IllegalAccessException ex)
- {
- throw new UndeclaredThrowableException(ex);
- }
- catch (InvocationTargetException ex)
- {
- Throwable cause = ex.getCause();
- throw new UndeclaredThrowableException((cause == null) ? ex
- : cause);
- }
- }
-
- public void addMouseListener(MouseListener listener)
- {
- try
- {
- addMouseListener.invoke(getImpl(), new Object[] { listener });
- }
- catch (IllegalAccessException ex)
- {
- throw new UndeclaredThrowableException(ex);
- }
- catch (InvocationTargetException ex)
- {
- Throwable cause = ex.getCause();
- throw new UndeclaredThrowableException((cause == null) ? ex
- : cause);
- }
- }
-
- public void addBalloonActionListener(ActionListener listener)
- {
- // java.awt.TrayIcon doesn't support addBalloonActionListener()
- }
-
- public void displayMessage(String caption, String text,
- java.awt.TrayIcon.MessageType messageType)
- throws NullPointerException
- {
- try
- {
- displayMessage.invoke(getImpl(), new Object[]
- { caption, text, messageType.name() });
- }
- catch (IllegalAccessException ex)
- {
- throw new UndeclaredThrowableException(ex);
- }
- catch (InvocationTargetException ex)
- {
- Throwable cause = ex.getCause();
- if (cause instanceof NullPointerException)
- throw (NullPointerException) cause;
- throw new UndeclaredThrowableException((cause == null) ? ex
- : cause);
- }
- }
-
- public Object getImpl()
- {
- return impl;
- }
-
- public void setIcon(ImageIcon icon) throws NullPointerException
- {
- try
- {
- setIcon.invoke(getImpl(), new Object[]
- { (icon == null) ? null : icon.getImage() });
- }
- catch (IllegalAccessException ex)
- {
- throw new UndeclaredThrowableException(ex);
- }
- catch (InvocationTargetException ex)
- {
- Throwable cause = ex.getCause();
- if (cause instanceof NullPointerException)
- throw (NullPointerException) cause;
- throw new UndeclaredThrowableException((cause == null) ? ex
- : cause);
- }
- }
-
- public void setIconAutoSize(boolean autoSize)
- {
- try
- {
- setIconAutoSize.invoke(getImpl(), new Object[]
- { autoSize });
- }
- catch (IllegalAccessException ex)
- {
- throw new UndeclaredThrowableException(ex);
- }
- catch (InvocationTargetException ex)
- {
- Throwable cause = ex.getCause();
- throw new UndeclaredThrowableException((cause == null) ? ex
- : cause);
- }
- }
- }
-
- /**
- * Extended mouse adapter to show the JPopupMenu in Java 6
- * Based on : http://weblogs.java.net/blog/ixmal/archive/2006/05/using_jpopupmen.html
- * And : http://weblogs.java.net/blog/alexfromsun/archive/2008/02/jtrayicon_updat.html
- *
- * Use a hidden JWindow (JDialog for Windows) to manage the JPopupMenu.
- *
- * @author Damien Roth
- */
- private static class AWTMouseAdapter
- extends MouseAdapter
- {
- private JPopupMenu popup = null;
- private Window hiddenWindow = null;
-
- public AWTMouseAdapter(JPopupMenu p)
- {
- this.popup = p;
- this.popup.addPopupMenuListener(new PopupMenuListener()
- {
- public void popupMenuWillBecomeVisible(PopupMenuEvent e)
- {}
-
- public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
- {
- if (hiddenWindow != null)
- {
- hiddenWindow.dispose();
- hiddenWindow = null;
- }
- }
-
- public void popupMenuCanceled(PopupMenuEvent e)
- {
- if (hiddenWindow != null)
- {
- hiddenWindow.dispose();
- hiddenWindow = null;
- }
- }
- });
- }
-
- @Override
- public void mouseReleased(MouseEvent e)
- {
- showPopupMenu(e);
- }
-
- @Override
- public void mousePressed(MouseEvent e)
- {
- showPopupMenu(e);
- }
-
- private void showPopupMenu(MouseEvent e)
- {
- if (e.isPopupTrigger() && popup != null)
- {
- if (hiddenWindow == null)
- {
- if (OSUtils.IS_WINDOWS)
- {
- hiddenWindow = new JDialog((Frame) null);
- ((JDialog) hiddenWindow).setUndecorated(true);
- }
- else
- hiddenWindow = new JWindow((Frame) null);
-
- hiddenWindow.setAlwaysOnTop(true);
- Dimension size = popup.getPreferredSize();
-
- Point centerPoint = GraphicsEnvironment
- .getLocalGraphicsEnvironment()
- .getCenterPoint();
-
- if(e.getY() > centerPoint.getY())
- hiddenWindow
- .setLocation(e.getX(), e.getY() - size.height);
- else
- hiddenWindow
- .setLocation(e.getX(), e.getY());
-
- hiddenWindow.setVisible(true);
-
- popup.show(
- ((RootPaneContainer)hiddenWindow).getContentPane(),
- 0, 0);
-
- // popup works only for focused windows
- hiddenWindow.toFront();
- }
- }
- }
- }
-}
diff --git a/src/net/java/sip/communicator/impl/osdependent/jdic/StatusSubMenu.java b/src/net/java/sip/communicator/impl/osdependent/jdic/StatusSubMenu.java
index 2d77621..92088fa 100644
--- a/src/net/java/sip/communicator/impl/osdependent/jdic/StatusSubMenu.java
+++ b/src/net/java/sip/communicator/impl/osdependent/jdic/StatusSubMenu.java
@@ -66,7 +66,7 @@ public class StatusSubMenu
* @param swing <tt>true</tt> to represent this instance with a Swing
* <tt>JMenu</tt>; <tt>false</tt> to use an AWT <tt>Menu</tt>
*/
- public StatusSubMenu(boolean swing)
+ public StatusSubMenu(boolean swing, boolean accountMenuSupported)
{
String text = Resources.getString("impl.systray.SET_STATUS");
@@ -86,6 +86,7 @@ public class StatusSubMenu
this.menu = new Menu(text);
}
+ if (accountMenuSupported)
{
String hideAccountStatusSelectorsProperty
= "impl.gui.HIDE_ACCOUNT_STATUS_SELECTORS";
@@ -103,6 +104,10 @@ public class StatusSubMenu
hideAccountStatusSelectorsProperty,
hideAccountStatusSelectors);
}
+ else
+ {
+ hideAccountStatusSelectors = true;
+ }
PresenceStatus offlineStatus = null;
// creates menu item entry for every global status
@@ -116,9 +121,11 @@ public class StatusSubMenu
// initially it is offline
selectItemFromStatus(offlineStatus.getStatus());
- this.addSeparator();
-
- addMenuItem(menu, new GlobalStatusMessageMenu(swing).getMenu());
+ if (accountMenuSupported)
+ {
+ this.addSeparator();
+ addMenuItem(menu, new GlobalStatusMessageMenu(swing).getMenu());
+ }
if(!hideAccountStatusSelectors)
this.addSeparator();
diff --git a/src/net/java/sip/communicator/impl/osdependent/jdic/SystrayServiceJdicImpl.java b/src/net/java/sip/communicator/impl/osdependent/jdic/SystrayServiceJdicImpl.java
index f774b23..0b40798 100644
--- a/src/net/java/sip/communicator/impl/osdependent/jdic/SystrayServiceJdicImpl.java
+++ b/src/net/java/sip/communicator/impl/osdependent/jdic/SystrayServiceJdicImpl.java
@@ -19,20 +19,25 @@ package net.java.sip.communicator.impl.osdependent.jdic;
import java.awt.*;
import java.awt.event.*;
+import java.awt.image.*;
import java.net.*;
+import java.util.HashMap;
+import java.util.Map;
import javax.swing.*;
import javax.swing.event.*;
import net.java.sip.communicator.impl.osdependent.*;
-import net.java.sip.communicator.impl.osdependent.SystemTray;
-import net.java.sip.communicator.impl.osdependent.TrayIcon;
+import net.java.sip.communicator.impl.osdependent.systemtray.SystemTray;
+import net.java.sip.communicator.impl.osdependent.systemtray.TrayIcon;
+import net.java.sip.communicator.impl.osdependent.windows.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.systray.*;
import net.java.sip.communicator.service.systray.event.*;
import net.java.sip.communicator.util.Logger;
+import org.apache.commons.lang3.tuple.Pair;
import org.jitsi.util.*;
import org.osgi.framework.*;
@@ -51,7 +56,6 @@ import com.apple.eawt.*;
public class SystrayServiceJdicImpl
extends AbstractSystrayService
{
-
/**
* The systray.
*/
@@ -93,10 +97,6 @@ public class SystrayServiceJdicImpl
private ImageIcon logoIconWhite;
- private ImageIcon envelopeIcon;
-
- private ImageIcon envelopeIconWhite;
-
/**
* The dock Icons used only in Mac version
*/
@@ -131,10 +131,9 @@ public class SystrayServiceJdicImpl
super(OsDependentActivator.bundleContext);
SystemTray systray;
-
try
{
- systray = SystemTray.getDefaultSystemTray();
+ systray = SystemTray.getSystemTray();
}
catch (Throwable t)
{
@@ -147,10 +146,39 @@ public class SystrayServiceJdicImpl
logger.error("Failed to create a systray!", t);
}
}
- this.systray = systray;
+ this.systray = systray;
if (this.systray != null)
+ {
initSystray();
+ }
+ }
+
+ @Override
+ public Map<String, String> getSystrayModes()
+ {
+ return new HashMap<String, String>()
+ {{
+ put("disabled", "service.systray.mode.DISABLED");
+ if (java.awt.SystemTray.isSupported())
+ {
+ put("native", "service.systray.mode.NATIVE");
+ }
+
+ if (!OSUtils.IS_MAC && !OSUtils.IS_WINDOWS)
+ {
+ put("appindicator",
+ "service.systray.mode.APPINDICATOR");
+ put("appindicator_static",
+ "service.systray.mode.APPINDICATOR_STATIC");
+ }
+ }};
+ }
+
+ @Override
+ public String getActiveSystrayMode()
+ {
+ return SystemTray.getSystemTrayMode();
}
/**
@@ -187,53 +215,33 @@ public class SystrayServiceJdicImpl
return;
}
- menu = TrayMenuFactory.createTrayMenu(this, systray.isSwing());
+ Pair<Object, Object> createdMenu = TrayMenuFactory.createTrayMenu(
+ this,
+ systray.useSwingPopupMenu(),
+ systray.supportsDynamicMenu());
+ menu = createdMenu.getLeft();
boolean isMac = OSUtils.IS_MAC;
- // If we're running under Windows, we use a special icon without
- // background.
- if (OSUtils.IS_WINDOWS)
- {
- logoIcon = Resources.getImage("service.systray.TRAY_ICON_WINDOWS");
- logoIconOffline = Resources.getImage(
- "service.systray.TRAY_ICON_WINDOWS_OFFLINE");
- logoIconAway = Resources.getImage(
- "service.systray.TRAY_ICON_WINDOWS_AWAY");
- logoIconExtendedAway = Resources.getImage(
- "service.systray.TRAY_ICON_WINDOWS_EXTENDED_AWAY");
- logoIconFFC = Resources.getImage(
- "service.systray.TRAY_ICON_WINDOWS_FFC");
- logoIconDND = Resources.getImage(
- "service.systray.TRAY_ICON_WINDOWS_DND");
- envelopeIcon = Resources.getImage(
- "service.systray.MESSAGE_ICON_WINDOWS");
- }
- /*
- * If we're running under Mac OS X, we use special black and white icons
- * without background.
- */
- else if (isMac)
+ logoIcon = Resources.getImage("service.systray.TRAY_ICON_WINDOWS");
+ logoIconOffline = Resources.getImage(
+ "service.systray.TRAY_ICON_WINDOWS_OFFLINE");
+ logoIconAway = Resources.getImage(
+ "service.systray.TRAY_ICON_WINDOWS_AWAY");
+ logoIconExtendedAway = Resources.getImage(
+ "service.systray.TRAY_ICON_WINDOWS_EXTENDED_AWAY");
+ logoIconFFC = Resources.getImage(
+ "service.systray.TRAY_ICON_WINDOWS_FFC");
+ logoIconDND = Resources.getImage(
+ "service.systray.TRAY_ICON_WINDOWS_DND");
+
+ // If we're running under Mac OS X, we use special black and white
+ // icons without background.
+ if (isMac)
{
logoIcon = Resources.getImage("service.systray.TRAY_ICON_MACOSX");
logoIconWhite = Resources.getImage(
"service.systray.TRAY_ICON_MACOSX_WHITE");
- envelopeIcon = Resources.getImage(
- "service.systray.MESSAGE_ICON_MACOSX");
- envelopeIconWhite = Resources.getImage(
- "service.systray.MESSAGE_ICON_MACOSX_WHITE");
- }
- else
- {
- logoIcon = Resources.getImage("service.systray.TRAY_ICON");
- logoIconOffline = Resources.getImage(
- "service.systray.TRAY_ICON_OFFLINE");
- logoIconAway = Resources.getImage("service.systray.TRAY_ICON_AWAY");
- logoIconExtendedAway = Resources.getImage(
- "service.systray.TRAY_ICON_EXTENDED_AWAY");
- logoIconFFC = Resources.getImage("service.systray.TRAY_ICON_FFC");
- logoIconDND = Resources.getImage("service.systray.TRAY_ICON_DND");
- envelopeIcon = Resources.getImage("service.systray.MESSAGE_ICON");
}
/*
@@ -243,7 +251,7 @@ public class SystrayServiceJdicImpl
currentIcon = isMac ? logoIcon : logoIconOffline;
trayIcon
- = new TrayIcon(
+ = systray.createTrayIcon(
currentIcon,
Resources.getApplicationString(
"service.gui.APPLICATION_NAME"),
@@ -268,21 +276,15 @@ public class SystrayServiceJdicImpl
}
//Show/hide the contact list when user clicks on the systray.
- trayIcon.addActionListener(
- new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- UIService uiService
- = OsDependentActivator.getUIService();
- ExportedWindow mainWindow
- = uiService.getExportedWindow(
- ExportedWindow.MAIN_WINDOW);
- boolean setIsVisible = !mainWindow.isVisible();
-
- uiService.setVisible(setIsVisible);
- }
- });
+ final Object defaultActionItem;
+ if (systray.useSwingPopupMenu())
+ {
+ defaultActionItem = ((JMenuItem) createdMenu.getRight());
+ }
+ else
+ {
+ defaultActionItem = ((MenuItem) createdMenu.getRight());
+ }
/*
* Change the Mac OS X icon with the white one when the pop-up menu
@@ -296,11 +298,7 @@ public class SystrayServiceJdicImpl
{
public void popupMenuWillBecomeVisible(PopupMenuEvent e)
{
- ImageIcon newIcon
- = (currentIcon == envelopeIcon)
- ? envelopeIconWhite
- : logoIconWhite;
-
+ ImageIcon newIcon = logoIconWhite;
trayIcon.setIcon(newIcon);
currentIcon = newIcon;
}
@@ -308,11 +306,7 @@ public class SystrayServiceJdicImpl
public void popupMenuWillBecomeInvisible(
PopupMenuEvent e)
{
- ImageIcon newIcon
- = (currentIcon == envelopeIconWhite)
- ? envelopeIcon
- : logoIcon;
-
+ ImageIcon newIcon = logoIcon;
getTrayIcon().setIcon(newIcon);
currentIcon = newIcon;
}
@@ -353,12 +347,12 @@ public class SystrayServiceJdicImpl
public void run()
{
systray.addTrayIcon(trayIcon);
+ trayIcon.setDefaultAction(defaultActionItem);
}
});
initialized = true;
-
- uiService.setExitOnMainWindowClose(false);
+ uiService.setMainWindowCanHide(true);
}
/**
@@ -402,12 +396,6 @@ public class SystrayServiceJdicImpl
if (!isMac)
systrayIconToSet = logoIconDND;
break;
- case SystrayService.ENVELOPE_IMG_TYPE:
- systrayIconToSet
- = (isMac && TrayMenuFactory.isVisible(menu))
- ? envelopeIconWhite
- : envelopeIcon;
- break;
}
if (systrayIconToSet != null)
@@ -469,14 +457,90 @@ public class SystrayServiceJdicImpl
}
}
- private boolean checkInitialized()
+ @Override
+ public boolean checkInitialized()
{
- if (!initialized)
- logger.error("Systray not init");
return initialized;
}
/**
+ * Set the number of pending notifications to the the application icon
+ * (Dock on OSX, TaskBar on Windows, nothing on Linux currently).
+ */
+ @Override
+ public void setNotificationCount(int count)
+ {
+ if (OSUtils.IS_MAC)
+ {
+ Application application = Application.getApplication();
+ application.setDockIconBadge(new Integer(count).toString());
+ }
+ else if (OSUtils.IS_WINDOWS)
+ {
+ UIService uiService = OsDependentActivator.getUIService();
+ if (uiService == null)
+ {
+ return;
+ }
+
+ ExportedWindow mainWindow =
+ uiService.getExportedWindow(ExportedWindow.MAIN_WINDOW);
+ if (mainWindow == null
+ || !(mainWindow.getSource() instanceof Component))
+ {
+ return;
+ }
+
+ BufferedImage img = null;
+ if (count > 0)
+ {
+ img = createOverlayImage(new Integer(count).toString());
+ }
+
+ try
+ {
+ TaskBarList3.getInstance().SetOverlayIcon(
+ (Component) mainWindow.getSource(), img, null);
+ }
+ catch (Exception ex)
+ {
+ logger.error("Could not set the notification count.", ex);
+ }
+ }
+ }
+
+ private BufferedImage createOverlayImage(String text)
+ {
+ int size = 16;
+ BufferedImage image =
+ new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = image.createGraphics();
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+
+ //background
+ g.setPaint(new Color(0, 0, 0, 102));
+ g.fillRoundRect(0, 0, size, size, size, size);
+
+ //filling
+ int mainRadius = 14;
+ g.setPaint(new Color(255, 98, 89));
+ g.fillRoundRect(size / 2 - mainRadius / 2, size / 2 - mainRadius / 2,
+ mainRadius, mainRadius, size, size);
+
+ //text
+ Font font = g.getFont();
+ g.setFont(new Font(font.getName(), Font.BOLD, 9));
+ FontMetrics fontMetrics = g.getFontMetrics();
+ int textWidth = fontMetrics.stringWidth(text);
+ g.setColor(Color.white);
+ g.drawString(text, size / 2 - textWidth / 2,
+ size / 2 - fontMetrics.getHeight() / 2 + fontMetrics.getAscent());
+
+ return image;
+ }
+
+ /**
* @return the trayIcon
*/
public TrayIcon getTrayIcon()
@@ -551,7 +615,7 @@ public class SystrayServiceJdicImpl
OsDependentActivator.bundleContext.removeServiceListener(
this);
- if (!initialized)
+ if (!initialized && systray != null)
initSystray();
}
}
diff --git a/src/net/java/sip/communicator/impl/osdependent/jdic/TrayMenuFactory.java b/src/net/java/sip/communicator/impl/osdependent/jdic/TrayMenuFactory.java
index 2b5df19..cae3ada 100644
--- a/src/net/java/sip/communicator/impl/osdependent/jdic/TrayMenuFactory.java
+++ b/src/net/java/sip/communicator/impl/osdependent/jdic/TrayMenuFactory.java
@@ -26,6 +26,7 @@ import javax.swing.event.*;
import net.java.sip.communicator.impl.osdependent.*;
import net.java.sip.communicator.service.gui.*;
+import org.apache.commons.lang3.tuple.*;
import org.jitsi.util.*;
/**
@@ -139,16 +140,16 @@ public final class TrayMenuFactory
*
* @param tray the system tray for which we're creating a menu
* @param swing indicates if we should create a Swing or an AWT menu
- * @return a tray menu for the given system tray
+ * @return a tray menu for the given system tray (first) and the default
+ * menu item (second)
*/
- public static Object createTrayMenu(SystrayServiceJdicImpl tray,
- boolean swing)
+ public static Pair<Object, Object> createTrayMenu(
+ SystrayServiceJdicImpl tray,
+ boolean swing,
+ boolean accountMenuSupported
+ )
{
- // Enable swing for java 1.6 except for the mac version
- if (!swing && !OSUtils.IS_MAC)
- swing = true;
-
- Object trayMenu = swing ? new JPopupMenu() : new PopupMenu();
+ final Object trayMenu = swing ? new JPopupMenu() : new PopupMenu();
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
@@ -184,9 +185,11 @@ public final class TrayMenuFactory
+ "CHAT_PRESENCE_DISABLED",
false);
- if (!chatPresenceDisabled.booleanValue())
+ if (!chatPresenceDisabled.booleanValue() && accountMenuSupported)
{
- add(trayMenu, new StatusSubMenu(swing).getMenu());
+ add(
+ trayMenu,
+ new StatusSubMenu(swing, accountMenuSupported).getMenu());
addSeparator(trayMenu);
}
@@ -201,9 +204,11 @@ public final class TrayMenuFactory
showHideIconId = "service.gui.icons.SEARCH_ICON_16x16";
}
else
+ {
showHideName = "service.gui.SHOW";
showHideTextId = "service.gui.SHOW";
showHideIconId = "service.gui.icons.SEARCH_ICON_16x16";
+ }
final Object showHideMenuItem = createTrayMenuItem( showHideName,
showHideTextId,
@@ -245,7 +250,7 @@ public final class TrayMenuFactory
}
});
- return trayMenu;
+ return Pair.of(trayMenu, showHideMenuItem);
}
/**
diff --git a/src/net/java/sip/communicator/impl/osdependent/osdependent.manifest.mf b/src/net/java/sip/communicator/impl/osdependent/osdependent.manifest.mf
index 0db99ad..91f2791 100644
--- a/src/net/java/sip/communicator/impl/osdependent/osdependent.manifest.mf
+++ b/src/net/java/sip/communicator/impl/osdependent/osdependent.manifest.mf
@@ -5,26 +5,29 @@ Bundle-Vendor: jitsi.org
Bundle-Version: 0.0.1
Bundle-SymbolicName: net.java.sip.communicator.osdependent
Export-Package: net.java.sip.communicator.service.desktop
-Import-Package: org.osgi.framework,
- com.apple.cocoa.application,
+Import-Package: com.apple.cocoa.application,
com.apple.cocoa.foundation,
com.apple.eawt,
- org.jitsi.service.configuration,
+ com.sun.jna,
+ com.sun.jna.platform.win32,
+ com.sun.jna.platform.win32.COM,
+ com.sun.jna.ptr,
+ com.sun.jna.win32,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.gui.event,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.globalstatus,
net.java.sip.communicator.service.protocol.event,
- org.jitsi.service.resources, net.java.sip.communicator.service.resources,
+ net.java.sip.communicator.service.resources,
net.java.sip.communicator.service.shutdown,
net.java.sip.communicator.service.systray,
net.java.sip.communicator.service.systray.event,
- org.jitsi.util,
net.java.sip.communicator.util,
net.java.sip.communicator.plugin.desktoputil,
net.java.sip.communicator.plugin.desktoputil.presence,
javax.accessibility,
javax.imageio,
+ javax.imageio.stream,
javax.swing,
javax.swing.border,
javax.swing.event,
@@ -36,4 +39,9 @@ Import-Package: org.osgi.framework,
javax.swing.text,
javax.swing.text.html,
javax.swing.tree,
- javax.swing.undo
+ javax.swing.undo,
+ org.apache.commons.lang3.tuple,
+ org.jitsi.service.configuration,
+ org.jitsi.service.resources,
+ org.jitsi.util,
+ org.osgi.framework
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/SystemTray.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/SystemTray.java
new file mode 100644
index 0000000..818d1e7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/SystemTray.java
@@ -0,0 +1,156 @@
+/*
+ * 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.osdependent.systemtray;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+import org.jitsi.util.*;
+
+import net.java.sip.communicator.impl.osdependent.*;
+import net.java.sip.communicator.impl.osdependent.systemtray.appindicator.*;
+import net.java.sip.communicator.impl.osdependent.systemtray.awt.*;
+import net.java.sip.communicator.service.systray.*;
+import net.java.sip.communicator.util.Logger;
+
+/**
+ * Base class for all wrappers of <tt>SystemTray</tt> implementations.
+ */
+public abstract class SystemTray
+{
+ private static final Logger logger = Logger.getLogger(SystemTray.class);
+ private static SystemTray systemTray;
+ private static final String DISABLED_TRAY_MODE = "disabled";
+
+ /**
+ * Gets or creates the supported <tt>SystemTray</tt> implementations.
+ * @return a <tt>SystemTray</tt> implementation for the current platform.
+ */
+ public final static SystemTray getSystemTray()
+ {
+ if (systemTray == null)
+ {
+ String mode = getSystemTrayMode();
+ logger.info("Tray for " + mode + " requested");
+ switch (mode)
+ {
+ case DISABLED_TRAY_MODE:
+ return null;
+ case "native":
+ if (java.awt.SystemTray.isSupported())
+ {
+ systemTray = new AWTSystemTray();
+ }
+
+ break;
+ case "appindicator":
+ try
+ {
+ systemTray = new AppIndicatorTray(true);
+ }
+ catch(Exception ex)
+ {
+ logger.error("AppIndicator tray not available", ex);
+ }
+ break;
+ case "appindicator_static":
+ try
+ {
+ systemTray = new AppIndicatorTray(false);
+ }
+ catch(Exception ex)
+ {
+ logger.error("AppIndicator tray not available", ex);
+ }
+
+ break;
+ }
+
+ if (systemTray == null)
+ {
+ OsDependentActivator.getConfigurationService()
+ .setProperty(SystrayService.PNMAE_TRAY_MODE, "disabled");
+ }
+ }
+
+ return systemTray;
+ }
+
+ public static String getSystemTrayMode()
+ {
+ String defaultTrayMode = DISABLED_TRAY_MODE;
+ if (GraphicsEnvironment.isHeadless())
+ {
+ return DISABLED_TRAY_MODE;
+ }
+
+ // setting from cmd-line: request to disable tray in case it failed
+ if (Boolean.getBoolean("disable-tray"))
+ {
+ OsDependentActivator.getConfigurationService().setProperty(
+ SystrayService.PNMAE_TRAY_MODE, DISABLED_TRAY_MODE);
+ }
+
+ if (OSUtils.IS_WINDOWS || OSUtils.IS_MAC)
+ {
+ defaultTrayMode = "native";
+ }
+
+ return OsDependentActivator.getConfigurationService()
+ .getString(SystrayService.PNMAE_TRAY_MODE, defaultTrayMode);
+ }
+
+ /**
+ * Adds a <tt>TrayIcon</tt> to this system tray implementation.
+ *
+ * @param trayIcon the <tt>TrayIcon</tt> to add
+ */
+ public abstract void addTrayIcon(TrayIcon trayIcon);
+
+ /**
+ * Creates an implementation specific <tt>TrayIcon</tt> that can later be
+ * added with {@link #addTrayIcon(TrayIcon)}.
+ *
+ * @param image the <tt>Image</tt> to be used
+ * @param tooltip the string to be used as tooltip text; if the value is
+ * <tt>null</tt> no tooltip is shown
+ * @param popup the menu to be used for the tray icon's popup menu; if the
+ * value is <tt>null</tt> no popup menu is shown
+ * @return a <tt>TrayIcon</tt> instance for this <tt>SystemTray</tt>
+ * implementation.
+ */
+ public abstract TrayIcon createTrayIcon(ImageIcon icon, String tooltip,
+ Object popup);
+
+ /**
+ * Determines if the popup menu for the icon is to be a Swing
+ * <tt>JPopupMenu</tt> or an AWT <tt>PopupMenu</tt>
+ *
+ * @return <tt>true</tt> for a <tt>JPopupMenu</tt>, <tt>false</tt> for a
+ * <tt>PopupMenu</tt>
+ */
+ public abstract boolean useSwingPopupMenu();
+
+ /**
+ * Determines if the tray icon supports dynamic menus.
+ *
+ * @return True if the menu can be changed while running, false otherwise.
+ */
+ public abstract boolean supportsDynamicMenu();
+}
diff --git a/src/net/java/sip/communicator/impl/dns/UnboundException.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/TrayIcon.java
index 3eb2a04..78df44c 100644
--- a/src/net/java/sip/communicator/impl/dns/UnboundException.java
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/TrayIcon.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,28 +15,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.dns;
-
-/**
- * Exception that is being thrown when native Unbound code resulted in an error.
- *
- * @author Ingo Bauersachs
- */
-public class UnboundException
- extends Exception
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * Creates a new instance of this class.
- *
- * @param message the detail message.
- */
- public UnboundException(String message)
- {
- super(message);
- }
-}
+package net.java.sip.communicator.impl.osdependent.systemtray;
+
+import java.awt.event.*;
+
+import javax.swing.*;
+
+/**
+ * Interface for all platform specific TrayIcon implementations. See
+ * {@link java.awt.TrayIcon} for a description of the methods.
+ *
+ * @author Lubomir Marinov
+ */
+public interface TrayIcon
+{
+ public void setDefaultAction(Object menuItem);
+
+ public void addBalloonActionListener(ActionListener listener);
+
+ public void displayMessage(String caption, String text,
+ java.awt.TrayIcon.MessageType messageType);
+
+ public void setIcon(ImageIcon icon) throws NullPointerException;
+
+ public void setIconAutoSize(boolean autoSize);
+}
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicator1.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicator1.java
new file mode 100644
index 0000000..85fbd73
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicator1.java
@@ -0,0 +1,189 @@
+/*
+ * 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.osdependent.systemtray.appindicator;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.sun.jna.*;
+
+/**
+ * JNA mappings for libappindicator1.
+ *
+ * @author Ingo Bauersachs
+ */
+interface AppIndicator1 extends Library
+{
+ static final AppIndicator1 INSTANCE =
+ (AppIndicator1) Native.loadLibrary("appindicator", AppIndicator1.class);
+
+ static final String APP_INDICATOR_SIGNAL_NEW_ICON = "new-icon";
+ static final String APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON = "new-attention-icon";
+ static final String APP_INDICATOR_SIGNAL_NEW_STATUS = "new-status";
+ static final String APP_INDICATOR_SIGNAL_NEW_LABEL = "new-label";
+ static final String APP_INDICATOR_SIGNAL_CONNECTION_CHANGED = "connection-changed";
+ static final String APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH = "new-icon-theme-path";
+ static final String APP_INDICATOR_SIGNAL_SCROLL_EVENT = "scroll-event";
+
+ /**
+ * The category provides grouping for the indicators so that users can find
+ * indicators that are similar together.
+ */
+ enum APP_INDICATOR_CATEGORY
+ {
+ /** The indicator is used to display the status of the application. */
+ APPLICATION_STATUS,
+
+ /** The application is used for communication with other people. */
+ COMMUNICATIONS,
+
+ /** A system indicator relating to something in the user's system. */
+ SYSTEM_SERVICES,
+
+ /** An indicator relating to the user's hardware. */
+ HARDWARE,
+
+ /**
+ * Something not defined in this enum, please don't use unless you
+ * really need it.
+ */
+ OTHER
+ }
+
+ /**
+ * These are the states that the indicator can be on in the user's panel.
+ * The indicator by default starts in the state {@link #PASSIVE} and can be
+ * shown by setting it to {@link #ACTIVE}.
+ */
+ enum APP_INDICATOR_STATUS
+ {
+ /** The indicator should not be shown to the user. */
+ PASSIVE,
+
+ /** The indicator should be shown in it's default state. */
+ ACTIVE,
+
+ /** The indicator should show it's attention icon. */
+ ATTENTION
+ }
+
+ class AppIndicatorClass extends Structure
+ {
+ // Parent
+ public /*Gobject.GObjectClass*/ Pointer parent_class;
+
+ // DBus Signals
+ public Pointer new_icon;
+ public Pointer new_attention_icon;
+ public Pointer new_status;
+ public Pointer new_icon_theme_path;
+ public Pointer new_label;
+
+ // Local Signals
+ public Pointer connection_changed;
+ public Pointer scroll_event;
+ public Pointer app_indicator_reserved_ats;
+
+ // Overridable Functions
+ public Pointer fallback;
+ public Pointer unfallback;
+
+ // Reserved
+ public Pointer app_indicator_reserved_1;
+ public Pointer app_indicator_reserved_2;
+ public Pointer app_indicator_reserved_3;
+ public Pointer app_indicator_reserved_4;
+ public Pointer app_indicator_reserved_5;
+ public Pointer app_indicator_reserved_6;
+
+ @Override
+ protected List getFieldOrder() {
+ return Arrays.asList(
+ "parent_class",
+ "new_icon",
+ "new_attention_icon",
+ "new_status",
+ "new_icon_theme_path",
+ "new_label",
+
+ "connection_changed",
+ "scroll_event",
+ "app_indicator_reserved_ats",
+
+ "fallback",
+ "unfallback",
+
+ "app_indicator_reserved_1",
+ "app_indicator_reserved_2",
+ "app_indicator_reserved_3",
+ "app_indicator_reserved_4",
+ "app_indicator_reserved_5",
+ "app_indicator_reserved_6");
+ }
+ }
+
+ class AppIndicator extends Structure
+ {
+ public /*Gobject.GObject*/ Pointer parent;
+ public Pointer priv;
+
+ @Override
+ protected List getFieldOrder()
+ {
+ return Arrays.asList("parent", "priv");
+ }
+ }
+
+ // GObject Stuff
+ NativeLong app_indicator_get_type();
+ AppIndicator app_indicator_new(String id, String icon_name, int category);
+ AppIndicator app_indicator_new_with_path(String id, String icon_name, int category, String icon_theme_path);
+
+ // Set properties
+ void app_indicator_set_status(AppIndicator self, int status);
+ void app_indicator_set_attention_icon(AppIndicator self, String icon_name);
+ void app_indicator_set_attention_icon_full(AppIndicator self, String name, String icon_desc);
+ void app_indicator_set_menu(AppIndicator self, Pointer menu);
+ void app_indicator_set_icon(AppIndicator self, String icon_name);
+ void app_indicator_set_icon_full(AppIndicator self, String icon_name, String icon_desc);
+ void app_indicator_set_label(AppIndicator self, String label, String guide);
+ void app_indicator_set_icon_theme_path(AppIndicator self, String icon_theme_path);
+ void app_indicator_set_ordering_index(AppIndicator self, int ordering_index);
+ void app_indicator_set_secondary_activate_target(AppIndicator self, Pointer menuitem);
+ void app_indicator_set_title(AppIndicator self, String title);
+
+ // Get properties
+ String app_indicator_get_id(AppIndicator self);
+ int app_indicator_get_category(AppIndicator self);
+ int app_indicator_get_status(AppIndicator self);
+ String app_indicator_get_icon(AppIndicator self);
+ String app_indicator_get_icon_desc(AppIndicator self);
+ String app_indicator_get_icon_theme_path(AppIndicator self);
+ String app_indicator_get_attention_icon(AppIndicator self);
+ String app_indicator_get_attention_icon_desc(AppIndicator self);
+ String app_indicator_get_title(AppIndicator self);
+
+ Pointer app_indicator_get_menu(AppIndicator self);
+ String app_indicator_get_label(AppIndicator self);
+ String app_indicator_get_label_guide(AppIndicator self);
+ int app_indicator_get_ordering_index(AppIndicator self);
+ Pointer app_indicator_get_secondary_activate_target(AppIndicator self, Pointer widget);
+
+ // Helpers
+ void app_indicator_build_menu_from_desktop(AppIndicator self, String desktop_file, String destkop_profile);
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicatorTray.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicatorTray.java
new file mode 100644
index 0000000..90c949a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicatorTray.java
@@ -0,0 +1,78 @@
+/*
+ * 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.osdependent.systemtray.appindicator;
+
+import javax.swing.*;
+
+import org.jitsi.util.*;
+
+import net.java.sip.communicator.impl.osdependent.*;
+import net.java.sip.communicator.impl.osdependent.systemtray.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Jitsi system tray abstraction for libappindicator.
+ *
+ * @author Ingo Bauersachs
+ */
+public class AppIndicatorTray extends SystemTray
+{
+ private boolean dynamicMenu;
+
+ public AppIndicatorTray(boolean dynamicMenu) throws Exception
+ {
+ this.dynamicMenu = dynamicMenu;
+ try
+ {
+ // pre-initialize the JNA libraries before attempting to use them
+ AppIndicator1.INSTANCE.toString();
+ Gtk.INSTANCE.toString();
+ Gobject.INSTANCE.toString();
+ Gtk.INSTANCE.gtk_init(0, null);
+ }
+ catch (Throwable t)
+ {
+ throw new Exception("AppIndicator1 tray icon not available", t);
+ }
+ }
+
+ @Override
+ public void addTrayIcon(TrayIcon trayIcon)
+ {
+ ((AppIndicatorTrayIcon) trayIcon).createTray();
+ }
+
+ @Override
+ public TrayIcon createTrayIcon(ImageIcon icon, String tooltip, Object popup)
+ {
+ return new AppIndicatorTrayIcon(icon, tooltip, (JPopupMenu) popup);
+ }
+
+ @Override
+ public boolean useSwingPopupMenu()
+ {
+ // we want icons
+ return true;
+ }
+
+ @Override
+ public boolean supportsDynamicMenu()
+ {
+ return dynamicMenu;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicatorTrayIcon.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicatorTrayIcon.java
new file mode 100644
index 0000000..12334f3
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/AppIndicatorTrayIcon.java
@@ -0,0 +1,695 @@
+/*
+ * 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.osdependent.systemtray.appindicator;
+
+import java.awt.*;
+import java.awt.TrayIcon.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.beans.*;
+import java.io.*;
+import java.net.*;
+import java.nio.file.*;
+import java.util.*;
+import java.util.List;
+import java.util.Timer;
+
+import javax.accessibility.*;
+import javax.imageio.*;
+import javax.imageio.stream.*;
+import javax.print.attribute.standard.*;
+import javax.swing.*;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.jitsi.util.*;
+import org.jitsi.util.Logger;
+
+import com.sun.jna.*;
+
+import net.java.sip.communicator.impl.osdependent.*;
+import net.java.sip.communicator.impl.osdependent.systemtray.*;
+import net.java.sip.communicator.impl.osdependent.systemtray.TrayIcon;
+import net.java.sip.communicator.impl.osdependent.systemtray.appindicator.Gobject.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * System tray icon implementation based on libappindicator1.
+ *
+ * @author Ingo Bauersachs
+ */
+class AppIndicatorTrayIcon implements TrayIcon
+{
+ private static final Logger logger =
+ Logger.getLogger(AppIndicatorTrayIcon.class);
+
+ // shortcuts
+ private static Gobject gobject = Gobject.INSTANCE;
+ private static Gtk gtk = Gtk.INSTANCE;
+ private static AppIndicator1 ai = AppIndicator1.INSTANCE;
+
+ // references to the root menu and the native icon
+ private ImageIcon mainIcon;
+ private String title;
+ private JPopupMenu popup;
+ private Map<String, String> extractedFiles = new HashMap<>();
+ private PopupMenuPeer popupPeer;
+ private AppIndicator1.AppIndicator appIndicator;
+
+ private PopupMenuPeer defaultMenuPeer;
+
+ public AppIndicatorTrayIcon(ImageIcon mainIcon, String title,
+ JPopupMenu popup)
+ {
+ this.mainIcon = mainIcon;
+ this.title = title;
+ this.popup = popup;
+ this.popupPeer = null;
+ }
+
+ /**
+ * Combines the references of each swing menu item with the GTK counterpart
+ */
+ private class PopupMenuPeer implements ContainerListener
+ {
+ public PopupMenuPeer(PopupMenuPeer parent, Component em)
+ {
+ menuItem = em;
+
+ // if this menu item is a submenu, add ourselves as listener to
+ // add or remove the native counterpart
+ if (em instanceof JMenu)
+ {
+ ((JMenu)em).getPopupMenu().addContainerListener(this);
+ ((JMenu)em).addContainerListener(this);
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable
+ {
+ super.finalize();
+ if (isDefaultMenuItem)
+ {
+ gobject.g_object_unref(gtkMenuItem);
+ }
+ }
+
+ public List<PopupMenuPeer> children = new ArrayList<>();
+ public Pointer gtkMenuItem;
+ public Pointer gtkMenu;
+ public Pointer gtkImage;
+ public Memory gtkImageBuffer;
+ public Pointer gtkPixbuf;
+ public Component menuItem;
+ public MenuItemSignalHandler signalHandler;
+ public long gtkSignalHandler;
+ public boolean isDefaultMenuItem;
+
+ @Override
+ public void componentAdded(ContainerEvent e)
+ {
+ AppIndicatorTrayIcon.this.printMenu(popup.getComponents(), 1);
+ gtk.gdk_threads_enter();
+ try
+ {
+ createGtkMenuItems(this, new Component[]{e.getChild()});
+ gtk.gtk_widget_show_all(popupPeer.gtkMenu);
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+ }
+
+ @Override
+ public void componentRemoved(ContainerEvent e)
+ {
+ AppIndicatorTrayIcon.this.printMenu(popup.getComponents(), 1);
+ for (PopupMenuPeer c : children)
+ {
+ if (c.menuItem == e.getChild())
+ {
+ gtk.gdk_threads_enter();
+ try
+ {
+ cleanMenu(c);
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+
+ children.remove(c);
+ break;
+ }
+ }
+ }
+ }
+
+ public void createTray()
+ {
+ gtk.gdk_threads_enter();
+ try
+ {
+ setupGtkMenu();
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+
+ new Thread()
+ {
+ public void run()
+ {
+ gtk.gtk_main();
+ }
+ }.start();
+ }
+
+ private void setupGtkMenu()
+ {
+ File iconFile = new File(imageIconToPath(mainIcon));
+ appIndicator = ai.app_indicator_new_with_path(
+ "jitsi",
+ iconFile.getName().replaceFirst("[.][^.]+$", ""),
+ AppIndicator1.APP_INDICATOR_CATEGORY.COMMUNICATIONS.ordinal(),
+ iconFile.getParent());
+
+ ai.app_indicator_set_title(appIndicator, title);
+ ai.app_indicator_set_icon_full(
+ appIndicator,
+ iconFile.getAbsolutePath(),
+ "Jitsi");
+
+ // create root menu
+ popupPeer = new PopupMenuPeer(null, popup);
+ popupPeer.gtkMenu = gtk.gtk_menu_new();
+
+ // transfer everything in the swing menu to the gtk menu
+ createGtkMenuItems(popupPeer, popup.getComponents());
+ gtk.gtk_widget_show_all(popupPeer.gtkMenu);
+
+ // attach the menu to the indicator
+ ai.app_indicator_set_menu(appIndicator, popupPeer.gtkMenu);
+ ai.app_indicator_set_status(
+ appIndicator,
+ AppIndicator1.APP_INDICATOR_STATUS.ACTIVE.ordinal());
+ }
+
+ private void cleanMenu(PopupMenuPeer peer)
+ {
+ assert !peer.isDefaultMenuItem;
+ for (PopupMenuPeer p : peer.children)
+ {
+ cleanMenu(p);
+ }
+
+ // - the root menu is released when it's unset from the indicator
+ // - gtk auto-frees menu item, submenu, image, and pixbuf
+ // - the imagebuffer was jna allocated, GC should take care of freeing
+ if (peer.gtkSignalHandler > 0)
+ {
+ gobject.g_signal_handler_disconnect(
+ peer.gtkMenuItem,
+ peer.gtkSignalHandler);
+ }
+
+ gtk.gtk_widget_destroy(peer.gtkMenuItem);
+ peer.gtkImageBuffer = null;
+ if (peer.menuItem instanceof JMenu)
+ {
+ ((JMenu)peer.menuItem).removeContainerListener(peer);
+ ((JMenu)peer.menuItem).getPopupMenu().removeContainerListener(peer);
+ }
+ }
+
+ private void createGtkMenuItems(
+ PopupMenuPeer parent,
+ Component[] components)
+ {
+ for (Component em : components)
+ {
+ PopupMenuPeer peer = new PopupMenuPeer(parent, em);
+ if (em instanceof JPopupMenu.Separator)
+ {
+ logger.debug("Creating separator");
+ peer.gtkMenuItem = gtk.gtk_separator_menu_item_new();
+ }
+
+ if (em instanceof JMenuItem)
+ {
+ createGtkMenuItem(peer);
+ }
+
+ if (em instanceof JMenu && peer.gtkMenuItem != null)
+ {
+ JMenu m = (JMenu)em;
+ logger.debug("Creating submenu on " + m.getText());
+ peer.gtkMenu = gtk.gtk_menu_new();
+ createGtkMenuItems(peer, m.getMenuComponents());
+ gtk.gtk_menu_item_set_submenu(peer.gtkMenuItem, peer.gtkMenu);
+ }
+
+ if (peer.gtkMenuItem != null)
+ {
+ parent.children.add(peer);
+ gtk.gtk_menu_shell_append(parent.gtkMenu, peer.gtkMenuItem);
+ }
+ }
+ }
+
+ private void createGtkMenuItem(PopupMenuPeer peer)
+ {
+ JMenuItem m = (JMenuItem)peer.menuItem;
+ logger.debug("Creating item for " + m.getClass().getName() + ": "
+ + m.getText());
+ if (m instanceof JCheckBoxMenuItem)
+ {
+ peer.gtkMenuItem = gtk.gtk_check_menu_item_new_with_label(
+ m.getText());
+ JCheckBoxMenuItem cb = (JCheckBoxMenuItem)m;
+ gtk.gtk_check_menu_item_set_active(
+ peer.gtkMenuItem,
+ cb.isSelected() ? 1 : 0);
+ }
+ else
+ {
+ peer.gtkMenuItem = gtk.gtk_image_menu_item_new_with_label(
+ m.getText());
+ if (m.getIcon() instanceof ImageIcon)
+ {
+ ImageIcon ii = ((ImageIcon) m.getIcon());
+ imageIconToGtkWidget(peer, ii);
+ if (peer.gtkImage != null)
+ {
+ gtk.gtk_image_menu_item_set_image(
+ peer.gtkMenuItem,
+ peer.gtkImage);
+ gtk.gtk_image_menu_item_set_always_show_image(
+ peer.gtkMenuItem,
+ 1);
+ }
+ }
+ }
+
+ if (peer.gtkMenuItem == null)
+ {
+ logger.debug("Could not create menu item for " + m.getText());
+ return;
+ }
+
+ MenuItemChangeListener micl = new MenuItemChangeListener(peer);
+ m.addPropertyChangeListener(micl);
+ m.addChangeListener(micl);
+
+ // skip GTK events if it's a submenu
+ if (!(m instanceof JMenu))
+ {
+ gtk.gtk_widget_set_sensitive(
+ peer.gtkMenuItem,
+ m.isEnabled() ? 1 : 0);
+ peer.signalHandler = new MenuItemSignalHandler(peer);
+ peer.gtkSignalHandler = gobject.g_signal_connect_data(
+ peer.gtkMenuItem,
+ "activate",
+ peer.signalHandler,
+ null,
+ null,
+ 0);
+ }
+ }
+
+ private String imageIconToPath(ImageIcon ii)
+ {
+ if (ii.getDescription() != null)
+ {
+ String path = extractedFiles.get(ii.getDescription());
+ if (path != null)
+ {
+ return path;
+ }
+ }
+
+ try
+ {
+ File f = File.createTempFile("jitsi-appindicator", ".png");
+ f.deleteOnExit();
+ try (FileImageOutputStream fios = new FileImageOutputStream(f))
+ {
+ if (!ImageIO.write(getBufferedImage(ii), "png", fios))
+ {
+ return null;
+ }
+
+ if (ii.getDescription() != null)
+ {
+ extractedFiles.put(
+ ii.getDescription(),
+ f.getAbsolutePath());
+ }
+
+ return f.getAbsolutePath();
+ }
+ }
+ catch (IOException e)
+ {
+ logger.debug("Failed to extract image: " + ii.getDescription(), e);
+ }
+
+ return null;
+ }
+
+ BufferedImage getBufferedImage(ImageIcon ii)
+ {
+ Image img = ii.getImage();
+ if (img == null)
+ {
+ return null;
+ }
+
+ if (img instanceof BufferedImage)
+ {
+ return (BufferedImage) img;
+ }
+
+ BufferedImage bi = new BufferedImage(
+ img.getWidth(null),
+ img.getHeight(null),
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics g = bi.createGraphics();
+ g.drawImage(img, 0, 0, null);
+ g.dispose();
+ return bi;
+ }
+
+ private void imageIconToGtkWidget(PopupMenuPeer peer, ImageIcon ii)
+ {
+ BufferedImage bi = getBufferedImage(ii);
+ if (bi == null)
+ {
+ return;
+ }
+
+ int[] pixels = bi.getRGB(
+ 0,
+ 0,
+ bi.getWidth(),
+ bi.getHeight(),
+ null,
+ 0,
+ bi.getWidth());
+
+ peer.gtkImageBuffer = new Memory(pixels.length * 4);
+ for (int i = 0; i < pixels.length; i++)
+ {
+ // convert from argb (big endian) -> rgba (little endian) => abgr
+ peer.gtkImageBuffer.setInt(i * 4, (pixels[i] & 0xFF000000) |
+ (pixels[i] << 16) |
+ (pixels[i] & 0xFF00) |
+ (pixels[i] >>> 16 & 0xFF));
+ }
+
+ peer.gtkPixbuf = gtk.gdk_pixbuf_new_from_data(
+ peer.gtkImageBuffer,
+ 0,
+ 1,
+ 8,
+ bi.getWidth(),
+ bi.getHeight(),
+ bi.getWidth() * 4,
+ null,
+ null);
+ peer.gtkImage = gtk.gtk_image_new_from_pixbuf(peer.gtkPixbuf);
+
+ // Now that the image ref's the buffer, we can release our own ref and
+ // the buffer will be free'd along with the image
+ gobject.g_object_unref(peer.gtkPixbuf);
+ }
+
+ private static class MenuItemChangeListener
+ implements PropertyChangeListener, ChangeListener
+ {
+ private PopupMenuPeer peer;
+ private JMenuItem menu;
+
+ public MenuItemChangeListener(PopupMenuPeer peer)
+ {
+ this.peer = peer;
+ this.menu = (JMenuItem)peer.menuItem;
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(menu.getText() + "::" + evt.getPropertyName());
+ }
+
+ switch (evt.getPropertyName())
+ {
+ case JMenuItem.TEXT_CHANGED_PROPERTY:
+ gtk.gdk_threads_enter();
+ try
+ {
+ gtk.gtk_menu_item_set_label(
+ peer.gtkMenuItem,
+ evt.getNewValue().toString());
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+
+ break;
+// case JMenuItem.ICON_CHANGED_PROPERTY:
+// gtk.gtk_image_menu_item_set_image(gtkMenuItem, image);
+// break;
+ case AccessibleContext.ACCESSIBLE_STATE_PROPERTY:
+ gtk.gdk_threads_enter();
+ try
+ {
+ gtk.gtk_widget_set_sensitive(
+ peer.gtkMenuItem,
+ AccessibleState.ENABLED.equals(
+ evt.getNewValue()) ? 1 : 0);
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void stateChanged(ChangeEvent e)
+ {
+ logger.debug(menu.getText() + " -> " + menu.isSelected());
+ gtk.gdk_threads_enter();
+ try
+ {
+ gtk.gtk_check_menu_item_set_active(
+ peer.gtkMenuItem,
+ menu.isSelected() ? 1 : 0);
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+ }
+ }
+
+ private static class MenuItemSignalHandler
+ implements SignalHandler, Runnable
+ {
+ private PopupMenuPeer peer;
+
+ MenuItemSignalHandler(PopupMenuPeer peer)
+ {
+ this.peer = peer;
+ }
+
+ @Override
+ public void signal(Pointer widget, Pointer data)
+ {
+ SwingUtilities.invokeLater(this);
+ }
+
+ @Override
+ public void run()
+ {
+ JMenuItem menu = (JMenuItem)peer.menuItem;
+ if (menu instanceof JCheckBoxMenuItem)
+ {
+ // Ignore GTK callback events if the menu state is
+ // already the same. Setting the selected state on the
+ // GTK sends the "activate" event, and would cause
+ // a loop
+ logger.debug("Checking selected state on: " + menu.getText());
+ if (menu.isSelected() == isGtkSelected())
+ {
+ return;
+ }
+ }
+
+ for (ActionListener l : menu.getActionListeners())
+ {
+ logger.debug("Invoking " + l + " on " + menu.getText());
+ l.actionPerformed(new ActionEvent(menu, 0, "activate"));
+ }
+ }
+
+ private boolean isGtkSelected()
+ {
+ gtk.gdk_threads_enter();
+ try
+ {
+ return gtk.gtk_check_menu_item_get_active(peer.gtkMenuItem) == 1;
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+ }
+ }
+
+ @Override
+ public void setDefaultAction(Object menuItem)
+ {
+ // It shouldn't be necessary that we hold a reference to the
+ // default item, it is contained in the menu. It might even create
+ // a memory leak. But if not set, the indicator loses track of it
+ // (at least on Debian). Unref an existing item, then ref the newly
+ // set
+ if (defaultMenuPeer != null)
+ {
+ gobject.g_object_unref(defaultMenuPeer.gtkMenuItem);
+ }
+
+ PopupMenuPeer peer = findMenuItem(popupPeer, menuItem);
+ if (peer != null && peer.gtkMenuItem != null)
+ {
+ gtk.gdk_threads_enter();
+ try
+ {
+ defaultMenuPeer = peer;
+ gobject.g_object_ref(peer.gtkMenuItem);
+ ai.app_indicator_set_secondary_activate_target(
+ appIndicator,
+ peer.gtkMenuItem);
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+ }
+ }
+
+ private PopupMenuPeer findMenuItem(PopupMenuPeer peer, Object menuItem)
+ {
+ if (peer.menuItem == menuItem)
+ {
+ logger.debug("Setting default action to: "
+ + ((JMenuItem)menuItem).getText()
+ + " @" + peer.gtkMenuItem);
+ return peer;
+ }
+
+ for (PopupMenuPeer p : peer.children)
+ {
+ PopupMenuPeer found = findMenuItem(p, menuItem);
+ if (found != null)
+ {
+ return found;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void addBalloonActionListener(ActionListener listener)
+ {
+ // not supported
+ }
+
+ @Override
+ public void displayMessage(String caption, String text,
+ MessageType messageType)
+ {
+ // not supported
+ }
+
+ @Override
+ public void setIcon(ImageIcon icon) throws NullPointerException
+ {
+ mainIcon = icon;
+ if (appIndicator != null)
+ {
+ gtk.gdk_threads_enter();
+ try
+ {
+ ai.app_indicator_set_icon(
+ appIndicator,
+ imageIconToPath(icon));
+ }
+ finally
+ {
+ gtk.gdk_threads_leave();
+ }
+ }
+ }
+
+ @Override
+ public void setIconAutoSize(boolean autoSize)
+ {
+ // nothing to do
+ }
+
+ private void printMenu(Component[] components, int indent)
+ {
+ if (!logger.isDebugEnabled())
+ {
+ return;
+ }
+
+ String p = String.format("%0" + indent * 4 + "d", 0).replace('0', ' ');
+ for (Component em : components)
+ {
+ if (em instanceof JPopupMenu.Separator)
+ {
+ logger.debug(p + "-----------------------");
+ }
+
+ if (em instanceof JMenuItem)
+ {
+ JMenuItem m = (JMenuItem) em;
+ logger.debug(p + em.getClass().getName() + ": " + m.getText());
+ }
+
+ if (em instanceof JMenu)
+ {
+ JMenu m = (JMenu) em;
+ printMenu(m.getMenuComponents(), indent + 1);
+ }
+ }
+ }
+}; \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/Gobject.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/Gobject.java
new file mode 100644
index 0000000..cfc7805
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/Gobject.java
@@ -0,0 +1,90 @@
+/*
+ * 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.osdependent.systemtray.appindicator;
+
+import java.util.*;
+
+import com.sun.jna.*;
+
+/**
+ * JNA mappings for GTK GObject types that are required for the tray menu.
+ *
+ * @author Ingo Bauersachs
+ */
+interface Gobject extends Library
+{
+ static final Gobject INSTANCE =
+ (Gobject) Native.loadLibrary("gobject-2.0", Gobject.class);
+
+ interface SignalHandler extends Callback
+ {
+ void signal(Pointer widget, Pointer data);
+ }
+
+ /**
+ * Connects a GCallback function to a signal for a particular object.
+ * Similar to g_signal_connect(), but allows to provide a GClosureNotify for
+ * the data which will be called when the signal handler is disconnected and
+ * no longer used. Specify connect_flags if you need ..._after() or
+ * ..._swapped() variants of this function.
+ *
+ * @param instance the instance to connect to.
+ * @param detailed_signal a string of the form "signal-name::detail".
+ * @param c_handler the GCallback to connect.
+ * @param data data to pass to c_handler calls.
+ * @param destroy_data a GClosureNotify for data.
+ * @param connect_flags a combination of GConnectFlags.
+ *
+ * @return the handler id (always greater than 0 for successful connections)
+ */
+ long g_signal_connect_data(Pointer instance, String detailed_signal,
+ SignalHandler c_handler, Pointer data, Pointer destroy_data,
+ int connect_flags);
+
+ /**
+ * Disconnects a handler from an instance so it will not be called during
+ * any future or currently ongoing emissions of the signal it has been
+ * connected to. The handler_id becomes invalid and may be reused. The
+ * handler_id has to be a valid signal handler id, connected to a signal of
+ * instance .
+ *
+ * @param instance The instance to remove the signal handler from.
+ * @param handler_id Handler id of the handler to be disconnected.
+ */
+ void g_signal_handler_disconnect(Pointer instance, long handler_id);
+
+ /**
+ * Decreases the reference count of object. When its reference count drops
+ * to 0, the object is finalized (i.e. its memory is freed). If the pointer
+ * to the GObject may be reused in future (for example, if it is an instance
+ * variable of another object), it is recommended to clear the pointer to
+ * NULL rather than retain a dangling pointer to a potentially invalid
+ * GObject instance. Use g_clear_object() for this.
+ *
+ * @param object a GObject.
+ */
+ void g_object_unref(Pointer object);
+
+ /**
+ * Increases the reference count of object.
+ *
+ * @param object a GObject.
+ * @return the same object.
+ */
+ Pointer g_object_ref(Pointer object);
+}
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/Gtk.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/Gtk.java
new file mode 100644
index 0000000..b66d59f
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/appindicator/Gtk.java
@@ -0,0 +1,68 @@
+/*
+ * 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.osdependent.systemtray.appindicator;
+
+import com.sun.jna.*;
+
+/**
+ * JNA mappings for the gtk2 library. Only functions required for the tray menu
+ * are defined.
+ *
+ * @author Ingo Bauersachs
+ */
+interface Gtk extends Library
+{
+ static final Gtk INSTANCE =
+ (Gtk) Native.loadLibrary("gtk-x11-2.0", Gtk.class);
+
+ public enum GtkIconSize
+ {
+ INVALID,
+ MENU,
+ SMALL_TOOLBAR,
+ LARGE_TOOLBAR,
+ BUTTON,
+ DND,
+ DIALOG
+ }
+
+ void gtk_init(int argc, String[] argv);
+ void gtk_main();
+ Pointer gtk_menu_new();
+ Pointer gtk_image_menu_item_new_with_label(String label);
+ Pointer gtk_separator_menu_item_new();
+ void gtk_menu_item_set_submenu(Pointer menu_item, Pointer submenu);
+ void gtk_image_menu_item_set_image(Pointer image_menu_item, Pointer image);
+ void gtk_image_menu_item_set_always_show_image(Pointer image_menu_item, int always_show);
+ void gtk_menu_item_set_label(Pointer menu_item, String label);
+ void gtk_menu_shell_append(Pointer menu_shell, Pointer child);
+ void gtk_widget_set_sensitive(Pointer widget, int sesitive);
+ void gtk_widget_show_all(Pointer widget);
+ void gtk_widget_destroy(Pointer widget);
+ Pointer gtk_check_menu_item_new_with_label(String label);
+ int gtk_check_menu_item_get_active(Pointer check_menu_item);
+ void gtk_check_menu_item_set_active(Pointer check_menu_item, int is_active);
+
+ void gdk_threads_enter();
+ void gdk_threads_leave();
+
+ Pointer gdk_pixbuf_new_from_data(Pointer data, int colorspace, int has_alpha,
+ int bits_per_sample, int width, int height, int rowstride,
+ Pointer destroy_fn, Pointer destroy_fn_data);
+ Pointer gtk_image_new_from_pixbuf(Pointer pixbuf);
+}
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTMouseAdapter.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTMouseAdapter.java
new file mode 100644
index 0000000..eb4a773
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTMouseAdapter.java
@@ -0,0 +1,124 @@
+/*
+ * 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.osdependent.systemtray.awt;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+import org.jitsi.util.*;
+
+/**
+ * Extended mouse adapter to show the JPopupMenu in Java 6. Based on: <a href=
+ * "https://community.oracle.com/blogs/ixmal/2006/05/03/using-jpopupmenu-trayicon">
+ * Using JPopupMenu in TrayIcon Blog</a> and <a href=
+ * "https://community.oracle.com/blogs/alexfromsun/2008/02/14/jtrayicon-update">
+ * JTrayIcon update Blog</a>.
+ *
+ * Use a hidden JWindow (JDialog for Windows) to manage the JPopupMenu.
+ *
+ * @author Damien Roth
+ */
+class AWTMouseAdapter
+ extends MouseAdapter
+{
+ private JPopupMenu popup = null;
+ private Window hiddenWindow = null;
+
+ public AWTMouseAdapter(JPopupMenu p)
+ {
+ this.popup = p;
+ this.popup.addPopupMenuListener(new PopupMenuListener()
+ {
+ public void popupMenuWillBecomeVisible(PopupMenuEvent e)
+ {}
+
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
+ {
+ if (hiddenWindow != null)
+ {
+ hiddenWindow.dispose();
+ hiddenWindow = null;
+ }
+ }
+
+ public void popupMenuCanceled(PopupMenuEvent e)
+ {
+ if (hiddenWindow != null)
+ {
+ hiddenWindow.dispose();
+ hiddenWindow = null;
+ }
+ }
+ });
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e)
+ {
+ showPopupMenu(e);
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e)
+ {
+ showPopupMenu(e);
+ }
+
+ private void showPopupMenu(MouseEvent e)
+ {
+ if (e.isPopupTrigger() && popup != null)
+ {
+ if (hiddenWindow == null)
+ {
+ if (OSUtils.IS_WINDOWS)
+ {
+ hiddenWindow = new JDialog((Frame) null);
+ ((JDialog) hiddenWindow).setUndecorated(true);
+ }
+ else
+ hiddenWindow = new JWindow((Frame) null);
+
+ hiddenWindow.setAlwaysOnTop(true);
+ Dimension size = popup.getPreferredSize();
+
+ Point centerPoint = GraphicsEnvironment
+ .getLocalGraphicsEnvironment()
+ .getCenterPoint();
+
+ if(e.getY() > centerPoint.getY())
+ hiddenWindow
+ .setLocation(e.getX(), e.getY() - size.height);
+ else
+ hiddenWindow
+ .setLocation(e.getX(), e.getY());
+
+ hiddenWindow.setVisible(true);
+
+ popup.show(
+ ((RootPaneContainer)hiddenWindow).getContentPane(),
+ 0, 0);
+
+ // popup works only for focused windows
+ hiddenWindow.toFront();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTSystemTray.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTSystemTray.java
new file mode 100644
index 0000000..1f211c7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTSystemTray.java
@@ -0,0 +1,78 @@
+/*
+ * 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.osdependent.systemtray.awt;
+
+import java.awt.*;
+
+import javax.swing.*;
+
+import org.jitsi.util.*;
+
+import net.java.sip.communicator.impl.osdependent.systemtray.SystemTray;
+import net.java.sip.communicator.impl.osdependent.systemtray.TrayIcon;
+
+/**
+ * Wrapper of the AWT SystemTray class.
+ */
+public class AWTSystemTray
+ extends SystemTray
+{
+ private final java.awt.SystemTray impl;
+
+ /**
+ * Creates a new instance of this class.
+ */
+ public AWTSystemTray()
+ {
+ impl = java.awt.SystemTray.getSystemTray();
+ }
+
+ @Override
+ public void addTrayIcon(TrayIcon trayIcon)
+ throws IllegalArgumentException
+ {
+ try
+ {
+ impl.add(((AWTTrayIcon) trayIcon).getImpl());
+ }
+ catch (AWTException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public TrayIcon createTrayIcon(ImageIcon icon, String tooltip,
+ Object popup)
+ {
+ return new AWTTrayIcon(icon.getImage(), tooltip, popup);
+ }
+
+ @Override
+ public boolean useSwingPopupMenu()
+ {
+ // enable swing for Java 1.6 except for the mac version
+ return !OSUtils.IS_MAC;
+ }
+
+ @Override
+ public boolean supportsDynamicMenu()
+ {
+ return true;
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTTrayIcon.java b/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTTrayIcon.java
new file mode 100644
index 0000000..2fa0318
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/systemtray/awt/AWTTrayIcon.java
@@ -0,0 +1,131 @@
+/*
+ * 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.osdependent.systemtray.awt;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.osdependent.systemtray.TrayIcon;
+
+/**
+ * Wrapper of the AWT TrayIcon class.
+ */
+public class AWTTrayIcon
+ implements TrayIcon
+{
+ private final java.awt.TrayIcon impl;
+
+ /**
+ * Creates a new instance of this class.
+ *
+ * @param image the <tt>Image</tt> to be used
+ * @param tooltip the string to be used as tooltip text; if the value is
+ * <tt>null</tt> no tooltip is shown
+ * @param popup the menu to be used for the tray icon's popup menu; if the
+ * value is <tt>null</tt> no popup menu is shown
+ */
+ public AWTTrayIcon(Image image, String tooltip,
+ Object popup)
+ {
+ if (popup instanceof JPopupMenu)
+ {
+ impl = new java.awt.TrayIcon(image, tooltip);
+ impl.addMouseListener(new AWTMouseAdapter((JPopupMenu)popup));
+ }
+ else if (popup instanceof PopupMenu)
+ {
+ impl = new java.awt.TrayIcon(image, tooltip, (PopupMenu)popup);
+ }
+ else if (popup == null)
+ {
+ impl = new java.awt.TrayIcon(image, tooltip);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid popup menu type");
+ }
+ }
+
+ public void setDefaultAction(final Object menuItem)
+ {
+ // clear all previous listeners
+ ActionListener[] previous = impl.getActionListeners();
+ for (ActionListener l : previous)
+ {
+ impl.removeActionListener(l);
+ }
+
+ // get the new handlers
+ final ActionListener[] listeners;
+ if (menuItem instanceof JMenuItem)
+ {
+ listeners = ((JMenuItem) menuItem).getActionListeners();
+ }
+ else if (menuItem instanceof MenuItem)
+ {
+ listeners = ((MenuItem) menuItem).getActionListeners();
+ }
+ else
+ {
+ return;
+ }
+
+ // create a custom handler to fake that the source is the menu item
+ impl.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ for (ActionListener l : listeners)
+ {
+ l.actionPerformed(new ActionEvent(menuItem,
+ e.getID(), e.getActionCommand()));
+ }
+ }
+ });
+ }
+
+ public void addBalloonActionListener(ActionListener listener)
+ {
+ // java.awt.TrayIcon doesn't support addBalloonActionListener()
+ }
+
+ public void displayMessage(String caption, String text,
+ java.awt.TrayIcon.MessageType messageType)
+ throws NullPointerException
+ {
+ impl.displayMessage(caption, text, messageType);
+ }
+
+ public void setIcon(ImageIcon icon) throws NullPointerException
+ {
+ impl.setImage(icon.getImage());
+ }
+
+ public void setIconAutoSize(boolean autoSize)
+ {
+ impl.setImageAutoSize(autoSize);
+ }
+
+ java.awt.TrayIcon getImpl()
+ {
+ return impl;
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/osdependent/windows/ImageConverter.java b/src/net/java/sip/communicator/impl/osdependent/windows/ImageConverter.java
new file mode 100644
index 0000000..99754d3
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/windows/ImageConverter.java
@@ -0,0 +1,172 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Copyright 2000-2016 JetBrains s.r.o.
+ * 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.osdependent.windows;
+
+import java.awt.image.*;
+import java.nio.*;
+
+import com.sun.jna.*;
+import com.sun.jna.platform.win32.*;
+import com.sun.jna.platform.win32.WinDef.*;
+
+/**
+ * Image conversion utilities.
+ *
+ * Parts of this code are based on AppIcon.java from IntelliJ community.
+ * Licensed under Apache 2.0, Copyright 2000-2016 JetBrains s.r.o.
+ */
+public class ImageConverter
+{
+ /**
+ * Converts the <tt>BufferedImage</tt> to an ICONDIR structure.
+ * @param src The source image to convert
+ * @return an ICONDIR structure with the data of the passed source image
+ */
+ public static byte[] writeTransparentIcoImage(BufferedImage src)
+ {
+ int bitCount = 32;
+
+ int scanline_size = (bitCount * src.getWidth() + 7) / 8;
+ if ((scanline_size % 4) != 0)
+ scanline_size += 4 - (scanline_size % 4); // pad scanline to 4 byte
+ // size.
+ int t_scanline_size = (src.getWidth() + 7) / 8;
+ if ((t_scanline_size % 4) != 0)
+ t_scanline_size += 4 - (t_scanline_size % 4); // pad scanline to 4
+ // byte size.
+ int imageSize = 40 + src.getHeight() * scanline_size
+ + src.getHeight() * t_scanline_size;
+
+ // sizeof(ICONDIR)
+ // + sizeof(ICONDIRENTRY)
+ // + (sizeof(BITMAPINFOHEADER)+data)
+ ByteBuffer bos = ByteBuffer.allocate(6 + 16 + imageSize);
+ bos.order(ByteOrder.LITTLE_ENDIAN);
+
+ // ICONDIR
+ bos.putShort((short) 0); // reserved
+ bos.putShort((short) 1); // 1=ICO, 2=CUR
+ bos.putShort((short) 1); // count
+
+ // ICONDIRENTRY
+ int iconDirEntryWidth = src.getWidth();
+ int iconDirEntryHeight = src.getHeight();
+ if (iconDirEntryWidth > 255 || iconDirEntryHeight > 255)
+ {
+ iconDirEntryWidth = 0;
+ iconDirEntryHeight = 0;
+ }
+ bos.put((byte) iconDirEntryWidth);
+ bos.put((byte) iconDirEntryHeight);
+ bos.put((byte) 0);
+ bos.put((byte) 0); // reserved
+ bos.putShort((short) 1); // color planes
+ bos.putShort((short) bitCount);
+ bos.putInt(imageSize);
+ bos.putInt(22); // image offset
+
+ // BITMAPINFOHEADER
+ bos.putInt(40); // size
+ bos.putInt(src.getWidth());
+ bos.putInt(2 * src.getHeight());
+ bos.putShort((short) 1); // planes
+ bos.putShort((short) bitCount);
+ bos.putInt(0); // compression
+ bos.putInt(0); // image size
+ bos.putInt(0); // x pixels per meter
+ bos.putInt(0); // y pixels per meter
+ bos.putInt(0); // colors used, 0 = (1 << bitCount) (ignored)
+ bos.putInt(0); // colors important
+
+ int bit_cache = 0;
+ int bits_in_cache = 0;
+ int row_padding = scanline_size - (bitCount * src.getWidth() + 7) / 8;
+ for (int y = src.getHeight() - 1; y >= 0; y--)
+ {
+ for (int x = 0; x < src.getWidth(); x++)
+ {
+ int argb = src.getRGB(x, y);
+
+ bos.put((byte) (0xff & argb));
+ bos.put((byte) (0xff & (argb >> 8)));
+ bos.put((byte) (0xff & (argb >> 16)));
+ bos.put((byte) (0xff & (argb >> 24)));
+ }
+
+ for (int x = 0; x < row_padding; x++)
+ bos.put((byte) 0);
+ }
+
+ int t_row_padding = t_scanline_size - (src.getWidth() + 7) / 8;
+ for (int y = src.getHeight() - 1; y >= 0; y--)
+ {
+ for (int x = 0; x < src.getWidth(); x++)
+ {
+ int argb = src.getRGB(x, y);
+ int alpha = 0xff & (argb >> 24);
+ bit_cache <<= 1;
+ if (alpha == 0)
+ bit_cache |= 1;
+ bits_in_cache++;
+ if (bits_in_cache >= 8)
+ {
+ bos.put((byte) (0xff & bit_cache));
+ bit_cache = 0;
+ bits_in_cache = 0;
+ }
+ }
+
+ if (bits_in_cache > 0)
+ {
+ bit_cache <<= (8 - bits_in_cache);
+ bos.put((byte) (0xff & bit_cache));
+ bit_cache = 0;
+ bits_in_cache = 0;
+ }
+
+ for (int x = 0; x < t_row_padding; x++)
+ bos.put((byte) 0);
+ }
+
+ byte[] result = new byte[bos.position()];
+ System.arraycopy(bos.array(), 0, result, 0, bos.position());
+ return result;
+ }
+
+ /**
+ * Converts an ICONDIR ico to an HICON handle
+ * @param ico the image data
+ * @return A Windows HICON handle
+ */
+ public static HICON createIcon(byte[] ico)
+ {
+ Memory buffer = new Memory(ico.length);
+ buffer.write(0, ico, 0, ico.length);
+ int nSize = 100;
+ int offset = User32Ex.INSTANCE.LookupIconIdFromDirectoryEx(buffer, true,
+ nSize, nSize, 0);
+ if (offset != 0)
+ {
+ return User32Ex.INSTANCE.CreateIconFromResourceEx(
+ buffer.share(offset), new WinDef.DWORD(0), true,
+ new WinDef.DWORD(0x00030000), nSize, nSize, 0);
+ }
+
+ return null;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/osdependent/windows/TaskBarList3.java b/src/net/java/sip/communicator/impl/osdependent/windows/TaskBarList3.java
new file mode 100644
index 0000000..a21cd8f
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/windows/TaskBarList3.java
@@ -0,0 +1,150 @@
+/*
+ * 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.osdependent.windows;
+
+import java.awt.*;
+import java.awt.image.*;
+
+import org.jitsi.util.*;
+
+import com.sun.jna.*;
+import com.sun.jna.platform.win32.*;
+import com.sun.jna.platform.win32.COM.*;
+import com.sun.jna.platform.win32.Guid.*;
+import com.sun.jna.platform.win32.WinDef.*;
+import com.sun.jna.platform.win32.WinNT.*;
+import com.sun.jna.ptr.*;
+
+/**
+ * JNA wrapper for the ITaskBarList3 COM interface.
+ * https://msdn.microsoft.com/en-us/library/dd391696(v=vs.85).aspx
+ *
+ * @author Ingo Bauersachs
+ */
+public class TaskBarList3
+ extends Unknown
+{
+ private static final GUID CLSID_TaskbarList =
+ new GUID("{56FDF344-FD6D-11d0-958A-006097C9A090}");
+
+ private static final GUID IID_ITaskbarList3 =
+ new GUID("{ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf}");
+
+ private static TaskBarList3 instance;
+
+ /**
+ * Gets the ITaskBarList3 interface and initializes it with HrInit
+ * @return A ready to use TaskBarList3 object.
+ * @throws COMException when the interface could not be accessed
+ */
+ public static TaskBarList3 getInstance()
+ {
+ if (instance == null && OSUtils.IS_WINDOWS)
+ {
+ Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, 0);
+ PointerByReference p = new PointerByReference();
+ WinNT.HRESULT hr =
+ Ole32.INSTANCE.CoCreateInstance(CLSID_TaskbarList, Pointer.NULL,
+ ObjBase.CLSCTX_ALL, IID_ITaskbarList3, p);
+ COMUtils.checkRC(hr);
+ instance = new TaskBarList3(p.getValue());
+ }
+
+ return instance;
+ }
+
+ private TaskBarList3(Pointer p)
+ {
+ super(p);
+ HrInit();
+ }
+
+ // VTable
+ // ------
+ // IUnknown:
+ // 0: AddRef
+ // 1: QueryInterface
+ // 2: Release
+ //
+ // ITaskBarList:
+ // 3: HrInit
+ // 4: AddTab
+ // 5: DeleteTab
+ // 6: ActivateTab
+ // 7: SetActiveAlt
+ //
+ // ITaskBarList2
+ // 8: MarkFullscreenWindow
+ //
+ // ITaskBarList3:
+ // 9: SetProgressValue
+ // 10: SetProgressState
+ // 11: RegisterTab
+ // 12: UnregisterTab
+ // 13: SetTabOrder
+ // 14: SetTabActive
+ // 15: ThumbBarAddButtons
+ // 16: ThumbBarAddButtons
+ // 17: ThumbBarSetImageList
+ // 18: SetOverlayIcon
+ // 19: SetThumbnailTooltip
+ // 20: SetThumbnailClip
+ //
+ // ITaskbarList4:
+ // 21: SetTabProperties
+
+ /**
+ * https://msdn.microsoft.com/en-us/library/bb774650(v=vs.85).aspx
+ */
+ private void HrInit()
+ {
+ int hr = this._invokeNativeInt(3, new Object[]
+ { this.getPointer() });
+ COMUtils.checkRC(new HRESULT(hr));
+ }
+
+ /**
+ * https://msdn.microsoft.com/en-us/library/dd391696(v=vs.85).aspx
+ */
+ private void SetOverlayIcon(HWND hwnd, HICON hIcon, String pszDescription)
+ {
+ int hr = this._invokeNativeInt(18, new Object[]
+ { this.getPointer(), hwnd, hIcon, pszDescription });
+ COMUtils.checkRC(new HRESULT(hr));
+ }
+
+ /**
+ * Sets an overlay image to the taskbar icon.
+ * @param frame The window that should receive the overlay
+ * @param image The overlay image, can be <tt>null</tt> to clear the overlay
+ * @param description An optional tooltip text, can be <tt>null</tt>
+ */
+ public void SetOverlayIcon(Component frame, BufferedImage image,
+ String description)
+ {
+ HICON ico = null;
+ if (image != null)
+ {
+ byte[] iconBytes = ImageConverter.writeTransparentIcoImage(image);
+ ico = ImageConverter.createIcon(iconBytes);
+ }
+
+ HWND hwnd = new HWND(Native.getComponentPointer(frame));
+ SetOverlayIcon(hwnd, ico, description);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/osdependent/windows/User32Ex.java b/src/net/java/sip/communicator/impl/osdependent/windows/User32Ex.java
new file mode 100644
index 0000000..59f3a7a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/osdependent/windows/User32Ex.java
@@ -0,0 +1,47 @@
+/*
+ * 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.osdependent.windows;
+
+import com.sun.jna.*;
+import com.sun.jna.platform.win32.*;
+import com.sun.jna.win32.*;
+
+/**
+ * Extension to missing user32 Windows APIs
+ *
+ * @author Ingo Bauersachs
+ */
+interface User32Ex
+ extends StdCallLibrary
+{
+ User32Ex INSTANCE = (User32Ex) Native.loadLibrary("user32", User32Ex.class,
+ W32APIOptions.DEFAULT_OPTIONS);
+
+ /**
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/ms648074(v=vs.85).aspx
+ */
+ int LookupIconIdFromDirectoryEx(Memory presbits, boolean fIcon,
+ int cxDesired, int cyDesired, int Flags);
+
+ /**
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/ms648061(v=vs.85).aspx
+ */
+ WinDef.HICON CreateIconFromResourceEx(Pointer pbIconBits,
+ WinDef.DWORD cbIconBits, boolean fIcon, WinDef.DWORD dwVersion,
+ int cxDesired, int cyDesired, int uFlags);
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingConfigurationImpl.java b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingConfigurationImpl.java
index b26b11a..978c74c 100644
--- a/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingConfigurationImpl.java
+++ b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingConfigurationImpl.java
@@ -60,6 +60,10 @@ public class PacketLoggingConfigurationImpl
configService.getBoolean(
PACKET_LOGGING_ICE4J_ENABLED_PROPERTY_NAME,
isIce4JLoggingEnabled()));
+ super.setArbitraryLoggingEnabled(
+ configService.getBoolean(
+ PACKET_LOGGING_ARBITRARY_ENABLED_PROPERTY_NAME,
+ isArbitraryLoggingEnabled()));
super.setLimit(
configService.getLong(
PACKET_LOGGING_FILE_SIZE_PROPERTY_NAME,
diff --git a/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java
index 788e8db..1e459f1 100644
--- a/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/packetlogging/PacketLoggingServiceImpl.java
@@ -20,6 +20,7 @@ package net.java.sip.communicator.impl.packetlogging;
import java.io.*;
import java.util.*;
+import com.google.common.collect.*;
import net.java.sip.communicator.util.*;
import org.jitsi.service.fileaccess.*;
@@ -41,6 +42,14 @@ public class PacketLoggingServiceImpl
= Logger.getLogger(PacketLoggingServiceImpl.class);
/**
+ * The max size of the <tt>EvictingQueue</tt> that the saver thread
+ * is using.
+ *
+ * TODO this needs to be configurable eventually.
+ */
+ private static final int EVICTING_QUEUE_MAX_SIZE = 1000;
+
+ /**
* The OutputStream we are currently writing to.
*/
private FileOutputStream outputStream = null;
@@ -62,10 +71,17 @@ public class PacketLoggingServiceImpl
new byte[]{
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
- (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
- (byte)0x08, (byte)0x00
+ (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00
};
+ /** IEEE 802.3 EtherType for IPv4 */
+ private final static byte[] ipv4EtherType =
+ new byte[] { 0x08, 0x00 };
+
+ /** IEEE 802.3 EtherType for IPv6 */
+ private final static byte[] ipv6EtherType =
+ new byte[] { (byte)0x86, (byte)0xdd };
+
/**
* The fake ipv4 header we use as template.
*/
@@ -313,22 +329,22 @@ public class PacketLoggingServiceImpl
{
switch(protocol)
{
- case SIP:
- return cfg.isSipLoggingEnabled();
- case JABBER:
- return cfg.isJabberLoggingEnabled();
- case RTP:
- return cfg.isRTPLoggingEnabled();
- case ICE4J:
- return cfg.isIce4JLoggingEnabled();
- default:
- /*
- * It may seem like it was unnecessary to invoke
- * getConfiguration and isGlobalLoggingEnabled prior to
- * checking that the specified protocol is supported but,
- * actually, there are no other ProtocolName values.
- */
- return false;
+ case SIP:
+ return cfg.isSipLoggingEnabled();
+ case JABBER:
+ return cfg.isJabberLoggingEnabled();
+ case RTP:
+ return cfg.isRTPLoggingEnabled();
+ case ICE4J:
+ return cfg.isIce4JLoggingEnabled();
+ case ARBITRARY:
+ return cfg.isArbitraryLoggingEnabled();
+ default:
+ // It may seem like it was unnecessary to invoke
+ // getConfiguration and isGlobalLoggingEnabled prior to checking
+ // that the specified protocol is supported but, actually, there
+ // are no other ProtocolName values.
+ return false;
}
}
else
@@ -553,6 +569,7 @@ public class PacketLoggingServiceImpl
int tsSec = (int)(current/1000);
int tsUsec = (int)((current%1000) * 1000);
int feakHeaderLen = fakeEthernetHeader.length +
+ (isIPv4 ? ipv4EtherType : ipv6EtherType).length +
ipHeader.length + transportHeader.length;
int inclLen = packet.packetLength + feakHeaderLen;
int origLen = inclLen;
@@ -577,6 +594,7 @@ public class PacketLoggingServiceImpl
addInt(origLen);
outputStream.write(fakeEthernetHeader);
+ outputStream.write(isIPv4 ? ipv4EtherType : ipv6EtherType);
outputStream.write(ipHeader);
outputStream.write(transportHeader);
outputStream.write(
@@ -767,7 +785,8 @@ public class PacketLoggingServiceImpl
/**
* List of packets queued to be written in the file.
*/
- private final List<Packet> pktsToSave = new ArrayList<Packet>();
+ private final EvictingQueue<Packet> pktsToSave
+ = EvictingQueue.create(EVICTING_QUEUE_MAX_SIZE);
/**
* Initializes a new <tt>SaverThread</tt>.
@@ -803,7 +822,7 @@ public class PacketLoggingServiceImpl
continue;
}
- pktToSave = pktsToSave.remove(0);
+ pktToSave = pktsToSave.poll();
}
if(pktToSave != null)
@@ -842,6 +861,11 @@ public class PacketLoggingServiceImpl
*/
public synchronized void queuePacket(Packet packet)
{
+ if (EVICTING_QUEUE_MAX_SIZE - pktsToSave.size() == 0)
+ {
+ logger.warn("Queue is full, packets are being evicted.");
+ }
+
pktsToSave.add(packet);
notifyAll();
}
diff --git a/src/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf b/src/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf
index 374294f..2099f02 100644
--- a/src/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf
+++ b/src/net/java/sip/communicator/impl/packetlogging/packetlogging.manifest.mf
@@ -16,4 +16,5 @@ Import-Package: org.osgi.framework,
javax.swing,
javax.swing.border,
javax.swing.event,
- javax.swing.text
+ javax.swing.text,
+ com.google.common.collect
diff --git a/src/net/java/sip/communicator/impl/phonenumbers/PhoneNumberI18nServiceImpl.java b/src/net/java/sip/communicator/impl/phonenumbers/PhoneNumberI18nServiceImpl.java
index d1942b2..4d4d417 100644
--- a/src/net/java/sip/communicator/impl/phonenumbers/PhoneNumberI18nServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/phonenumbers/PhoneNumberI18nServiceImpl.java
@@ -18,6 +18,9 @@
package net.java.sip.communicator.impl.phonenumbers;
import com.google.i18n.phonenumbers.*;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.*;
+import com.google.i18n.phonenumbers.Phonenumber.*;
+
import net.java.sip.communicator.service.protocol.*;
import org.jitsi.service.configuration.*;
@@ -183,6 +186,33 @@ public class PhoneNumberI18nServiceImpl
}
/**
+ * Tries to format the passed phone number into the international format. If
+ * parsing fails or the string is not recognized as a valid phone number,
+ * the input is returned as is.
+ *
+ * @param phoneNumber The phone number to format.
+ * @return the formatted phone number in the international format.
+ */
+ public String formatForDisplay(String phoneNumber)
+ {
+ try
+ {
+ PhoneNumber pn = PhoneNumberUtil.getInstance().parse(phoneNumber,
+ System.getProperty("user.country"));
+ if (PhoneNumberUtil.getInstance().isPossibleNumber(pn))
+ {
+ return PhoneNumberUtil.getInstance().format(pn,
+ PhoneNumberFormat.INTERNATIONAL);
+ }
+ }
+ catch (NumberParseException e)
+ {
+ }
+
+ return phoneNumber;
+ }
+
+ /**
* Indicates if the given string is possibly a phone number.
*
* @param possibleNumber the string to be verified
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ContactDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ContactDictImpl.java
deleted file mode 100644
index 924c0f7..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/ContactDictImpl.java
+++ /dev/null
@@ -1,343 +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.dict;
-
-import net.java.dict4j.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * An implementation of a Dict contact
- *
- * @author ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class ContactDictImpl
- extends AbstractContact
-{
- private Logger logger = Logger.getLogger(ContactDictImpl.class);
-
- /**
- * Icon
- */
- private static byte[] icon = DictActivator.getResources()
- .getImageInBytes("service.protocol.dict.DICT_64x64");
-
- /**
- * The id of the contact.
- */
- private String contactID = null;
-
- /**
- * The provider that created us.
- */
- private ProtocolProviderServiceDictImpl parentProvider = null;
-
- /**
- * The group that belong to.
- */
- private ContactGroupDictImpl parentGroup = null;
-
- /**
- * The presence status of the contact.
- */
- private PresenceStatus presenceStatus = DictStatusEnum.ONLINE;
-
- /**
- * Determines whether this contact is persistent, i.e. member of the contact
- * list or whether it is here only temporarily.
- */
- private boolean isPersistent = true;
-
- /**
- * 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;
-
- /**
- * The string in a "humain readable and understandable representation" of
- * the dictionnaire. In brief this is a short description of the dictionary.
- */
- private String dictName = null;
-
- /**
- * Creates an instance of a meta contact with the specified string used
- * as a name and identifier.
- *
- * @param databaseCode The identifier of this contact (also used as a name).
- * @param parentProvider The provider that created us.
- */
- public ContactDictImpl(
- String databaseCode,
- ProtocolProviderServiceDictImpl parentProvider)
- {
- this.contactID = databaseCode;
- this.parentProvider = parentProvider;
- }
-
- /**
- * This method is only called when the contact is added to a new
- * <tt>ContactGroupDictImpl</tt> by the
- * <tt>ContactGroupDictImpl</tt> itself.
- *
- * @param newParentGroup the <tt>ContactGroupDictImpl</tt> that is now
- * parent of this <tt>ContactDictImpl</tt>
- */
- void setParentGroup(ContactGroupDictImpl newParentGroup)
- {
- this.parentGroup = newParentGroup;
- }
-
- /**
- * Returns a String that can be used for identifying the contact.
- *
- * @return a String id representing and uniquely identifying the contact.
- */
- public String getContactID()
- {
- return contactID;
- }
-
- /**
- * 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()
- {
- if (dictName == null)
- {
- if (this.contactID.equals("*"))
- {
- this.dictName = DictActivator.getResources()
- .getI18NString("plugin.dictaccregwizz.ANY_DICTIONARY");
- }
- else if (this.contactID.equals("!"))
- {
- this.dictName = DictActivator.getResources()
- .getI18NString("plugin.dictaccregwizz.FIRST_MATCH");
- }
- else
- {
- try
- {
- this.dictName = this.parentProvider.getConnection()
- .getDictionaryName(this.contactID);
- }
- catch (DictException dx)
- {
- logger.error("Error while getting dictionary long name", dx);
- }
-
- if (this.dictName == null)
- this.dictName = this.contactID;
- }
- }
-
- return dictName;
- }
-
- /**
- * 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 icon;
- }
-
- /**
- * Returns the status of the contact.
- *
- * @return always DictStatusEnum.ONLINE.
- */
- public PresenceStatus getPresenceStatus()
- {
- return this.presenceStatus;
- }
-
- /**
- * Sets <tt>dictPresenceStatus</tt> as the PresenceStatus that this
- * contact is currently in.
- * @param dictPresenceStatus the <tt>DictPresenceStatus</tt>
- * currently valid for this contact.
- */
- public void setPresenceStatus(PresenceStatus dictPresenceStatus)
- {
- this.presenceStatus = dictPresenceStatus;
- }
-
- /**
- * 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>ContactGroupDictImpl</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("ContactDictImpl[ 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;
- }
-
- /**
- * 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;
- }
-
- /**
- * 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>OperationSetPersistentPresenceGibberishImpl</tt> that
- * this contact belongs to.
- */
- public OperationSetPersistentPresenceDictImpl
- getParentPresenceOperationSet()
- {
- return (OperationSetPersistentPresenceDictImpl) parentProvider
- .getOperationSet(OperationSetPersistentPresence.class);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ContactGroupDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ContactGroupDictImpl.java
deleted file mode 100644
index e52ce96..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/ContactGroupDictImpl.java
+++ /dev/null
@@ -1,588 +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.dict;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * A simple, straightforward implementation of a dict ContactGroup. Since
- * the Dict protocol is not a real one, 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 ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class ContactGroupDictImpl
- implements ContactGroup
-{
-
- /**
- * The name of this Dict contact group.
- */
- private String groupName = null;
-
- /**
- * The list of this group's members.
- */
- private List<Contact> contacts = new ArrayList<Contact>();
-
- /**
- * The list of sub groups belonging to this group.
- */
- private List<ContactGroup> subGroups = new ArrayList<ContactGroup>();
-
- /**
- * The group that this group belongs to (or null if this is the root group).
- */
- private ContactGroupDictImpl 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 = true;
-
- /**
- * The protocol provider that created us.
- */
- private ProtocolProviderServiceDictImpl 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 ContactGroupDictImpl with the specified name.
- *
- * @param groupName the name of the group.
- * @param parentProvider the protocol provider that created this group.
- */
- public ContactGroupDictImpl(
- String groupName,
- ProtocolProviderServiceDictImpl 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 ContactDictImpl to add to this group.
- */
- public void addContact(ContactDictImpl 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 ContactGroupDictImpl to add as a subgroup to this group.
- */
- public void addSubgroup(ContactGroupDictImpl subgroup)
- {
- this.subGroups.add(subgroup);
- subgroup.setParentGroup(this);
- }
-
- /**
- * Sets the group that is the new parent of this group
- * @param parent ContactGroupDictImpl
- */
- void setParentGroup(ContactGroupDictImpl 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 ContactGroupDictImpl subgroup to remove.
- */
- public void removeSubGroup(ContactGroupDictImpl subgroup)
- {
- this.subGroups.remove(subgroup);
- subgroup.setParentGroup(null);
- }
-
- /**
- * Returns the group that is parent of the specified dictGroup or null
- * if no parent was found.
- * @param dictGroup the group whose parent we're looking for.
- * @return the ContactGroupDictImpl instance that dictGroup
- * belongs to or null if no parent was found.
- */
- public ContactGroupDictImpl findGroupParent(ContactGroupDictImpl dictGroup)
- {
- if ( subGroups.contains(dictGroup) )
- return this;
-
- Iterator<ContactGroup> subGroupsIter = subgroups();
- while (subGroupsIter.hasNext())
- {
- ContactGroupDictImpl subgroup
- = (ContactGroupDictImpl) subGroupsIter.next();
-
- ContactGroupDictImpl parent
- = subgroup.findGroupParent(dictGroup);
-
- if(parent != null)
- return parent;
- }
- return null;
- }
-
- /**
- * Returns the group that is parent of the specified dictContact or
- * null if no parent was found.
- *
- * @param dictContact the contact whose parent we're looking for.
- * @return the ContactGroupDictImpl instance that dictContact
- * belongs to or <tt>null</tt> if no parent was found.
- */
- public ContactGroupDictImpl findContactParent(
- ContactDictImpl dictContact)
- {
- if ( contacts.contains(dictContact) )
- return this;
-
- Iterator<ContactGroup> subGroupsIter = subgroups();
- while (subGroupsIter.hasNext())
- {
- ContactGroupDictImpl subgroup
- = (ContactGroupDictImpl) subGroupsIter.next();
-
- ContactGroupDictImpl parent
- = subgroup.findContactParent(dictContact);
-
- 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())
- {
- ContactDictImpl contact = (ContactDictImpl) 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())
- {
- ContactGroupDictImpl contactGroup
- = (ContactGroupDictImpl) 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 ContactDictImpl to remove from this group
- */
- public void removeContact(ContactDictImpl 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 ContactDictImpl
- */
- public ContactDictImpl findContactByID(String id)
- {
- //first go through the contacts that are direct children.
- Iterator<Contact> contactsIter = contacts();
-
- while(contactsIter.hasNext())
- {
- ContactDictImpl mContact = (ContactDictImpl)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() )
- {
- ContactGroupDictImpl mGroup = (ContactGroupDictImpl)groupsIter.next();
-
- ContactDictImpl 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())
- {
- ContactGroupDictImpl group = (ContactGroupDictImpl)subGroups.next();
- buff.append(group.toString());
- if (subGroups.hasNext())
- buff.append("\n");
- }
-
- buff.append("\nChildContacts="+countContacts()+":[");
-
- Iterator<Contact> contacts = contacts();
- while (contacts.hasNext())
- {
- ContactDictImpl contact = (ContactDictImpl) 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 ContactGroupDictImpl))
- return false;
-
- ContactGroupDictImpl dictGroup
- = (ContactGroupDictImpl)obj;
-
- if( ! dictGroup.getGroupName().equals(getGroupName())
- || ! dictGroup.getUID().equals(getUID())
- || dictGroup.countContacts() != countContacts()
- || dictGroup.countSubgroups() != countSubgroups())
- return false;
-
- //traverse child contacts
- Iterator<Contact> theirContacts = dictGroup.contacts();
-
- while(theirContacts.hasNext())
- {
- ContactDictImpl theirContact
- = (ContactDictImpl)theirContacts.next();
-
- ContactDictImpl ourContact
- = (ContactDictImpl)getContact(theirContact.getAddress());
-
- if(ourContact == null
- || !ourContact.equals(theirContact))
- return false;
- }
-
- //traverse subgroups
- Iterator<ContactGroup> theirSubgroups = dictGroup.subgroups();
-
- while(theirSubgroups.hasNext())
- {
- ContactGroupDictImpl theirSubgroup
- = (ContactGroupDictImpl)theirSubgroups.next();
-
- ContactGroupDictImpl ourSubgroup
- = (ContactGroupDictImpl)getGroup(
- theirSubgroup.getGroupName());
-
- if(ourSubgroup == null
- || !ourSubgroup.equals(theirSubgroup))
- return false;
- }
-
- return true;
- }
-}
-
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictAccountID.java b/src/net/java/sip/communicator/impl/protocol/dict/DictAccountID.java
deleted file mode 100644
index 538ae71..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/DictAccountID.java
+++ /dev/null
@@ -1,70 +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.dict;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * The Dict implementation of a sip-communicator account id.
- * @author LITZELMANN Cedric
- * @author ROTH Damien
- */
-public class DictAccountID
- extends AccountID
-{
- /**
- * Creates an account id from the specified id and account properties.
- *
- * @param userID the user identifier correspnding to the account
- * @param accountProperties any other properties necessary for the account.
- */
- DictAccountID(String userID, Map<String, String> accountProperties)
- {
- super(userID, accountProperties, ProtocolNames.DICT, "dict.org");
- }
-
- /**
- * Returns the dict server adress
- * @return the dict server adress
- */
- public String getHost()
- {
- return getAccountPropertyString(ProtocolProviderFactory.SERVER_ADDRESS);
- }
-
- /**
- * Returns the dict server port
- * @return the dict server port
- */
- public int getPort()
- {
- return Integer
- .parseInt(getAccountPropertyString(ProtocolProviderFactory.SERVER_PORT));
- }
-
- /**
- * Returns the selected strategy
- * @return the selected strategy
- */
- public String getStrategy()
- {
- return getAccountPropertyString(ProtocolProviderFactory.STRATEGY);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java b/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java
deleted file mode 100644
index b5976c7..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/DictActivator.java
+++ /dev/null
@@ -1,146 +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.dict;
-
-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.*;
-
-/**
- * Loads the Dict provider factory and registers its services in the OSGI
- * bundle context.
- *
- * @author ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class DictActivator
- implements BundleActivator
-{
- private static final Logger logger = Logger.getLogger(DictActivator.class);
-
- /**
- * The currently valid bundle context.
- */
- private static BundleContext bundleContext = null;
-
- private ServiceRegistration dictPpFactoryServReg = null;
- private static ProtocolProviderFactoryDictImpl
- dictProviderFactory = null;
-
- private static ResourceManagementService resourceService;
-
- /**
- * Called when this bundle is started. In here we'll export the
- * dict 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
- {
- bundleContext = context;
-
- Hashtable<String,String> hashtable = new Hashtable<String,String>();
- hashtable.put(ProtocolProviderFactory.PROTOCOL, ProtocolNames.DICT);
-
- dictProviderFactory = new ProtocolProviderFactoryDictImpl();
-
- //reg the dict provider factory.
- dictPpFactoryServReg = context.registerService(
- ProtocolProviderFactory.class.getName(),
- dictProviderFactory,
- hashtable);
-
- if (logger.isInfoEnabled())
- logger.info("DICT 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;
- }
-
- /**
- * 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
- {
-
- dictProviderFactory.stop();
- dictPpFactoryServReg.unregister();
-
- if (logger.isInfoEnabled())
- logger.info("DICT protocol implementation [STOPPED].");
- }
-
- /**
- * Returns a reference to the protocol provider factory that we have
- * registered.
- * @return a reference to the <tt>ProtocolProviderFactoryDictImpl</tt>
- * instance that we have registered from this package.
- */
- public static ProtocolProviderFactoryDictImpl getProtocolProviderFactory()
- {
- return dictProviderFactory;
- }
-
-
- /**
- * Returns the <tt>ResourceManagementService</tt>.
- *
- * @return the <tt>ResourceManagementService</tt>.
- */
- public static ResourceManagementService getResources()
- {
- if (resourceService == null)
- {
- ServiceReference serviceReference = bundleContext
- .getServiceReference(ResourceManagementService.class.getName());
-
- if(serviceReference == null)
- return null;
-
- resourceService = (ResourceManagementService) bundleContext
- .getService(serviceReference);
- }
-
- return resourceService;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java b/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java
deleted file mode 100644
index 87086f7..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/DictStatusEnum.java
+++ /dev/null
@@ -1,88 +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.dict;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * An implementation of <tt>PresenceStatus</tt> that enumerates all states that
- * a Dict contact can fall into.
- *
- * @author ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class DictStatusEnum
- extends PresenceStatus
-{
-
- /**
- * Indicates an Offline status or status with 0 connectivity.
- */
- public static final DictStatusEnum OFFLINE
- = new DictStatusEnum(
- 0, "Offline",
- DictActivator.getResources()
- .getImageInBytes("service.protocol.dict.OFFLINE_STATUS_ICON"));
-
- /**
- * The Online status. Indicate that the user is able and willing to
- * communicate.
- */
- public static final DictStatusEnum ONLINE
- = new DictStatusEnum(
- 65, "Online",
- DictActivator.getResources()
- .getImageInBytes("service.protocol.dict.DICT_16x16"));
-
- /**
- * Initialize the list of supported status states.
- */
- private static List<PresenceStatus> supportedStatusSet = new LinkedList<PresenceStatus>();
- static
- {
- supportedStatusSet.add(OFFLINE);
- supportedStatusSet.add(ONLINE);
- }
-
- /**
- * Creates an instance of <tt>RssPresneceStatus</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 DictStatusEnum(int status,
- String statusName,
- byte[] statusIcon)
- {
- super(status, statusName, statusIcon);
- }
-
- /**
- * Returns an iterator over all status instances supproted by the rss
- * provider.
- * @return an <tt>Iterator</tt> over all status instances supported by the
- * rss provider.
- */
- static Iterator<PresenceStatus> supportedStatusSet()
- {
- return supportedStatusSet.iterator();
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java
deleted file mode 100644
index 5b95dd4..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/MessageDictImpl.java
+++ /dev/null
@@ -1,46 +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.dict;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * Very simple message implementation for the Dict protocol.
- *
- * @author ROTH Damien
- * @author LITZELMANN Cedric
- * @author Lubomir Marinov
- */
-public class MessageDictImpl
- extends AbstractMessage
-{
-
- /**
- * Creates a message instance according to the specified parameters.
- *
- * @param content the message body
- * @param contentType message content type or null for text/plain
- * @param contentEncoding message encoding or null for UTF8
- * @param subject the subject of the message or null for no subject.
- */
- public MessageDictImpl(String content, String contentType,
- String contentEncoding, String subject)
- {
- super(content, contentType, contentEncoding, subject);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java
deleted file mode 100644
index f1f4973..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetBasicInstantMessagingDictImpl.java
+++ /dev/null
@@ -1,412 +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.dict;
-
-import java.util.*;
-
-import net.java.dict4j.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Instant messaging functionalities for the Dict protocol.
- *
- * @author ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class OperationSetBasicInstantMessagingDictImpl
- extends AbstractOperationSetBasicInstantMessaging
- implements RegistrationStateChangeListener
-{
- /**
- * The currently valid persistent presence operation set.
- */
- private OperationSetPersistentPresenceDictImpl opSetPersPresence = null;
-
- /**
- * The protocol provider that created us.
- */
- private ProtocolProviderServiceDictImpl parentProvider = null;
-
- private DictAccountID accountID;
-
- /**
- * 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>OperationSetPersistentPresenceDictImpl</tt> instance.
- */
- public OperationSetBasicInstantMessagingDictImpl(
- ProtocolProviderServiceDictImpl provider,
- OperationSetPersistentPresenceDictImpl opSetPersPresence)
- {
- this.opSetPersPresence = opSetPersPresence;
- this.parentProvider = provider;
- this.accountID = (DictAccountID) provider.getAccountID();
-
- parentProvider.addRegistrationStateChangeListener(this);
- }
-
- @Override
- public Message createMessage(String content)
- {
- return new MessageDictImpl(content, HTML_MIME_TYPE,
- DEFAULT_MIME_ENCODING, null);
- }
-
- @Override
- public Message createMessage(String content, String contentType,
- String encoding, String subject)
- {
- return new MessageDictImpl(content, contentType, encoding, subject);
- }
-
- /**
- * 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 ICQ 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 ContactDictImpl) )
- {
- throw new IllegalArgumentException(
- "The specified contact is not a Dict contact."
- + to);
- }
-
- // Remove all html tags from the message
- message = createMessage(Html2Text.extractText(message.getContent()));
-
- // Display the queried word
- fireMessageDelivered(message, to);
-
- this.submitDictQuery((ContactDictImpl) to, message);
- }
-
- /**
- * Determines whether the protocol provider (or the protocol itself) supports
- * 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 false;
- }
-
- /**
- * 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)
- {
- if(contentType.equals(DEFAULT_MIME_TYPE))
- return true;
- else if(contentType.equals(HTML_MIME_TYPE))
- return true;
- else
- return false;
- }
-
- /**
- * Returns the protocol provider that this operation set belongs to.
- *
- * @return a reference to the <tt>ProtocolProviderServiceDictImpl</tt>
- * instance that this operation set belongs to.
- */
- public ProtocolProviderServiceDictImpl getParentProvider()
- {
- return this.parentProvider;
- }
-
- /**
- * Returns a reference to the presence operation set instance used by our
- * source provider.
- *
- * @return a reference to the <tt>OperationSetPersistentPresenceDictImpl</tt>
- * instance used by this provider.
- */
- public OperationSetPersistentPresenceDictImpl getOpSetPersPresence()
- {
- return this.opSetPersPresence;
- }
-
- /**
- * The method is called by the ProtocolProvider whenever a change in the
- * registration state of the corresponding provider has occurred.
- *
- * @param evt ProviderStatusChangeEvent the event describing the status
- * change.
- */
- public void registrationStateChanged(RegistrationStateChangeEvent evt)
- {
-
- }
-
-
- /**
- * Create, execute and display a query to a dictionary (ContactDictImpl)
- *
- * @param dictContact the contact containing the database name
- * @param message the message containing the word
- */
- private void submitDictQuery(ContactDictImpl dictContact, Message message)
- {
- Message msg = this.createMessage("");
-
- String database = dictContact.getContactID();
- DictConnection conn = this.parentProvider.getConnection();
- boolean doMatch = false;
-
- String word;
-
- // Formatting the query message, if the word as one or more spaces we
- // put it between quotes to prevent errors
- word = message.getContent().replace("\"", "").trim();
- if (word.indexOf(' ') > 0)
- {
- word = "\"" + word + "\"";
- }
-
- // Try to get the definition of the work
- try
- {
- List<Definition> definitions = conn.define(database, word);
- msg = this.createMessage(retrieveDefine(definitions, word));
- }
- catch(DictException dx)
- {
- if (dx.getErrorCode() == DictReturnCode.NO_MATCH)
- { // No word found, we are going to try the match command
- doMatch = true;
- }
- else
- { // Otherwise we display the error returned by the server
- msg = this.createMessage(manageException(dx, database));
- }
- }
-
- if (doMatch)
- {
- // Trying the match command
- try
- {
- List<MatchWord> matchWords = conn.match(database, word,
- this.accountID.getStrategy());
- msg = this.createMessage(retrieveMatch(matchWords, word));
- }
- catch(DictException dx)
- {
- msg = this.createMessage(manageException(dx, database));
- }
- }
-
- // Send message
- fireMessageReceived(msg, dictContact);
- }
-
- /**
- * Generate the display of the results of the Define command
- *
- * @param data the result of the Define command
- * @param word the queried word
- * @return the formatted result
- */
- private String retrieveDefine(List<Definition> data, String word)
- {
- StringBuffer res = new StringBuffer();
- Definition def;
-
- for (int i=0; i<data.size(); i++)
- {
- def = data.get(i);
-
- if(i != 0 && data.size() > 0)
- {
- res.append("<hr>");
- }
- res.append(def.getDefinition().replaceAll("\n", "<br>"))
- .append("<div align=\"right\"><font size=\"-2\">-- From ")
- .append(def.getDictionary())
- .append("</font></div>");
- }
-
- String result = res.toString();
- result = formatResult(result, "\\\\", "<em>", "</em>");
- result = formatResult(result, "[\\[\\]]", "<cite>", "</cite>");
- result = formatResult(result, "[\\{\\}]", "<strong>", "</strong>");
- result = formatWordDefined(result, word);
-
- return result;
- }
-
- /**
- * Makes a stronger emphasis for the word defined.
- * @param result The text containing the definition of the word.
- * @param word The word defined to display with bold font. For this we
- * had the strong HTML tag.
- * @return Returns the result text with an strong emphasis of all
- * the occurences of the word defined.
- */
- private String formatWordDefined(String result, String word)
- {
- String tmpWord;
-
- tmpWord = word.toUpperCase();
- result = result.replaceAll("\\b" + tmpWord + "\\b", "<strong>" + tmpWord + "</strong>");
- tmpWord = word.toLowerCase();
- result = result.replaceAll("\\b" + tmpWord + "\\b", "<strong>" + tmpWord + "</strong>");
- if(tmpWord.length() > 1)
- {
- tmpWord = tmpWord.substring(0, 1).toUpperCase() + tmpWord.substring(1);
- result = result.replaceAll("\\b" + tmpWord + "\\b", "<strong>" + tmpWord + "</strong>");
- }
-
- return result;
- }
-
- /**
- * Remplaces special characters into HTML tags to make some emphasis.
- * @param result The text containing the definition of the word.
- * @param regex The special character to replace with HTML tags.
- * @param startTag The start HTML tag to use.
- * @param endTag The end HTML tag to use.
- * @return The result with all special characters replaced by HTML
- * tags.
- */
- private String formatResult(String result, String regex, String startTag, String endTag)
- {
- String[] tmp = result.split(regex);
- String res = "";
-
- for(int i = 0; i < (tmp.length - 1); i += 2)
- {
- res += tmp[i] + startTag + tmp[i+1] + endTag;
- }
- if((tmp.length % 2) != 0)
- {
- res += tmp[tmp.length - 1];
- }
- return res;
- }
-
- /**
- * Generate the display of the results of the Match command
- *
- * @param data the result of the Match command
- * @param word the queried word
- * @return the formatted result
- */
- private String retrieveMatch(List<MatchWord> data, String word)
- {
- StringBuffer result = new StringBuffer();
- boolean isStart = true;
-
- result.append(DictActivator.getResources()
- .getI18NString("plugin.dictaccregwizz.MATCH_RESULT", new String[] {word}));
-
- for (int i=0; i<data.size(); i++)
- {
- if (isStart)
- isStart = false;
- else
- result.append(", ");
-
- result.append(data.get(i).getWord());
- }
-
- return result.toString();
- }
-
- /**
- * Manages the return exception of a dict query.
- *
- * @param dix The exception returned by the adapter
- * @param database The dictionary used
- * @return Exception message
- */
- private String manageException(DictException dix, String database)
- {
- int errorCode = dix.getErrorCode();
-
- // We change the text only for exception 550 (invalid dictionary) and 551 (invalid strategy)
- if (errorCode == DictReturnCode.INVALID_DATABASE)
- {
- return DictActivator.getResources()
- .getI18NString("plugin.dictaccregwizz.INVALID_DATABASE", new String[] {database});
- }
- else if (errorCode == DictReturnCode.INVALID_STRATEGY)
- {
- return DictActivator.getResources()
- .getI18NString("plugin.dictaccregwizz.INVALID_STRATEGY");
- }
- else if (errorCode == DictReturnCode.NO_MATCH)
- {
- return DictActivator.getResources()
- .getI18NString("plugin.dictaccregwizz.NO_MATCH");
- }
-
- return dix.getMessage();
- }
-
- /**
- * Sends the <tt>message</tt> to the destination indicated by the
- * <tt>to</tt>. Resources are not supported by this operation set
- * implementation.
- *
- * @param to the <tt>Contact</tt> to send <tt>message</tt> to
- * @param toResource the resource to which the message should be send
- * @param message the <tt>Message</tt> to send.
- * @throws java.lang.IllegalStateException if the underlying ICQ stack is
- * not registered and initialized.
- * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an
- * instance belonging to the underlying implementation.
- */
- @Override
- public void sendInstantMessage( Contact to,
- ContactResource toResource,
- Message message)
- throws IllegalStateException,
- IllegalArgumentException
- {
- sendInstantMessage(to, message);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java
deleted file mode 100644
index 8184fe9..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/OperationSetPersistentPresenceDictImpl.java
+++ /dev/null
@@ -1,988 +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.dict;
-
-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 Dict 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 ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class OperationSetPersistentPresenceDictImpl
- extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceDictImpl>
-{
- private static final Logger logger =
- Logger.getLogger(OperationSetPersistentPresenceDictImpl.class);
-
- /**
- * The root of the dict contact list.
- */
- private ContactGroupDictImpl contactListRoot = null;
-
- /**
- * The currently active status message.
- */
- private String statusMessage = "Default Status Message";
-
- /**
- * Our presence status.
- */
- private PresenceStatus presenceStatus = DictStatusEnum.ONLINE;
-
- /**
- * 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 ProtocolProviderServiceDictImpl instance that
- * created us.
- */
- public OperationSetPersistentPresenceDictImpl(
- ProtocolProviderServiceDictImpl provider)
- {
- super(provider);
-
- contactListRoot = new ContactGroupDictImpl("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)
- {
- ContactGroupDictImpl newGroup
- = new ContactGroupDictImpl(groupName, parentProvider);
-
- ((ContactGroupDictImpl)parent).addSubgroup(newGroup);
-
- this.fireServerStoredGroupEvent(
- newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT);
- }
-
- /**
- * A Dict Provider method to use for fast filling of a contact list.
- *
- * @param contactGroup the group to add
- */
- public void addDictGroup(ContactGroupDictImpl contactGroup)
- {
- contactListRoot.addSubgroup(contactGroup);
- }
-
- /**
- * A Dict 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 addDictGroupAndFireEvent(
- ContactGroupDictImpl parent
- , ContactGroupDictImpl 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 the protocol specific contact instance representing the local
- * user.
- *
- * @return the Contact (address, phone number, or uin) that the Provider
- * implementation is communicating on behalf of.
- */
- public Contact getLocalContact()
- {
- return null;
- }
-
- /**
- * 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 DictStatusEnum.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)
- {
- ContactDictImpl dictContact
- = (ContactDictImpl)contactToMove;
-
- ContactGroupDictImpl parentDictGroup
- = findContactParent(dictContact);
-
- parentDictGroup.removeContact(dictContact);
-
- //if this is a volatile contact then we haven't really subscribed to
- //them so we'd need to do so here
- if(!dictContact.isPersistent())
- {
- //first tell everyone that the volatile contact was removed
- fireSubscriptionEvent(dictContact
- , parentDictGroup
- , SubscriptionEvent.SUBSCRIPTION_REMOVED);
-
- try
- {
- //now subscribe
- this.subscribe(newParent, contactToMove.getAddress());
-
- //now tell everyone that we've added the contact
- fireSubscriptionEvent(dictContact
- , newParent
- , SubscriptionEvent.SUBSCRIPTION_CREATED);
- }
- catch (Exception ex)
- {
- logger.error("Failed to move contact "
- + dictContact.getAddress()
- , ex);
- }
- }
- else
- {
- ( (ContactGroupDictImpl) newParent)
- .addContact(dictContact);
-
- fireSubscriptionMovedEvent(contactToMove
- , parentDictGroup
- , 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;
-
- this.fireProviderStatusChangeEvent(oldPresenceStatus);
-
- //since we are not a real protocol, we set the contact presence status
- //ourselves and make them have the same status as ours.
- changePresenceStatusForAllContacts( getServerStoredContactListRoot()
- , getPresenceStatus());
-
- //now check whether we are in someone else's contact list and modify
- //our status there
- List<Contact> contacts = findContactsPointingToUs();
-
- Iterator<Contact> contactsIter = contacts.iterator();
- while (contactsIter.hasNext())
- {
- ContactDictImpl contact
- = (ContactDictImpl) contactsIter.next();
-
- PresenceStatus oldStatus = contact.getPresenceStatus();
- contact.setPresenceStatus(status);
- contact.getParentPresenceOperationSet()
- .fireContactPresenceStatusChangeEvent(
- contact
- , contact.getParentContactGroup()
- , oldStatus);
-
- }
- }
-
-
-
- /**
- * 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>ContactDictImpl</tt> whose status we'd like
- * to set.
- * @param newStatus the new status we'd like to set to <tt>contact</tt>.
- */
- private void changePresenceStatusForContact(
- ContactDictImpl 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.
- */
- private void changePresenceStatusForAllContacts(ContactGroup parent,
- PresenceStatus newStatus)
- {
- //first set the status for contacts in this group
- Iterator<Contact> childContacts = parent.contacts();
-
- while(childContacts.hasNext())
- {
- ContactDictImpl contact
- = (ContactDictImpl)childContacts.next();
-
- if(findProviderForDictUserID(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 dictGroup or null
- * if no parent was found.
- * @param dictGroup the group whose parent we're looking for.
- * @return the ContactGroupDictImpl instance that dictGroup
- * belongs to or null if no parent was found.
- */
- public ContactGroupDictImpl findGroupParent(
- ContactGroupDictImpl dictGroup)
- {
- return contactListRoot.findGroupParent(dictGroup);
- }
-
- /**
- * Returns the group that is parent of the specified dictContact or
- * null if no parent was found.
- * @param dictContact the contact whose parent we're looking for.
- * @return the ContactGroupDictImpl instance that dictContact
- * belongs to or null if no parent was found.
- */
- public ContactGroupDictImpl findContactParent(
- ContactDictImpl dictContact)
- {
- return (ContactGroupDictImpl)dictContact
- .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
- {
- ContactGroupDictImpl dictGroup
- = (ContactGroupDictImpl)group;
-
- ContactGroupDictImpl parent = findGroupParent(dictGroup);
-
- if(parent == null){
- throw new IllegalArgumentException(
- "group " + group
- + " does not seem to belong to this protocol's contact list.");
- }
-
- parent.removeSubGroup(dictGroup);
-
- this.fireServerStoredGroupEvent(
- dictGroup, 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)
- {
- ((ContactGroupDictImpl)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
- {
- ContactDictImpl contact = new ContactDictImpl(
- contactIdentifier
- , parentProvider);
-
- ((ContactGroupDictImpl)parent).addContact(contact);
-
- fireSubscriptionEvent(contact,
- parent,
- SubscriptionEvent.SUBSCRIPTION_CREATED);
-
- //notify presence listeners for the status change.
- fireContactPresenceStatusChangeEvent(contact
- , parent
- , DictStatusEnum.ONLINE);
- }
-
-
- /**
- * Depending on whether <tt>contact</tt> corresponds to another protocol
- * provider installed in sip-communicator, this method would either deliver
- * it to that provider or simulate a corresponding request from the
- * destination contact and make return a response after it has received
- * one If the destination contact matches us, then we'll ask the user to
- * act upon the request, and return the response.
- *
- * @param request the authorization request that we'd like to deliver to the
- * destination <tt>contact</tt>.
- * @param contact the <tt>Contact</tt> to notify
- *
- * @return the <tt>AuthorizationResponse</tt> that has been given or
- * generated in response to <tt>request</tt>.
- */
- private AuthorizationResponse deliverAuthorizationRequest(
- AuthorizationRequest request,
- Contact contact)
- {
- String userID = contact.getAddress();
-
- //if the user id is our own id, then this request is being routed to us
- //from another instance of the dict provider.
- if (userID.equals(this.parentProvider.getAccountID().getUserID()))
- {
- //check who is the provider sending the message
- String sourceUserID = contact.getProtocolProvider()
- .getAccountID().getUserID();
-
- //check whether they are in our contact list
- Contact from = findContactByID(sourceUserID);
-
- //and if not - add them there as volatile.
- if (from == null)
- {
- from = createVolatileContact(sourceUserID);
- }
-
- //and now handle the request.
- return authorizationHandler.processAuthorisationRequest(
- request, from);
- }
- else
- {
- //if userID is not our own, try a check whether another provider
- //has that id and if yes - deliver the request to them.
- ProtocolProviderServiceDictImpl dictProvider
- = this.findProviderForDictUserID(userID);
- if (dictProvider != null)
- {
- OperationSetPersistentPresenceDictImpl opSetPersPresence
- = (OperationSetPersistentPresenceDictImpl)
- dictProvider.getOperationSet(
- OperationSetPersistentPresence.class);
- return opSetPersPresence
- .deliverAuthorizationRequest(request, contact);
- }
- else
- {
- //if we got here then "to" is simply someone in our contact
- //list so let's just simulate a reciproce request and generate
- //a response accordingly.
-
- //pretend that the remote contact is asking for authorization
- authorizationHandler.processAuthorisationRequest(
- request, contact);
-
- //and now pretend that the remote contact has granted us
- //authorization
- return new AuthorizationResponse(AuthorizationResponse.ACCEPT
- , "You are welcome!");
- }
- }
- }
-
- /**
- * 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
- {
- ContactGroupDictImpl parentGroup
- = (ContactGroupDictImpl)((ContactDictImpl)contact)
- .getParentContactGroup();
-
- parentGroup.removeContact((ContactDictImpl)contact);
-
- fireSubscriptionEvent(contact,
- ((ContactDictImpl)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)
- {
-
- ContactDictImpl contact = new ContactDictImpl(
- address
- , parentProvider);
- contact.setResolved(false);
-
- ( (ContactGroupDictImpl) parent).addContact(contact);
-
- fireSubscriptionEvent(contact,
- parent,
- SubscriptionEvent.SUBSCRIPTION_CREATED);
-
- //since we don't have any server, we'll simply resolve the contact
- //ourselves as if we've just received an event from the server telling
- //us that it has been resolved.
- contact.setResolved(true);
- fireSubscriptionEvent(contact, parent, SubscriptionEvent.SUBSCRIPTION_RESOLVED);
-
- //since we are not a real protocol, we set the contact presence status
- //ourselves
- changePresenceStatusForContact( contact, getPresenceStatus());
-
- return contact;
- }
-
- /**
- * Looks for a dict protocol provider registered for a user id matching
- * <tt>dictUserID</tt>.
- *
- * @param dictUserID the ID of the Dict user whose corresponding
- * protocol provider we'd like to find.
- * @return ProtocolProviderServiceDictImpl a dict protocol
- * provider registered for a user with id <tt>dictUserID</tt> or null
- * if there is no such protocol provider.
- */
- public ProtocolProviderServiceDictImpl
- findProviderForDictUserID(String dictUserID)
- {
- BundleContext bc = DictActivator.getBundleContext();
-
- String osgiQuery = "(&"
- + "(" + ProtocolProviderFactory.PROTOCOL
- + "=Dict)"
- + "(" + ProtocolProviderFactory.USER_ID
- + "=" + dictUserID + ")"
- + ")";
-
- 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 (ProtocolProviderServiceDictImpl)bc.getService(refs[0]);
- }
-
- return null;
- }
-
- /**
- * Looks for dict protocol providers that have added us to their
- * contact list and returns list of all contacts representing us in these
- * providers.
- *
- * @return a list of all contacts in other providers' contact lists that
- * point to us.
- */
- public List<Contact> findContactsPointingToUs()
- {
- List<Contact> contacts = new LinkedList<Contact>();
- BundleContext bc = DictActivator.getBundleContext();
-
- String osgiQuery =
- "(" + ProtocolProviderFactory.PROTOCOL
- + "=Dict)";
-
- 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);
- }
-
- for (int i =0; refs != null && i < refs.length; i++)
- {
- ProtocolProviderServiceDictImpl gibProvider
- = (ProtocolProviderServiceDictImpl)bc.getService(refs[i]);
-
- OperationSetPersistentPresenceDictImpl opSetPersPresence
- = (OperationSetPersistentPresenceDictImpl)gibProvider
- .getOperationSet(OperationSetPersistentPresence.class);
-
- Contact contact = opSetPersPresence.findContactByID(
- parentProvider.getAccountID().getUserID());
-
- if (contact != null)
- contacts.add(contact);
- }
-
- return contacts;
- }
-
-
- /**
- * 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)
- {
- ContactGroupDictImpl newGroup
- = new ContactGroupDictImpl(
- ContactGroupDictImpl.createNameFromUID(groupUID)
- , parentProvider);
- newGroup.setResolved(false);
-
- //if parent is null then we're adding under root.
- if(parentGroup == null)
- parentGroup = getServerStoredContactListRoot();
-
- ((ContactGroupDictImpl)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 dict 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 (! evt.getNewState().equals(RegistrationState.UNREGISTERED)
- && !evt.getNewState().equals(RegistrationState.AUTHENTICATION_FAILED)
- && !evt.getNewState().equals(RegistrationState.CONNECTION_FAILED))
- {
- return;
- }
-
- //send event notifications saying that all our buddies are
- //offline. The icq 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())
- {
- ContactGroupDictImpl group
- = (ContactGroupDictImpl) groupsIter.next();
-
- Iterator<Contact> contactsIter = group.contacts();
-
- while (contactsIter.hasNext())
- {
- ContactDictImpl contact
- = (ContactDictImpl) contactsIter.next();
-
- PresenceStatus oldContactStatus
- = contact.getPresenceStatus();
-
- if (!oldContactStatus.isOnline())
- continue;
-
- contact.setPresenceStatus(DictStatusEnum.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.
- */
- private ContactGroupDictImpl getNonPersistentGroup()
- {
- for (int i = 0
- ; i < getServerStoredContactListRoot().countSubgroups()
- ; i++)
- {
- ContactGroupDictImpl gr =
- (ContactGroupDictImpl)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.
- *
- * @param contactAddress the address of the volatile contact we'd like to
- * create.
- * @return the newly created volatile contact.
- */
- public ContactDictImpl createVolatileContact(String contactAddress)
- {
- //First create the new volatile contact;
- ContactDictImpl newVolatileContact
- = new ContactDictImpl(contactAddress
- , this.parentProvider);
- newVolatileContact.setPersistent(false);
-
-
- //Check whether a volatile group already exists and if not create
- //one
- ContactGroupDictImpl theVolatileGroup = getNonPersistentGroup();
-
-
- //if the parent volatile group is null then we create it
- if (theVolatileGroup == null)
- {
- theVolatileGroup = new ContactGroupDictImpl(
- DictActivator.getResources().getI18NString(
- "service.gui.NOT_IN_CONTACT_LIST_GROUP_NAME")
- , 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;
- }
-
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolIconDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolIconDictImpl.java
deleted file mode 100644
index 4f09f22..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolIconDictImpl.java
+++ /dev/null
@@ -1,133 +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.dict;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * Represents the Dict protocol icon. Implements the <tt>ProtocolIcon</tt>
- * interface in order to provide a Dict logo image in two different sizes.
- *
- * @author ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class ProtocolIconDictImpl
- implements ProtocolIcon
-{
- /**
- * 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,
- DictActivator.getResources()
- .getImageInBytes("service.protocol.dict.DICT_16x16"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_32x32,
- DictActivator.getResources()
- .getImageInBytes("service.protocol.dict.DICT_32x32"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_48x48,
- DictActivator.getResources()
- .getImageInBytes("service.protocol.dict.DICT_48x48"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_64x64,
- DictActivator.getResources()
- .getImageInBytes("service.protocol.dict.DICT_64x64"));
- }
-
- /**
- * A hash table containing the path to the protocol icon in different sizes.
- */
- private static Hashtable<String, String> iconPathsTable
- = new Hashtable<String, String>();
- static
- {
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_16x16,
- DictActivator.getResources()
- .getImagePath("service.protocol.dict.DICT_16x16"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_32x32,
- DictActivator.getResources()
- .getImagePath("service.protocol.dict.DICT_32x32"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_48x48,
- DictActivator.getResources()
- .getImagePath("service.protocol.dict.DICT_48x48"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_64x64,
- DictActivator.getResources()
- .getImagePath("service.protocol.dict.DICT_64x64"));
- }
-
- /**
- * Implements the <tt>ProtocolIcon.getSupportedSizes()</tt> method. Returns
- * an iterator to a set containing the supported icon sizes.
- * @return Returns an iterator to a set containing the supported icon sizes
- */
- public Iterator<String> getSupportedSizes()
- {
- return iconsTable.keySet().iterator();
- }
-
- /**
- * Returns TRUE if an icon with the given size is supported, FALSE otherwise.
- * @param iconSize The size of the icon, that we want to know if it is
- * supported.
- * @return Returns true if the 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 Returns a byte[] containing the pixels of the icon for the given
- * size.
- */
- 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 Returns the icon image used to represent the protocol connecting
- * state.
- */
- public byte[] getConnectingIcon()
- {
- return iconsTable.get(ProtocolIcon.ICON_SIZE_16x16);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java
deleted file mode 100644
index d7eaf30..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderFactoryDictImpl.java
+++ /dev/null
@@ -1,200 +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.dict;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.contactlist.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-import org.osgi.framework.*;
-
-/**
- * The Dict protocol provider factory creates instances of the Dict
- * protocol provider service. One Service instance corresponds to one account.
- *
- * @author ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class ProtocolProviderFactoryDictImpl
- extends ProtocolProviderFactory
-{
- private static final Logger logger
- = Logger.getLogger(ProtocolProviderFactoryDictImpl.class);
-
- /**
- * Creates an instance of the ProtocolProviderFactoryDictImpl.
- */
- public ProtocolProviderFactoryDictImpl()
- {
- super(DictActivator.getBundleContext(), ProtocolNames.DICT);
- }
-
- /**
- * Initializaed and creates an account corresponding to the specified
- * accountProperties and registers the resulting ProtocolProvider in the
- * <tt>context</tt> BundleContext parameter.
- *
- * @param userIDStr The 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 = DictActivator.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 DictAccountID(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 acces
- //the configuration service and check for a stored password.
- this.storeAccount(accountID, false);
-
- accountID = loadAccount(accountProperties);
-
- // Creates the dict contact group.
- this.createGroup();
- // Creates the default conatct for this dict server.
- this.createDefaultContact(accountID);
-
- return accountID;
- }
-
- @Override
- protected AccountID createAccountID(String userID, Map<String, String> accountProperties)
- {
- return new DictAccountID(userID, accountProperties);
- }
-
- @Override
- protected ProtocolProviderService createService(String userID,
- AccountID accountID)
- {
- ProtocolProviderServiceDictImpl service =
- new ProtocolProviderServiceDictImpl();
-
- service.initialize(userID, accountID);
- return service;
- }
-
- /**
- * Creates a group for the dict contacts
- */
- private void createGroup()
- {
- // Get MetaContactListService
- BundleContext bundleContext = getBundleContext();
- ServiceReference<MetaContactListService> mfcServiceRef
- = bundleContext.getServiceReference(MetaContactListService.class);
-
- MetaContactListService mcl = bundleContext.getService(mfcServiceRef);
-
- try
- {
- String groupName = DictActivator.getResources()
- .getI18NString("service.protocol.DICTIONARIES");
-
- mcl.createMetaContactGroup(mcl.getRoot(), groupName);
- }
- catch (MetaContactListException ex)
- {
- int errorCode = ex.getErrorCode();
- if (errorCode != MetaContactListException.CODE_GROUP_ALREADY_EXISTS_ERROR)
- {
- logger.error(ex);
- }
- }
- }
-
- /**
- * Creates a default contact for the new DICT server.
- * @param accountID The accountID of the dict protocol provider for which we
- * want to add a default contact.
- */
- private void createDefaultContact(AccountID accountID)
- {
- // Gets the MetaContactListService.
- BundleContext bundleContext = getBundleContext();
- ServiceReference<MetaContactListService> mfcServiceRef
- = bundleContext.getServiceReference(MetaContactListService.class);
- MetaContactListService mcl = bundleContext.getService(mfcServiceRef);
-
- // Gets the ProtocolProviderService.
- ServiceReference<ProtocolProviderService> serRef
- = getProviderForAccount(accountID);
- ProtocolProviderService protocolProvider
- = DictActivator.getBundleContext().getService(serRef);
-
- // Gets group name
- String groupName = DictActivator.getResources()
- .getI18NString("service.protocol.DICTIONARIES");
-
- // Gets contact name
- String contactName = DictActivator.getResources()
- .getI18NString("plugin.dictaccregwizz.ANY_DICTIONARY_FORM",
- new String[] {accountID.getUserID()});
-
- // Gets the MetaContactGroup for the "dictionaries" group.
- MetaContactGroup group = mcl.getRoot().getMetaContactSubgroup(groupName);
-
- // Sets the default contact identifier to "*" corresponding to "all the
- // dictionaries" available on the server (cf. RFC-2229).
- String dict_uin = "*";
- // Create the default contact.
- mcl.createMetaContact(protocolProvider, group, dict_uin);
- // Rename the default contact.
- mcl.renameMetaContact(
- group.getMetaContact(protocolProvider, dict_uin),
- contactName);
- }
-
- @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/dict/ProtocolProviderServiceDictImpl.java b/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java
deleted file mode 100644
index aa9ef50..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/ProtocolProviderServiceDictImpl.java
+++ /dev/null
@@ -1,364 +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.dict;
-
-import net.java.dict4j.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.service.version.*;
-import org.osgi.framework.*;
-
-/**
- * A Dict implementation of the ProtocolProviderService.
- *
- * @author ROTH Damien
- * @author LITZELMANN Cedric
- */
-public class ProtocolProviderServiceDictImpl
- extends AbstractProtocolProviderService
-{
- private static final Logger logger
- = Logger.getLogger(ProtocolProviderServiceDictImpl.class);
-
- /**
- * The name of this protocol.
- */
- public static final String DICT_PROTOCOL_NAME = "Dict";
-
- /**
- * The id of the account that this protocol provider represents.
- */
- private DictAccountID accountID = null;
-
- /**
- * We use this to lock access to initialization.
- */
- private Object initializationLock = new Object();
-
- /**
- * Indicates whether or not the provider is initialized and ready for use.
- */
- private boolean isInitialized = false;
-
- /**
- * The logo corresponding to the gibberish protocol.
- */
- private ProtocolIconDictImpl dictIcon = new ProtocolIconDictImpl();
-
- /**
- * 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 <tt>DictConnection</tt> opened by this provider
- */
- private DictConnection dictConnection;
-
- /**
- * The default constructor for the Dict protocol provider.
- */
- public ProtocolProviderServiceDictImpl()
- {
- if (logger.isTraceEnabled())
- logger.trace("Creating a Dict provider.");
- }
-
- /**
- * 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 gibberish 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 = (DictAccountID) accountID;
-
- this.dictConnection = new DictConnection(this.accountID.getHost(),
- this.accountID.getPort());
- this.dictConnection.setClientName(getSCVersion());
-
- //initialize the presence operationset
- OperationSetPersistentPresenceDictImpl persistentPresence =
- new OperationSetPersistentPresenceDictImpl(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 OperationSetBasicInstantMessagingDictImpl(
- this,
- persistentPresence));
-
- //initialize the typing notifications operation set
- /*OperationSetTypingNotifications typingNotifications =
- new OperationSetTypingNotificationsDictImpl(
- this, persistentPresence);
-
- supportedOperationSets.put(
- OperationSetTypingNotifications.class.getName(),
- typingNotifications);
- */
- isInitialized = true;
- }
- }
-
- /**
- * Returns the <tt>DictConnection</tt> opened by this provider
- * @return the <tt>DictConnection</tt> opened by this provider
- */
- public DictConnection getConnection()
- {
- return this.dictConnection;
- }
-
- /**
- * 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 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 DICT_PROTOCOL_NAME;
- }
-
- /**
- * Returns the dict protocol icon.
- * @return the dict protocol icon
- */
- public ProtocolIcon getProtocolIcon()
- {
- return this.dictIcon;
- }
-
- /**
- * 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
- {
- // Try to connect to the server
- boolean connected = connect();
-
- if (connected)
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.REGISTERED,
- RegistrationStateChangeEvent.REASON_USER_REQUEST,
- null);
- currentRegistrationState = RegistrationState.REGISTERED;
- }
- else
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.CONNECTION_FAILED,
- RegistrationStateChangeEvent.REASON_SERVER_NOT_FOUND,
- null);
- currentRegistrationState = RegistrationState.UNREGISTERED;
- }
- }
-
- /**
- * Checks if the connection to the dict server is open
- * @return TRUE if the connection is open - FALSE otherwise
- */
- private boolean connect()
- {
- if (this.dictConnection.isConnected())
- {
- return true;
- }
-
- try
- {
- return this.dictConnection.isAvailable();
- }
- catch (DictException dx)
- {
- if (logger.isInfoEnabled())
- logger.info(dx);
- }
-
- return false;
- }
-
- /**
- * 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 Dict Protocol Provider for account "
- + this.accountID.getUserID());
-
- closeConnection();
-
- 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
- {
- closeConnection();
-
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.UNREGISTERED,
- RegistrationStateChangeEvent.REASON_USER_REQUEST,
- null);
- }
-
- /**
- * DICT has no support for secure transport.
- */
- 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;
- }
-
- /**
- * Close the connection to the server
- */
- private void closeConnection()
- {
- try
- {
- this.dictConnection.close();
- }
- catch (DictException dx)
- {
- if (logger.isInfoEnabled())
- logger.info(dx);
- }
- }
-
- /**
- * Returns the current version of SIP-Communicator
- * @return the current version of SIP-Communicator
- */
- private String getSCVersion()
- {
- BundleContext bc = DictActivator.getBundleContext();
- ServiceReference vsr = bc.getServiceReference(VersionService.class.getName());
-
- VersionService vs = (VersionService) bc.getService(vsr);
- return vs.getCurrentVersion().toString();
-
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/dict/dict.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/dict/dict.provider.manifest.mf
deleted file mode 100644
index d764657..0000000
--- a/src/net/java/sip/communicator/impl/protocol/dict/dict.provider.manifest.mf
+++ /dev/null
@@ -1,15 +0,0 @@
-Bundle-Activator: net.java.sip.communicator.impl.protocol.dict.DictActivator
-Bundle-Name: Dict Protocol Provider
-Bundle-Description: A bundle providing support for the Dict protocol.
-Bundle-Vendor: jitsi.org
-Bundle-Version: 1.0.0
-Bundle-SymbolicName: net.java.sip.communicator.protocol.dict
-Import-Package: org.osgi.framework,
- org.jitsi.service.version,
- net.java.sip.communicator.service.contactlist,
- 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,
-Export-Package: net.java.dict4j
diff --git a/src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java b/src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java
index 2a95493..8528267 100644
--- a/src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/gibberish/ContactGroupGibberishImpl.java
@@ -577,4 +577,29 @@ public class ContactGroupGibberishImpl
return true;
}
+
+ @Override
+ public int hashCode()
+ {
+ List<Object> objects = new ArrayList<Object>();
+ objects.add(getGroupName());
+ objects.add(getUID());
+ objects.add(countContacts());
+ objects.add(countSubgroups());
+
+ //traverse child contacts
+ for (Contact c : contacts)
+ {
+ objects.add(c.getAddress());
+ }
+
+
+ //traverse subgroups
+ for (ContactGroup g : subGroups)
+ {
+ objects.add(g.getGroupName());
+ }
+
+ return Objects.hash(objects.toArray());
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf
index da47277..959ae6d 100644
--- a/src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf
+++ b/src/net/java/sip/communicator/impl/protocol/icq/icq.provider.manifest.mf
@@ -13,5 +13,4 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.dns,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
- net.java.sip.communicator.service.protocol.aimconstants,
net.java.sip.communicator.service.protocol.event
diff --git a/src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java b/src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java
index 6b09978..7a1c2b3 100644
--- a/src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/irc/OperationSetMultiUserChatIrcImpl.java
@@ -275,7 +275,7 @@ public class OperationSetMultiUserChatIrcImpl
*/
protected ChatRoomIrcImpl getChatRoom(final String chatRoomName)
{
- return (ChatRoomIrcImpl) this.chatRoomCache.get(chatRoomName);
+ return this.chatRoomCache.get(chatRoomName);
}
/**
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java b/src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java
index c955100..b62d01a 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/AnonymousLoginStrategy.java
@@ -70,7 +70,7 @@ public class AnonymousLoginStrategy
}
@Override
- public boolean login(XMPPConnection connection, String userName,
+ public boolean login(Connection connection, String userName,
String resource)
throws XMPPException
{
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
index e23481a..3987ea8 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
@@ -311,7 +311,7 @@ public class CallJabberImpl
contentRequest.addChannel(remoteChannelRequest);
}
- XMPPConnection connection = protocolProvider.getConnection();
+ Connection connection = protocolProvider.getConnection();
PacketCollector packetCollector
= connection.createPacketCollector(
new PacketIDFilter(conferenceRequest.getPacketID()));
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
index 64eb5fa..c257202 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerJabberImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,1647 +15,1644 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.lang.reflect.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.SendersEnum;
-import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.service.neomedia.*;
-import org.jivesoftware.smack.*;
-import org.jivesoftware.smack.filter.*;
-import org.jivesoftware.smack.packet.*;
-import org.jivesoftware.smack.util.*;
-import org.jivesoftware.smackx.packet.*;
-
-/**
- * Implements a Jabber <tt>CallPeer</tt>.
- *
- * @author Emil Ivov
- * @author Lyubomir Marinov
- * @author Boris Grozev
- */
-public class CallPeerJabberImpl
- extends AbstractCallPeerJabberGTalkImpl
- <CallJabberImpl, CallPeerMediaHandlerJabberImpl, JingleIQ>
-{
- /**
- * The <tt>Logger</tt> used by the <tt>CallPeerJabberImpl</tt> class and its
- * instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(CallPeerJabberImpl.class);
-
- /**
- * If the call is cancelled before session-initiate is sent.
- */
- private boolean cancelled = false;
-
- /**
- * Synchronization object for candidates available.
- */
- private final Object candSyncRoot = new Object();
-
- /**
- * If the content-add does not contains candidates.
- */
- private boolean contentAddWithNoCands = false;
-
- /**
- * If we have processed the session initiate.
- */
- private boolean sessionInitiateProcessed = false;
-
- /**
- * Synchronization object. Synchronization object? Wow, who would have
- * thought! ;) Would be great to have a word on what we are syncing with it
- */
- private final Object sessionInitiateSyncRoot = new Object();
-
- /**
- * Synchronization object for SID.
- */
- private final Object sidSyncRoot = new Object();
-
- /**
- * The current value of the 'senders' field of the audio content in the
- * Jingle session with this <tt>CallPeer</tt>.
- * <tt>null</tt> should be interpreted as 'both', which is the default in
- * Jingle if the XML attribute is missing.
- */
- private SendersEnum audioSenders = SendersEnum.none;
-
- /**
- * The current value of the 'senders' field of the video content in the
- * Jingle session with this <tt>CallPeer</tt>.
- * <tt>null</tt> should be interpreted as 'both', which is the default in
- * Jingle if the XML attribute is missing.
- */
- private SendersEnum videoSenders = SendersEnum.none;
-
- /**
- * Creates a new call peer with address <tt>peerAddress</tt>.
- *
- * @param peerAddress the Jabber address of the new call peer.
- * @param owningCall the call that contains this call peer.
- */
- public CallPeerJabberImpl(String peerAddress,
- CallJabberImpl owningCall)
- {
- super(peerAddress, owningCall);
-
- setMediaHandler(new CallPeerMediaHandlerJabberImpl(this));
- }
-
- /**
- * Creates a new call peer with address <tt>peerAddress</tt>.
- *
- * @param peerAddress the Jabber address of the new call peer.
- * @param owningCall the call that contains this call peer.
- * @param sessionIQ The session-initiate <tt>JingleIQ</tt> which was
- * received from <tt>peerAddress</tt> and caused the creation of this
- * <tt>CallPeerJabberImpl</tt>
- */
- public CallPeerJabberImpl(String peerAddress,
- CallJabberImpl owningCall,
- JingleIQ sessionIQ)
- {
- this(peerAddress, owningCall);
- this.sessionInitIQ = sessionIQ;
- }
-
- /**
- * Send a session-accept <tt>JingleIQ</tt> to this <tt>CallPeer</tt>
- * @throws OperationFailedException if we fail to create or send the
- * response.
- */
- public synchronized void answer()
- throws OperationFailedException
- {
- Iterable<ContentPacketExtension> answer;
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- try
- {
- mediaHandler
- .getTransportManager()
- .wrapupConnectivityEstablishment();
- answer = mediaHandler.generateSessionAccept();
- for (ContentPacketExtension c : answer)
- setSenders(getMediaType(c), c.getSenders());
- }
- catch(Exception exc)
- {
- logger.info("Failed to answer an incoming call", exc);
-
- //send an error response
- String reasonText = "Error: " + exc.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- Reason.FAILED_APPLICATION,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- JingleIQ response
- = JinglePacketFactory.createSessionAccept(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- getSID(),
- answer);
-
- //send the packet first and start the stream later in case the media
- //relay needs to see it before letting hole punching techniques through.
- getProtocolProvider().getConnection().sendPacket(response);
-
- try
- {
- mediaHandler.start();
- }
- catch(UndeclaredThrowableException e)
- {
- Throwable exc = e.getUndeclaredThrowable();
-
- logger.info("Failed to establish a connection", exc);
-
- //send an error response
- String reasonText = "Error: " + exc.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- Reason.GENERAL_ERROR,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- //tell everyone we are connected so that the audio notifications would
- //stop
- setState(CallPeerState.CONNECTED);
- }
-
- /**
- * Returns the session ID of the Jingle session associated with this call.
- *
- * @return the session ID of the Jingle session associated with this call.
- */
- @Override
- public String getSID()
- {
- return sessionInitIQ != null ? sessionInitIQ.getSID() : null;
- }
-
- /**
- * Returns the IQ ID of the Jingle session-initiate packet associated with
- * this call.
- *
- * @return the IQ ID of the Jingle session-initiate packet associated with
- * this call.
- */
- public JingleIQ getSessionIQ()
- {
- return sessionInitIQ;
- }
-
- /**
- * Ends the call with 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)
- {
- CallPeerState prevPeerState = getState();
-
- // do nothing if the call is already ended
- if (CallPeerState.DISCONNECTED.equals(prevPeerState)
- || CallPeerState.FAILED.equals(prevPeerState))
- {
- if (logger.isDebugEnabled())
- logger.debug("Ignoring a request to hangup a call peer "
- + "that is already DISCONNECTED");
- return;
- }
-
- setState(
- failed ? CallPeerState.FAILED : CallPeerState.DISCONNECTED,
- reasonText);
-
- JingleIQ responseIQ = null;
-
- if (prevPeerState.equals(CallPeerState.CONNECTED)
- || CallPeerState.isOnHold(prevPeerState))
- {
- responseIQ = JinglePacketFactory.createBye(
- getProtocolProvider().getOurJID(), peerJID, getSID());
- }
- else if (CallPeerState.CONNECTING.equals(prevPeerState)
- || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState)
- || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState))
- {
- String jingleSID = getSID();
-
- if(jingleSID == null)
- {
- synchronized(sidSyncRoot)
- {
- // we cancelled the call too early because the jingleSID
- // is null (i.e. the session-initiate has not been created)
- // and no need to send the session-terminate
- cancelled = true;
- return;
- }
- }
-
- responseIQ = JinglePacketFactory.createCancel(
- getProtocolProvider().getOurJID(), peerJID, getSID());
- }
- else if (prevPeerState.equals(CallPeerState.INCOMING_CALL))
- {
- responseIQ = JinglePacketFactory.createBusy(
- getProtocolProvider().getOurJID(), peerJID, getSID());
- }
- 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)
- {
- reason.setOtherExtension(reasonOtherExtension);
- }
- else if(reasonOtherExtension instanceof ReasonPacketExtension)
- {
- responseIQ.setReason(
- (ReasonPacketExtension)reasonOtherExtension);
- }
- }
-
- getProtocolProvider().getConnection().sendPacket(responseIQ);
- }
- }
-
- /**
- * Creates and sends a session-initiate {@link JingleIQ}.
- *
- * @param sessionInitiateExtensions a collection of additional and optional
- * <tt>PacketExtension</tt>s to be added to the <tt>session-initiate</tt>
- * {@link JingleIQ} which is to initiate the session with this
- * <tt>CallPeerJabberImpl</tt>
- * @throws OperationFailedException exception
- */
- protected synchronized void initiateSession(
- Iterable<PacketExtension> sessionInitiateExtensions)
- throws OperationFailedException
- {
- initiator = false;
-
- //Create the media description that we'd like to send to the other side.
- List<ContentPacketExtension> offer
- = getMediaHandler().createContentList();
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
-
- synchronized(sidSyncRoot)
- {
- sessionInitIQ
- = JinglePacketFactory.createSessionInitiate(
- protocolProvider.getOurJID(),
- this.peerJID,
- JingleIQ.generateSID(),
- offer);
-
- if(cancelled)
- {
- // we cancelled the call too early so no need to send the
- // session-initiate to peer
- getMediaHandler().getTransportManager().close();
- return;
- }
- }
-
- if (sessionInitiateExtensions != null)
- {
- for (PacketExtension sessionInitiateExtension
- : sessionInitiateExtensions)
- {
- sessionInitIQ.addExtension(sessionInitiateExtension);
- }
- }
-
- protocolProvider.getConnection().sendPacket(sessionInitIQ);
- }
-
- /**
- * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has
- * been received. This <tt>CallPeerJabberImpl</tt> uses the part of the
- * information provided in the specified <tt>conferenceIQ</tt> which
- * concerns it only.
- *
- * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been
- * received
- */
- void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ)
- {
- /*
- * CallPeerJabberImpl does not itself/directly know the specifics
- * related to the channels allocated on the Jitsi Videobridge server.
- * The channels contain transport and media-related information so
- * forward the notification to CallPeerMediaHandlerJabberImpl.
- */
- getMediaHandler().processColibriConferenceIQ(conferenceIQ);
- }
-
- /**
- * Processes the content-accept {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer has accepted
- */
- public void processContentAccept(JingleIQ content)
- {
- List<ContentPacketExtension> contents = content.getContentList();
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- try
- {
- mediaHandler
- .getTransportManager()
- .wrapupConnectivityEstablishment();
- mediaHandler.processAnswer(contents);
- for (ContentPacketExtension c : contents)
- setSenders(getMediaType(c), c.getSenders());
- }
- catch (Exception e)
- {
- logger.warn("Failed to process a content-accept", e);
-
- // Send an error response.
- String reason = "Error: " + e.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- getProtocolProvider().getOurJID(),
- peerJID,
- sessionInitIQ.getSID(),
- Reason.INCOMPATIBLE_PARAMETERS,
- reason);
-
- setState(CallPeerState.FAILED, reason);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- mediaHandler.start();
- }
-
- /**
- * Processes the content-add {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be added
- */
- public void processContentAdd(final JingleIQ content)
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- List<ContentPacketExtension> contents = content.getContentList();
- Iterable<ContentPacketExtension> answerContents;
- JingleIQ contentIQ;
- boolean noCands = false;
- MediaStream oldVideoStream = mediaHandler.getStream(MediaType.VIDEO);
-
- if(logger.isInfoEnabled())
- logger.info("Looking for candidates in content-add.");
- try
- {
- if(!contentAddWithNoCands)
- {
- mediaHandler.processOffer(contents);
-
- /*
- * Gingle transport will not put candidate in session-initiate
- * and content-add.
- */
- for(ContentPacketExtension c : contents)
- {
- if(JingleUtils.getFirstCandidate(c, 1) == null)
- {
- contentAddWithNoCands = true;
- noCands = true;
- }
- }
- }
-
- // if no candidates are present, launch a new Thread which will
- // process and wait for the connectivity establishment (otherwise
- // the existing thread will be blocked and thus cannot receive
- // transport-info with candidates
- if(noCands)
- {
- new Thread()
- {
- @Override
- public void run()
- {
- try
- {
- synchronized(candSyncRoot)
- {
- candSyncRoot.wait();
- }
- }
- catch(InterruptedException e)
- {
- }
-
- processContentAdd(content);
- contentAddWithNoCands = false;
- }
- }.start();
- if(logger.isInfoEnabled())
- logger.info("No candidates found in content-add, started "
- + "new thread.");
- return;
- }
-
- mediaHandler
- .getTransportManager()
- .wrapupConnectivityEstablishment();
- if(logger.isInfoEnabled())
- logger.info("Wrapping up connectivity establishment");
- answerContents = mediaHandler.generateSessionAccept();
- contentIQ = null;
- }
- catch(Exception e)
- {
- logger.warn("Exception occurred", e);
-
- answerContents = null;
- contentIQ
- = JinglePacketFactory.createContentReject(
- getProtocolProvider().getOurJID(),
- this.peerJID,
- getSID(),
- answerContents);
- }
-
- if(contentIQ == null)
- {
- /* send content-accept */
- contentIQ
- = JinglePacketFactory.createContentAccept(
- getProtocolProvider().getOurJID(),
- this.peerJID,
- getSID(),
- answerContents);
- for (ContentPacketExtension c : answerContents)
- setSenders(getMediaType(c), c.getSenders());
- }
-
- getProtocolProvider().getConnection().sendPacket(contentIQ);
- mediaHandler.start();
-
- /*
- * If a remote peer turns her video on in a conference which is hosted
- * by the local peer and the local peer is not streaming her local
- * video, reinvite the other remote peers to enable RTP translation.
- */
- if (oldVideoStream == null)
- {
- MediaStream newVideoStream
- = mediaHandler.getStream(MediaType.VIDEO);
-
- if ((newVideoStream != null)
- && mediaHandler.isRTPTranslationEnabled(MediaType.VIDEO))
- {
- try
- {
- getCall().modifyVideoContent();
- }
- catch (OperationFailedException ofe)
- {
- logger.error("Failed to enable RTP translation", ofe);
- }
- }
- }
- }
-
- /**
- * Processes the content-modify {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be modified
- */
- public void processContentModify(JingleIQ content)
- {
- ContentPacketExtension ext = content.getContentList().get(0);
- MediaType mediaType = getMediaType(ext);
-
- try
- {
- boolean modify
- = (ext.getFirstChildOfType(RtpDescriptionPacketExtension.class)
- != null);
-
- getMediaHandler().reinitContent(ext.getName(), ext, modify);
-
- setSenders(mediaType, ext.getSenders());
-
- if (MediaType.VIDEO.equals(mediaType))
- getCall().modifyVideoContent();
- }
- catch(Exception e)
- {
- logger.info("Failed to process an incoming content-modify", e);
-
- // Send an error response.
- String reason = "Error: " + e.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- getProtocolProvider().getOurJID(),
- peerJID,
- sessionInitIQ.getSID(),
- Reason.INCOMPATIBLE_PARAMETERS,
- reason);
-
- setState(CallPeerState.FAILED, reason);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
- }
-
- /**
- * Processes the content-reject {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ}
- */
- public void processContentReject(JingleIQ content)
- {
- if(content.getContentList().isEmpty())
- {
- //send an error response;
- JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
- "Error: content rejected");
-
- setState(CallPeerState.FAILED, "Error: content rejected");
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
- }
-
- /**
- * Processes the content-remove {@link JingleIQ}.
- *
- * @param content The {@link JingleIQ} that contains content that remote
- * peer wants to be removed
- */
- public void processContentRemove(JingleIQ content)
- {
- List<ContentPacketExtension> contents = content.getContentList();
- boolean videoContentRemoved = false;
-
- if (!contents.isEmpty())
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- for(ContentPacketExtension c : contents)
- {
- mediaHandler.removeContent(c.getName());
-
- MediaType mediaType = getMediaType(c);
- setSenders(mediaType, SendersEnum.none);
-
- if (MediaType.VIDEO.equals(mediaType))
- videoContentRemoved = true;
- }
-
- /*
- * TODO XEP-0166: Jingle says: If the content-remove results in zero
- * content definitions for the session, the entity that receives the
- * content-remove SHOULD send a session-terminate action to the
- * other party (since a session with no content definitions is
- * void).
- */
- }
-
- if (videoContentRemoved)
- {
- // removing of the video content might affect the other sessions
- // in the call
- try
- {
- getCall().modifyVideoContent();
- }
- catch (Exception e)
- {
- logger.warn("Failed to update Jingle sessions");
- }
- }
- }
-
- /**
- * Processes a session-accept {@link JingleIQ}.
- *
- * @param sessionInitIQ The session-accept {@link JingleIQ} to process.
- */
- public void processSessionAccept(JingleIQ sessionInitIQ)
- {
- this.sessionInitIQ = sessionInitIQ;
-
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- List<ContentPacketExtension> answer = sessionInitIQ.getContentList();
-
- try
- {
- mediaHandler
- .getTransportManager()
- .wrapupConnectivityEstablishment();
- mediaHandler.processAnswer(answer);
- for (ContentPacketExtension c : answer)
- setSenders(getMediaType(c), c.getSenders());
- }
- catch(Exception exc)
- {
- if (logger.isInfoEnabled())
- logger.info("Failed to process a session-accept", exc);
-
- //send an error response;
- JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
- exc.getClass().getName() + ": " + exc.getMessage());
-
- setState(CallPeerState.FAILED, "Error: " + exc.getMessage());
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- //tell everyone we are connected so that the audio notifications would
- //stop
- setState(CallPeerState.CONNECTED);
-
- mediaHandler.start();
-
- /*
- * If video was added to the call after we sent the session-initiate
- * to this peer, it needs to be added to this peer's session with a
- * content-add.
- */
- sendModifyVideoContent();
- }
-
- /**
- * Handles the specified session <tt>info</tt> packet according to its
- * content.
- *
- * @param info the {@link SessionInfoPacketExtension} that we just received.
- */
- public void processSessionInfo(SessionInfoPacketExtension info)
- {
- switch (info.getType())
- {
- case ringing:
- setState(CallPeerState.ALERTING_REMOTE_SIDE);
- break;
- case hold:
- getMediaHandler().setRemotelyOnHold(true);
- reevalRemoteHoldStatus();
- break;
- case unhold:
- case active:
- getMediaHandler().setRemotelyOnHold(false);
- reevalRemoteHoldStatus();
- break;
- default:
- logger.warn("Received SessionInfoPacketExtension of unknown type");
- }
- }
-
- /**
- * Processes the session initiation {@link JingleIQ} that we were created
- * with, passing its content to the media handler and then sends either a
- * "session-info/ringing" or a "session-terminate" response.
- *
- * @param sessionInitIQ The {@link JingleIQ} that created the session that
- * we are handling here.
- */
- protected synchronized void processSessionInitiate(JingleIQ sessionInitIQ)
- {
- // Do initiate the session.
- this.sessionInitIQ = sessionInitIQ;
- this.initiator = true;
-
- // This is the SDP offer that came from the initial session-initiate.
- // Contrary to SIP, we are guaranteed to have content because XEP-0166
- // says: "A session consists of at least one content type at a time."
- List<ContentPacketExtension> offer = sessionInitIQ.getContentList();
-
- try
- {
- getMediaHandler().processOffer(offer);
-
- CoinPacketExtension coin = null;
-
- for(PacketExtension ext : sessionInitIQ.getExtensions())
- {
- if(ext.getElementName().equals(
- CoinPacketExtension.ELEMENT_NAME))
- {
- coin = (CoinPacketExtension)ext;
- break;
- }
- }
-
- /* does the call peer acts as a conference focus ? */
- if(coin != null)
- {
- setConferenceFocus(Boolean.parseBoolean(
- (String)coin.getAttribute("isfocus")));
- }
- }
- catch(Exception ex)
- {
- logger.info("Failed to process an incoming session initiate", ex);
-
- //send an error response;
- String reasonText = "Error: " + ex.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- Reason.INCOMPATIBLE_PARAMETERS,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
- return;
- }
-
- // If we do not get the info about the remote peer yet. Get it right
- // now.
- if(this.getDiscoveryInfo() == null)
- {
- String calleeURI = sessionInitIQ.getFrom();
- retrieveDiscoveryInfo(calleeURI);
- }
-
- //send a ringing response
- if (logger.isTraceEnabled())
- logger.trace("will send ringing response: ");
-
- getProtocolProvider().getConnection().sendPacket(
- JinglePacketFactory.createRinging(sessionInitIQ));
-
- synchronized(sessionInitiateSyncRoot)
- {
- sessionInitiateProcessed = true;
- sessionInitiateSyncRoot.notify();
- }
-
- //if this is a 3264 initiator, let's give them an early peek at our
- //answer so that they could start ICE (SIP-2-Jingle gateways won't
- //be able to send their candidates unless they have this)
- DiscoverInfo discoverInfo = getDiscoveryInfo();
- if ((discoverInfo != null)
- && discoverInfo.containsFeature(
- ProtocolProviderServiceJabberImpl.URN_IETF_RFC_3264))
- {
- getProtocolProvider().getConnection().sendPacket(
- JinglePacketFactory.createDescriptionInfo(
- sessionInitIQ.getTo(),
- sessionInitIQ.getFrom(),
- sessionInitIQ.getSID(),
- getMediaHandler().getLocalContentList()));
- }
- }
-
- /**
- * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
- * reason to the user, if there is one.
- *
- * @param jingleIQ the {@link JingleIQ} that's terminating our session.
- */
- public void processSessionTerminate(JingleIQ jingleIQ)
- {
- String reasonStr = "Call ended by remote side.";
- ReasonPacketExtension reasonExt = jingleIQ.getReason();
-
- if(reasonExt != null)
- {
- Reason reason = reasonExt.getReason();
-
- if(reason != null)
- reasonStr += " Reason: " + reason.toString() + ".";
-
- String text = reasonExt.getText();
-
- if(text != null)
- reasonStr += " " + text;
- }
-
- setState(CallPeerState.DISCONNECTED, reasonStr);
- }
-
- /**
- * Processes a specific "XEP-0251: Jingle Session Transfer"
- * <tt>transfer</tt> packet (extension).
- *
- * @param transfer the "XEP-0251: Jingle Session Transfer" transfer packet
- * (extension) to process
- * @throws OperationFailedException if anything goes wrong while processing
- * the specified <tt>transfer</tt> packet (extension)
- */
- public void processTransfer(TransferPacketExtension transfer)
- throws OperationFailedException
- {
- String attendantAddress = transfer.getFrom();
-
- if (attendantAddress == null)
- {
- throw new OperationFailedException(
- "Session transfer must contain a \'from\' attribute value.",
- OperationFailedException.ILLEGAL_ARGUMENT);
- }
-
- String calleeAddress = transfer.getTo();
-
- if (calleeAddress == null)
- {
- throw new OperationFailedException(
- "Session transfer must contain a \'to\' attribute value.",
- OperationFailedException.ILLEGAL_ARGUMENT);
- }
-
- // Checks if the transfer remote peer is contained by the roster of this
- // account.
- Roster roster = getProtocolProvider().getConnection().getRoster();
- if(!roster.contains(StringUtils.parseBareAddress(calleeAddress)))
- {
- String failedMessage =
- "Transfer impossible:\n"
- + "Account roster does not contain transfer peer: "
- + StringUtils.parseBareAddress(calleeAddress);
- setState(CallPeerState.FAILED, failedMessage);
- logger.info(failedMessage);
- }
-
- OperationSetBasicTelephonyJabberImpl basicTelephony
- = (OperationSetBasicTelephonyJabberImpl)
- getProtocolProvider()
- .getOperationSet(OperationSetBasicTelephony.class);
- CallJabberImpl calleeCall = new CallJabberImpl(basicTelephony);
- TransferPacketExtension calleeTransfer = new TransferPacketExtension();
- String sid = transfer.getSID();
-
- calleeTransfer.setFrom(attendantAddress);
- if (sid != null)
- {
- calleeTransfer.setSID(sid);
- calleeTransfer.setTo(calleeAddress);
- }
- basicTelephony.createOutgoingCall(
- calleeCall,
- calleeAddress,
- Arrays.asList(new PacketExtension[] { calleeTransfer }));
- }
-
- /**
- * Processes the <tt>transport-info</tt> {@link JingleIQ}.
- *
- * @param jingleIQ the <tt>transport-info</tt> {@link JingleIQ} to process
- */
- public void processTransportInfo(JingleIQ jingleIQ)
- {
- /*
- * The transport-info action is used to exchange transport candidates so
- * it only concerns the mediaHandler.
- */
- try
- {
- if(isInitiator())
- {
- synchronized(sessionInitiateSyncRoot)
- {
- if(!sessionInitiateProcessed)
- {
- try
- {
- sessionInitiateSyncRoot.wait();
- }
- catch(InterruptedException e)
- {
- }
- }
- }
- }
-
- getMediaHandler().processTransportInfo(
- jingleIQ.getContentList());
- }
- catch (OperationFailedException ofe)
- {
- logger.warn("Failed to process an incoming transport-info", ofe);
-
- //send an error response
- String reasonText = "Error: " + ofe.getMessage();
- JingleIQ errResp
- = JinglePacketFactory.createSessionTerminate(
- getProtocolProvider().getOurJID(),
- peerJID,
- sessionInitIQ.getSID(),
- Reason.GENERAL_ERROR,
- reasonText);
-
- setState(CallPeerState.FAILED, reasonText);
- getProtocolProvider().getConnection().sendPacket(errResp);
-
- return;
- }
-
- synchronized(candSyncRoot)
- {
- candSyncRoot.notify();
- }
- }
-
- /**
- * Puts the <tt>CallPeer</tt> represented by this instance on or off hold.
- *
- * @param onHold <tt>true</tt> to have the <tt>CallPeer</tt> put on hold;
- * <tt>false</tt>, otherwise
- *
- * @throws OperationFailedException if we fail to construct or send the
- * INVITE request putting the remote side on/off hold.
- */
- public void putOnHold(boolean onHold)
- throws OperationFailedException
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- mediaHandler.setLocallyOnHold(onHold);
-
- SessionInfoType type;
-
- if(onHold)
- type = SessionInfoType.hold;
- else
- {
- type = SessionInfoType.unhold;
- getMediaHandler().reinitAllContents();
- }
-
- //we are now on hold and need to realize this before potentially
- //spoiling it all with an exception while sending the packet :).
- reevalLocalHoldStatus();
-
- JingleIQ onHoldIQ = JinglePacketFactory.createSessionInfo(
- getProtocolProvider().getOurJID(),
- peerJID,
- getSID(),
- type);
-
- getProtocolProvider().getConnection().sendPacket(onHoldIQ);
- }
-
- /**
- * Send a <tt>content-add</tt> to add video setup.
- */
- private void sendAddVideoContent()
- {
- List<ContentPacketExtension> contents;
-
- try
- {
- contents = getMediaHandler().createContentList(MediaType.VIDEO);
- }
- catch(Exception exc)
- {
- logger.warn("Failed to gather content for video type", exc);
- return;
- }
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentAdd(
- protocolProvider.getOurJID(),
- this.peerJID,
- getSID(),
- contents);
-
- protocolProvider.getConnection().sendPacket(contentIQ);
- }
-
- /**
- * Sends a <tt>content</tt> message to reflect changes in the setup such as
- * the local peer/user becoming a conference focus.
- */
- public void sendCoinSessionInfo()
- {
- JingleIQ sessionInfoIQ
- = JinglePacketFactory.createSessionInfo(
- getProtocolProvider().getOurJID(),
- this.peerJID,
- getSID());
- CoinPacketExtension coinExt
- = new CoinPacketExtension(getCall().isConferenceFocus());
-
- sessionInfoIQ.addExtension(coinExt);
- getProtocolProvider().getConnection().sendPacket(sessionInfoIQ);
- }
-
- /**
- * Returns the <tt>MediaDirection</tt> that should be set for the content
- * of type <tt>mediaType</tt> in the Jingle session for this
- * <tt>CallPeer</tt>.
- * If we are the focus of a conference and are doing RTP translation,
- * takes into account the other <tt>CallPeer</tt>s in the <tt>Call</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> for which to return the
- * <tt>MediaDirection</tt>
- * @return the <tt>MediaDirection</tt> that should be used for the content
- * of type <tt>mediaType</tt> in the Jingle session for this
- * <tt>CallPeer</tt>.
- */
- private MediaDirection getDirectionForJingle(MediaType mediaType)
- {
- MediaDirection direction = MediaDirection.INACTIVE;
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- // If we are streaming media, the direction should allow sending
- if ( (MediaType.AUDIO == mediaType &&
- mediaHandler.isLocalAudioTransmissionEnabled()) ||
- (MediaType.VIDEO == mediaType &&
- isLocalVideoStreaming()))
- direction = direction.or(MediaDirection.SENDONLY);
-
- // If we are receiving media from this CallPeer, the direction should
- // allow receiving
- SendersEnum senders = getSenders(mediaType);
- if (senders == null || senders == SendersEnum.both ||
- (isInitiator() && senders == SendersEnum.initiator) ||
- (!isInitiator() && senders == SendersEnum.responder))
- direction = direction.or(MediaDirection.RECVONLY);
-
- // If we are the focus of a conference and we are receiving media from
- // another CallPeer in the same Call, the direction should allow sending
- CallJabberImpl call = getCall();
- if (call != null && call.isConferenceFocus())
- {
- for (CallPeerJabberImpl peer : call.getCallPeerList())
- {
- if (peer != this)
- {
- senders = peer.getSenders(mediaType);
- if (senders == null || senders == SendersEnum.both ||
- (peer.isInitiator()
- && senders == SendersEnum.initiator) ||
- (!peer.isInitiator()
- && senders == SendersEnum.responder))
- {
- direction = direction.or(MediaDirection.SENDONLY);
- break;
- }
- }
- }
- }
-
- return direction;
- }
-
- /**
- * Send, if necessary, a jingle <tt>content</tt> message to reflect change
- * in video setup. Whether the jingle session should have a video content,
- * and if so, the value of the <tt>senders</tt> field is determined
- * based on whether we are streaming local video and, if we are the focus
- * of a conference, on the other peers in the conference.
- * The message can be content-modify if video content exists (and the
- * <tt>senders</tt> field changes), content-add or content-remove.
- *
- * @return <tt>true</tt> if a jingle <tt>content</tt> message was sent.
- */
- public boolean sendModifyVideoContent()
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- MediaDirection direction = getDirectionForJingle(MediaType.VIDEO);
-
- ContentPacketExtension remoteContent
- = mediaHandler.getLocalContent(MediaType.VIDEO.toString());
-
- if (remoteContent == null)
- {
- if (direction == MediaDirection.INACTIVE)
- {
- // no video content, none needed
- return false;
- }
- else
- {
- if (getState() == CallPeerState.CONNECTED)
- {
- if (logger.isInfoEnabled())
- logger.info("Adding video content for " + this);
- sendAddVideoContent();
- return true;
- }
- return false;
- }
- }
- else
- {
- if (direction == MediaDirection.INACTIVE)
- {
- sendRemoveVideoContent();
- return true;
- }
- }
-
- SendersEnum senders = getSenders(MediaType.VIDEO);
- if (senders == null)
- senders = SendersEnum.both;
-
- SendersEnum newSenders = SendersEnum.none;
- if (MediaDirection.SENDRECV == direction)
- newSenders = SendersEnum.both;
- else if (MediaDirection.RECVONLY == direction)
- newSenders = isInitiator()
- ? SendersEnum.initiator : SendersEnum.responder;
- else if (MediaDirection.SENDONLY == direction)
- newSenders = isInitiator()
- ? SendersEnum.responder : SendersEnum.initiator;
-
- /*
- * Send Content-Modify
- */
- ContentPacketExtension ext = new ContentPacketExtension();
- String remoteContentName = remoteContent.getName();
-
- ext.setSenders(newSenders);
- ext.setCreator(remoteContent.getCreator());
- ext.setName(remoteContentName);
-
- if (newSenders != senders)
- {
- if (logger.isInfoEnabled())
- logger.info("Sending content modify, senders: "
- + senders + "->" + newSenders);
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentModify(
- protocolProvider.getOurJID(),
- this.peerJID,
- getSID(),
- ext);
-
- protocolProvider.getConnection().sendPacket(contentIQ);
- }
-
- try
- {
- mediaHandler.reinitContent(remoteContentName, ext, false);
- mediaHandler.start();
- }
- catch(Exception e)
- {
- logger.warn("Exception occurred during media reinitialization", e);
- }
-
- return (newSenders != senders);
- }
-
- /**
- * Send a <tt>content</tt> message to reflect change in video setup (start
- * or stop).
- */
- public void sendModifyVideoResolutionContent()
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- ContentPacketExtension remoteContent
- = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
- ContentPacketExtension content;
-
- logger.info("send modify-content to change resolution");
-
- // send content-modify with RTP description
-
- // create content list with resolution
- try
- {
- content = mediaHandler.createContentForMedia(MediaType.VIDEO);
- }
- catch (Exception e)
- {
- logger.warn("Failed to gather content for video type", e);
- return;
- }
-
- // if we are only receiving video senders is null
- SendersEnum senders = remoteContent.getSenders();
-
- if (senders != null)
- content.setSenders(senders);
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentModify(
- protocolProvider.getOurJID(),
- this.peerJID,
- getSID(),
- content);
-
- protocolProvider.getConnection().sendPacket(contentIQ);
-
- try
- {
- mediaHandler.reinitContent(remoteContent.getName(), content, false);
- mediaHandler.start();
- }
- catch(Exception e)
- {
- logger.warn("Exception occurred when media reinitialization", e);
- }
- }
-
- /**
- * Send a <tt>content-remove</tt> to remove video setup.
- */
- private void sendRemoveVideoContent()
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
-
- ContentPacketExtension content = new ContentPacketExtension();
- ContentPacketExtension remoteContent
- = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
- if (remoteContent == null)
- return;
- String remoteContentName = remoteContent.getName();
-
- content.setName(remoteContentName);
- content.setCreator(remoteContent.getCreator());
- content.setSenders(remoteContent.getSenders());
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
- JingleIQ contentIQ
- = JinglePacketFactory.createContentRemove(
- protocolProvider.getOurJID(),
- this.peerJID,
- getSID(),
- Arrays.asList(content));
-
- protocolProvider.getConnection().sendPacket(contentIQ);
- mediaHandler.removeContent(remoteContentName);
- setSenders(MediaType.VIDEO, SendersEnum.none);
- }
-
- /**
- * Sends local candidate addresses from the local peer to the remote peer
- * using the <tt>transport-info</tt> {@link JingleIQ}.
- *
- * @param contents the local candidate addresses to be sent from the local
- * peer to the remote peer using the <tt>transport-info</tt>
- * {@link JingleIQ}
- */
- protected void sendTransportInfo(Iterable<ContentPacketExtension> contents)
- {
- // if the call is canceled, do not start sending candidates in
- // transport-info
- if(cancelled)
- return;
-
- JingleIQ transportInfo = new JingleIQ();
-
- for (ContentPacketExtension content : contents)
- transportInfo.addContent(content);
-
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
-
- transportInfo.setAction(JingleAction.TRANSPORT_INFO);
- transportInfo.setFrom(protocolProvider.getOurJID());
- transportInfo.setSID(getSID());
- transportInfo.setTo(getAddress());
- transportInfo.setType(IQ.Type.SET);
-
- PacketCollector collector
- = protocolProvider.getConnection().createPacketCollector(
- new PacketIDFilter(transportInfo.getPacketID()));
-
- protocolProvider.getConnection().sendPacket(transportInfo);
- collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
- collector.cancel();
- }
-
- @Override
- public void setState(CallPeerState newState, String reason, int reasonCode)
- {
- CallPeerState oldState = getState();
- try
- {
- /*
- * We need to dispose of the transport manager before the
- * 'call' field is set to null, because if Jitsi Videobridge is in
- * use, it (the call) is needed in order to expire the
- * Videobridge channels.
- */
- if (CallPeerState.DISCONNECTED.equals(newState)
- || CallPeerState.FAILED.equals(newState))
- getMediaHandler().getTransportManager().close();
- }
- finally
- {
- super.setState(newState, reason, reasonCode);
- }
-
- if (CallPeerState.isOnHold(oldState)
- && CallPeerState.CONNECTED.equals(newState))
- {
- try
- {
- getCall().modifyVideoContent();
- }
- catch (OperationFailedException ofe)
- {
- logger.error("Failed to update call video state after " +
- "'hold' status removed for "+this);
- }
- }
- }
-
- /**
- * Transfer (in the sense of call transfer) this <tt>CallPeer</tt> to a
- * specific callee address which may optionally be participating in an
- * active <tt>Call</tt>.
- *
- * @param to the address of the callee to transfer this <tt>CallPeer</tt> to
- * @param sid the Jingle session ID of the active <tt>Call</tt> between the
- * local peer and the callee in the case of attended transfer; <tt>null</tt>
- * in the case of unattended transfer
- * @throws OperationFailedException if something goes wrong
- */
- protected void transfer(String to, String sid)
- throws OperationFailedException
- {
- JingleIQ transferSessionInfo = new JingleIQ();
- ProtocolProviderServiceJabberImpl protocolProvider
- = getProtocolProvider();
-
- transferSessionInfo.setAction(JingleAction.SESSION_INFO);
- transferSessionInfo.setFrom(protocolProvider.getOurJID());
- transferSessionInfo.setSID(getSID());
- transferSessionInfo.setTo(getAddress());
- transferSessionInfo.setType(IQ.Type.SET);
-
- TransferPacketExtension transfer = new TransferPacketExtension();
-
- // Attended transfer.
- if (sid != null)
- {
- /*
- * Not really sure what the value of the "from" attribute of the
- * "transfer" element should be but the examples in "XEP-0251:
- * Jingle Session Transfer" has it in the case of attended transfer.
- */
- transfer.setFrom(protocolProvider.getOurJID());
- transfer.setSID(sid);
-
- // Puts on hold the 2 calls before making the attended transfer.
- OperationSetBasicTelephonyJabberImpl basicTelephony
- = (OperationSetBasicTelephonyJabberImpl)
- protocolProvider.getOperationSet(
- OperationSetBasicTelephony.class);
- CallPeerJabberImpl callPeer = basicTelephony.getActiveCallPeer(sid);
- if(callPeer != null)
- {
- if(!CallPeerState.isOnHold(callPeer.getState()))
- {
- callPeer.putOnHold(true);
- }
- }
-
- if(!CallPeerState.isOnHold(this.getState()))
- {
- this.putOnHold(true);
- }
- }
- transfer.setTo(to);
-
- transferSessionInfo.addExtension(transfer);
-
- Connection connection = protocolProvider.getConnection();
- PacketCollector collector = connection.createPacketCollector(
- new PacketIDFilter(transferSessionInfo.getPacketID()));
- protocolProvider.getConnection().sendPacket(transferSessionInfo);
-
- Packet result
- = collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
-
- if(result == null)
- {
- // Log the failed transfer call and notify the user.
- throw new OperationFailedException(
- "No response to the \"transfer\" request.",
- OperationFailedException.ILLEGAL_ARGUMENT);
- }
- else if (((IQ) result).getType() != IQ.Type.RESULT)
- {
- // Log the failed transfer call and notify the user.
- throw new OperationFailedException(
- "Remote peer does not manage call \"transfer\"."
- + "Response to the \"transfer\" request is: "
- + ((IQ) result).getType(),
- OperationFailedException.ILLEGAL_ARGUMENT);
- }
- else
- {
- String message = ((sid == null) ? "Unattended" : "Attended")
- + " transfer to: "
- + to;
- // Implements the SIP behavior: once the transfer is accepted, the
- // current call is closed.
- hangup(
- false,
- message,
- new ReasonPacketExtension(Reason.SUCCESS,
- message,
- new TransferredPacketExtension()));
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public String getEntity()
- {
- return getAddress();
- }
-
- /**
- * {@inheritDoc}
- *
- * In Jingle there isn't an actual "direction" parameter. We use the
- * <tt>senders</tt> field to calculate the direction.
- */
- @Override
- public MediaDirection getDirection(MediaType mediaType)
- {
- SendersEnum senders = getSenders(mediaType);
-
- if (senders == SendersEnum.none)
- {
- return MediaDirection.INACTIVE;
- }
- else if (senders == null || senders == SendersEnum.both)
- {
- return MediaDirection.SENDRECV;
- }
- else if (senders == SendersEnum.initiator)
- {
- return
- isInitiator()
- ? MediaDirection.RECVONLY
- : MediaDirection.SENDONLY;
- }
- else //senders == SendersEnum.responder
- {
- return
- isInitiator()
- ? MediaDirection.SENDONLY
- : MediaDirection.RECVONLY;
- }
- }
-
- /**
- * Gets the current value of the <tt>senders</tt> field of the content with
- * name <tt>mediaType</tt> in the Jingle session with this
- * <tt>CallPeer</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> for which to get the current
- * value of the <tt>senders</tt> field.
- * @return the current value of the <tt>senders</tt> field of the content
- * with name <tt>mediaType</tt> in the Jingle session with this
- * <tt>CallPeer</tt>.
- */
- public SendersEnum getSenders(MediaType mediaType)
- {
- switch (mediaType)
- {
- case AUDIO:
- return audioSenders;
- case DATA:
- /*
- * FIXME DATA has been introduced as a MediaType but explicit
- * support for DATA content has not been added yet.
- */
- return SendersEnum.none;
- case VIDEO:
- return videoSenders;
- default:
- throw new IllegalArgumentException("mediaType");
- }
- }
-
- /**
- * Set the current value of the <tt>senders</tt> field of the content with
- * name <tt>mediaType</tt> in the Jingle session with this <tt>CallPeer</tt>
- * @param mediaType the <tt>MediaType</tt> for which to get the current
- * value of the <tt>senders</tt> field.
- * @param senders the value to set
- */
- public void setSenders(MediaType mediaType, SendersEnum senders)
- {
- if (mediaType == null)
- return;
- else if (MediaType.AUDIO.equals(mediaType))
- this.audioSenders = senders;
- else if (MediaType.VIDEO.equals(mediaType))
- this.videoSenders = senders;
- else
- throw new IllegalArgumentException("mediaType");
- }
-
- /**
- * Gets the <tt>MediaType</tt> of <tt>content</tt>. If <tt>content</tt>
- * does not have a <tt>description</tt> child and therefore not
- * <tt>MediaType</tt> can be associated with it, tries to take the
- * <tt>MediaType</tt> from the session's already established contents with
- * the same name as <tt>content</tt>
- * @param content the <tt>ContentPacketExtention</tt> for which to get the
- * <tt>MediaType</tt>
- * @return the <tt>MediaType</tt> of <tt>content</tt>.
- */
- public MediaType getMediaType(ContentPacketExtension content)
- {
- String contentName = content.getName();
- if (contentName == null)
- return null;
-
- MediaType mediaType = JingleUtils.getMediaType(content);
- if (mediaType == null)
- {
- CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
- for (MediaType m : MediaType.values())
- {
- ContentPacketExtension sessionContent
- = mediaHandler.getRemoteContent(m.toString());
- if (sessionContent == null)
- sessionContent = mediaHandler.getLocalContent(m.toString());
-
- if (sessionContent != null
- && contentName.equals(sessionContent.getName()))
- {
- mediaType = m;
- break;
- }
- }
- }
-
- return mediaType;
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
+import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
+import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.ContentPacketExtension.SendersEnum;
+import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+import org.jitsi.service.neomedia.*;
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.util.*;
+import org.jivesoftware.smackx.packet.*;
+
+/**
+ * Implements a Jabber <tt>CallPeer</tt>.
+ *
+ * @author Emil Ivov
+ * @author Lyubomir Marinov
+ * @author Boris Grozev
+ */
+public class CallPeerJabberImpl
+ extends AbstractCallPeerJabberGTalkImpl
+ <CallJabberImpl, CallPeerMediaHandlerJabberImpl, JingleIQ>
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>CallPeerJabberImpl</tt> class and its
+ * instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(CallPeerJabberImpl.class);
+
+ /**
+ * If the call is cancelled before session-initiate is sent.
+ */
+ private boolean cancelled = false;
+
+ /**
+ * Synchronization object for candidates available.
+ */
+ private final Object candSyncRoot = new Object();
+
+ /**
+ * If the content-add does not contains candidates.
+ */
+ private boolean contentAddWithNoCands = false;
+
+ /**
+ * If we have processed the session initiate.
+ */
+ private boolean sessionInitiateProcessed = false;
+
+ /**
+ * Synchronization object. Synchronization object? Wow, who would have
+ * thought! ;) Would be great to have a word on what we are syncing with it
+ */
+ private final Object sessionInitiateSyncRoot = new Object();
+
+ /**
+ * Synchronization object for SID.
+ */
+ private final Object sidSyncRoot = new Object();
+
+ /**
+ * The current value of the 'senders' field of the audio content in the
+ * Jingle session with this <tt>CallPeer</tt>.
+ * <tt>null</tt> should be interpreted as 'both', which is the default in
+ * Jingle if the XML attribute is missing.
+ */
+ private SendersEnum audioSenders = SendersEnum.none;
+
+ /**
+ * The current value of the 'senders' field of the video content in the
+ * Jingle session with this <tt>CallPeer</tt>.
+ * <tt>null</tt> should be interpreted as 'both', which is the default in
+ * Jingle if the XML attribute is missing.
+ */
+ private SendersEnum videoSenders = SendersEnum.none;
+
+ /**
+ * Creates a new call peer with address <tt>peerAddress</tt>.
+ *
+ * @param peerAddress the Jabber address of the new call peer.
+ * @param owningCall the call that contains this call peer.
+ */
+ public CallPeerJabberImpl(String peerAddress,
+ CallJabberImpl owningCall)
+ {
+ super(peerAddress, owningCall);
+
+ setMediaHandler(new CallPeerMediaHandlerJabberImpl(this));
+ }
+
+ /**
+ * Creates a new call peer with address <tt>peerAddress</tt>.
+ *
+ * @param peerAddress the Jabber address of the new call peer.
+ * @param owningCall the call that contains this call peer.
+ * @param sessionIQ The session-initiate <tt>JingleIQ</tt> which was
+ * received from <tt>peerAddress</tt> and caused the creation of this
+ * <tt>CallPeerJabberImpl</tt>
+ */
+ public CallPeerJabberImpl(String peerAddress,
+ CallJabberImpl owningCall,
+ JingleIQ sessionIQ)
+ {
+ this(peerAddress, owningCall);
+ this.sessionInitIQ = sessionIQ;
+ }
+
+ /**
+ * Send a session-accept <tt>JingleIQ</tt> to this <tt>CallPeer</tt>
+ * @throws OperationFailedException if we fail to create or send the
+ * response.
+ */
+ public synchronized void answer()
+ throws OperationFailedException
+ {
+ Iterable<ContentPacketExtension> answer;
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
+ try
+ {
+ mediaHandler
+ .getTransportManager()
+ .wrapupConnectivityEstablishment();
+ answer = mediaHandler.generateSessionAccept();
+ for (ContentPacketExtension c : answer)
+ setSenders(getMediaType(c), c.getSenders());
+ }
+ catch(Exception exc)
+ {
+ logger.info("Failed to answer an incoming call", exc);
+
+ //send an error response
+ String reasonText = "Error: " + exc.getMessage();
+ JingleIQ errResp
+ = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(),
+ Reason.FAILED_APPLICATION,
+ reasonText);
+
+ setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+
+ JingleIQ response
+ = JinglePacketFactory.createSessionAccept(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ getSID(),
+ answer);
+
+ //send the packet first and start the stream later in case the media
+ //relay needs to see it before letting hole punching techniques through.
+ getProtocolProvider().getConnection().sendPacket(response);
+
+ try
+ {
+ mediaHandler.start();
+ }
+ catch(UndeclaredThrowableException e)
+ {
+ Throwable exc = e.getUndeclaredThrowable();
+
+ logger.info("Failed to establish a connection", exc);
+
+ //send an error response
+ String reasonText = "Error: " + exc.getMessage();
+ JingleIQ errResp
+ = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(),
+ Reason.GENERAL_ERROR,
+ reasonText);
+
+ setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+
+ //tell everyone we are connected so that the audio notifications would
+ //stop
+ setState(CallPeerState.CONNECTED);
+ }
+
+ /**
+ * Returns the session ID of the Jingle session associated with this call.
+ *
+ * @return the session ID of the Jingle session associated with this call.
+ */
+ @Override
+ public String getSID()
+ {
+ return sessionInitIQ != null ? sessionInitIQ.getSID() : null;
+ }
+
+ /**
+ * Returns the IQ ID of the Jingle session-initiate packet associated with
+ * this call.
+ *
+ * @return the IQ ID of the Jingle session-initiate packet associated with
+ * this call.
+ */
+ public JingleIQ getSessionIQ()
+ {
+ return sessionInitIQ;
+ }
+
+ /**
+ * Ends the call with 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)
+ {
+ CallPeerState prevPeerState = getState();
+
+ // do nothing if the call is already ended
+ if (CallPeerState.DISCONNECTED.equals(prevPeerState)
+ || CallPeerState.FAILED.equals(prevPeerState))
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Ignoring a request to hangup a call peer "
+ + "that is already DISCONNECTED");
+ return;
+ }
+
+ setState(
+ failed ? CallPeerState.FAILED : CallPeerState.DISCONNECTED,
+ reasonText);
+
+ JingleIQ responseIQ = null;
+
+ if (prevPeerState.equals(CallPeerState.CONNECTED)
+ || CallPeerState.isOnHold(prevPeerState))
+ {
+ responseIQ = JinglePacketFactory.createBye(
+ getProtocolProvider().getOurJID(), peerJID, getSID());
+ }
+ else if (CallPeerState.CONNECTING.equals(prevPeerState)
+ || CallPeerState.CONNECTING_WITH_EARLY_MEDIA.equals(prevPeerState)
+ || CallPeerState.ALERTING_REMOTE_SIDE.equals(prevPeerState))
+ {
+ String jingleSID = getSID();
+
+ if(jingleSID == null)
+ {
+ synchronized(sidSyncRoot)
+ {
+ // we cancelled the call too early because the jingleSID
+ // is null (i.e. the session-initiate has not been created)
+ // and no need to send the session-terminate
+ cancelled = true;
+ return;
+ }
+ }
+
+ responseIQ = JinglePacketFactory.createCancel(
+ getProtocolProvider().getOurJID(), peerJID, getSID());
+ }
+ else if (prevPeerState.equals(CallPeerState.INCOMING_CALL))
+ {
+ responseIQ = JinglePacketFactory.createBusy(
+ getProtocolProvider().getOurJID(), peerJID, getSID());
+ }
+ 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)
+ {
+ reason.setOtherExtension(reasonOtherExtension);
+ }
+ else if(reasonOtherExtension instanceof ReasonPacketExtension)
+ {
+ responseIQ.setReason(
+ (ReasonPacketExtension)reasonOtherExtension);
+ }
+ }
+
+ getProtocolProvider().getConnection().sendPacket(responseIQ);
+ }
+ }
+
+ /**
+ * Creates and sends a session-initiate {@link JingleIQ}.
+ *
+ * @param sessionInitiateExtensions a collection of additional and optional
+ * <tt>PacketExtension</tt>s to be added to the <tt>session-initiate</tt>
+ * {@link JingleIQ} which is to initiate the session with this
+ * <tt>CallPeerJabberImpl</tt>
+ * @throws OperationFailedException exception
+ */
+ protected synchronized void initiateSession(
+ Iterable<PacketExtension> sessionInitiateExtensions)
+ throws OperationFailedException
+ {
+ initiator = false;
+
+ //Create the media description that we'd like to send to the other side.
+ List<ContentPacketExtension> offer
+ = getMediaHandler().createContentList();
+
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+
+ synchronized(sidSyncRoot)
+ {
+ sessionInitIQ
+ = JinglePacketFactory.createSessionInitiate(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ JingleIQ.generateSID(),
+ offer);
+
+ if(cancelled)
+ {
+ // we cancelled the call too early so no need to send the
+ // session-initiate to peer
+ getMediaHandler().getTransportManager().close();
+ return;
+ }
+ }
+
+ if (sessionInitiateExtensions != null)
+ {
+ for (PacketExtension sessionInitiateExtension
+ : sessionInitiateExtensions)
+ {
+ sessionInitIQ.addExtension(sessionInitiateExtension);
+ }
+ }
+
+ protocolProvider.getConnection().sendPacket(sessionInitIQ);
+ }
+
+ /**
+ * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has
+ * been received. This <tt>CallPeerJabberImpl</tt> uses the part of the
+ * information provided in the specified <tt>conferenceIQ</tt> which
+ * concerns it only.
+ *
+ * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been
+ * received
+ */
+ void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ)
+ {
+ /*
+ * CallPeerJabberImpl does not itself/directly know the specifics
+ * related to the channels allocated on the Jitsi Videobridge server.
+ * The channels contain transport and media-related information so
+ * forward the notification to CallPeerMediaHandlerJabberImpl.
+ */
+ getMediaHandler().processColibriConferenceIQ(conferenceIQ);
+ }
+
+ /**
+ * Processes the content-accept {@link JingleIQ}.
+ *
+ * @param content The {@link JingleIQ} that contains content that remote
+ * peer has accepted
+ */
+ public void processContentAccept(JingleIQ content)
+ {
+ List<ContentPacketExtension> contents = content.getContentList();
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
+ try
+ {
+ mediaHandler
+ .getTransportManager()
+ .wrapupConnectivityEstablishment();
+ mediaHandler.processAnswer(contents);
+ for (ContentPacketExtension c : contents)
+ setSenders(getMediaType(c), c.getSenders());
+ }
+ catch (Exception e)
+ {
+ logger.warn("Failed to process a content-accept", e);
+
+ // Send an error response.
+ String reason = "Error: " + e.getMessage();
+ JingleIQ errResp
+ = JinglePacketFactory.createSessionTerminate(
+ getProtocolProvider().getOurJID(),
+ peerJID,
+ sessionInitIQ.getSID(),
+ Reason.INCOMPATIBLE_PARAMETERS,
+ reason);
+
+ setState(CallPeerState.FAILED, reason);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+
+ mediaHandler.start();
+ }
+
+ /**
+ * Processes the content-add {@link JingleIQ}.
+ *
+ * @param content The {@link JingleIQ} that contains content that remote
+ * peer wants to be added
+ */
+ public void processContentAdd(final JingleIQ content)
+ {
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+ List<ContentPacketExtension> contents = content.getContentList();
+ Iterable<ContentPacketExtension> answerContents;
+ JingleIQ contentIQ;
+ boolean noCands = false;
+ MediaStream oldVideoStream = mediaHandler.getStream(MediaType.VIDEO);
+
+ if(logger.isInfoEnabled())
+ logger.info("Looking for candidates in content-add.");
+ try
+ {
+ if(!contentAddWithNoCands)
+ {
+ mediaHandler.processOffer(contents);
+
+ /*
+ * Gingle transport will not put candidate in session-initiate
+ * and content-add.
+ */
+ for(ContentPacketExtension c : contents)
+ {
+ if(JingleUtils.getFirstCandidate(c, 1) == null)
+ {
+ contentAddWithNoCands = true;
+ noCands = true;
+ }
+ }
+ }
+
+ // if no candidates are present, launch a new Thread which will
+ // process and wait for the connectivity establishment (otherwise
+ // the existing thread will be blocked and thus cannot receive
+ // transport-info with candidates
+ if(noCands)
+ {
+ new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ synchronized(candSyncRoot)
+ {
+ candSyncRoot.wait();
+ }
+ }
+ catch(InterruptedException e)
+ {
+ }
+
+ processContentAdd(content);
+ contentAddWithNoCands = false;
+ }
+ }.start();
+ if(logger.isInfoEnabled())
+ logger.info("No candidates found in content-add, started "
+ + "new thread.");
+ return;
+ }
+
+ mediaHandler
+ .getTransportManager()
+ .wrapupConnectivityEstablishment();
+ if(logger.isInfoEnabled())
+ logger.info("Wrapping up connectivity establishment");
+ answerContents = mediaHandler.generateSessionAccept();
+ contentIQ = null;
+ }
+ catch(Exception e)
+ {
+ logger.warn("Exception occurred", e);
+
+ answerContents = null;
+ contentIQ
+ = JinglePacketFactory.createContentReject(
+ getProtocolProvider().getOurJID(),
+ this.peerJID,
+ getSID(),
+ answerContents);
+ }
+
+ if(contentIQ == null)
+ {
+ /* send content-accept */
+ contentIQ
+ = JinglePacketFactory.createContentAccept(
+ getProtocolProvider().getOurJID(),
+ this.peerJID,
+ getSID(),
+ answerContents);
+ for (ContentPacketExtension c : answerContents)
+ setSenders(getMediaType(c), c.getSenders());
+ }
+
+ getProtocolProvider().getConnection().sendPacket(contentIQ);
+ mediaHandler.start();
+
+ /*
+ * If a remote peer turns her video on in a conference which is hosted
+ * by the local peer and the local peer is not streaming her local
+ * video, reinvite the other remote peers to enable RTP translation.
+ */
+ if (oldVideoStream == null)
+ {
+ MediaStream newVideoStream
+ = mediaHandler.getStream(MediaType.VIDEO);
+
+ if ((newVideoStream != null)
+ && mediaHandler.isRTPTranslationEnabled(MediaType.VIDEO))
+ {
+ try
+ {
+ getCall().modifyVideoContent();
+ }
+ catch (OperationFailedException ofe)
+ {
+ logger.error("Failed to enable RTP translation", ofe);
+ }
+ }
+ }
+ }
+
+ /**
+ * Processes the content-modify {@link JingleIQ}.
+ *
+ * @param content The {@link JingleIQ} that contains content that remote
+ * peer wants to be modified
+ */
+ public void processContentModify(JingleIQ content)
+ {
+ ContentPacketExtension ext = content.getContentList().get(0);
+ MediaType mediaType = getMediaType(ext);
+
+ try
+ {
+ boolean modify
+ = (ext.getFirstChildOfType(RtpDescriptionPacketExtension.class)
+ != null);
+
+ getMediaHandler().reinitContent(ext.getName(), ext, modify);
+
+ setSenders(mediaType, ext.getSenders());
+
+ if (MediaType.VIDEO.equals(mediaType))
+ getCall().modifyVideoContent();
+ }
+ catch(Exception e)
+ {
+ logger.info("Failed to process an incoming content-modify", e);
+
+ // Send an error response.
+ String reason = "Error: " + e.getMessage();
+ JingleIQ errResp
+ = JinglePacketFactory.createSessionTerminate(
+ getProtocolProvider().getOurJID(),
+ peerJID,
+ sessionInitIQ.getSID(),
+ Reason.INCOMPATIBLE_PARAMETERS,
+ reason);
+
+ setState(CallPeerState.FAILED, reason);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+ }
+
+ /**
+ * Processes the content-reject {@link JingleIQ}.
+ *
+ * @param content The {@link JingleIQ}
+ */
+ public void processContentReject(JingleIQ content)
+ {
+ if(content.getContentList().isEmpty())
+ {
+ //send an error response;
+ JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
+ "Error: content rejected");
+
+ setState(CallPeerState.FAILED, "Error: content rejected");
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+ }
+
+ /**
+ * Processes the content-remove {@link JingleIQ}.
+ *
+ * @param content The {@link JingleIQ} that contains content that remote
+ * peer wants to be removed
+ */
+ public void processContentRemove(JingleIQ content)
+ {
+ List<ContentPacketExtension> contents = content.getContentList();
+ boolean videoContentRemoved = false;
+
+ if (!contents.isEmpty())
+ {
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
+ for(ContentPacketExtension c : contents)
+ {
+ mediaHandler.removeContent(c.getName());
+
+ MediaType mediaType = getMediaType(c);
+ setSenders(mediaType, SendersEnum.none);
+
+ if (MediaType.VIDEO.equals(mediaType))
+ videoContentRemoved = true;
+ }
+
+ /*
+ * TODO XEP-0166: Jingle says: If the content-remove results in zero
+ * content definitions for the session, the entity that receives the
+ * content-remove SHOULD send a session-terminate action to the
+ * other party (since a session with no content definitions is
+ * void).
+ */
+ }
+
+ if (videoContentRemoved)
+ {
+ // removing of the video content might affect the other sessions
+ // in the call
+ try
+ {
+ getCall().modifyVideoContent();
+ }
+ catch (Exception e)
+ {
+ logger.warn("Failed to update Jingle sessions");
+ }
+ }
+ }
+
+ /**
+ * Processes a session-accept {@link JingleIQ}.
+ *
+ * @param sessionInitIQ The session-accept {@link JingleIQ} to process.
+ */
+ public void processSessionAccept(JingleIQ sessionInitIQ)
+ {
+ this.sessionInitIQ = sessionInitIQ;
+
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+ List<ContentPacketExtension> answer = sessionInitIQ.getContentList();
+
+ try
+ {
+ mediaHandler
+ .getTransportManager()
+ .wrapupConnectivityEstablishment();
+ mediaHandler.processAnswer(answer);
+ for (ContentPacketExtension c : answer)
+ setSenders(getMediaType(c), c.getSenders());
+ }
+ catch(Exception exc)
+ {
+ if (logger.isInfoEnabled())
+ logger.info("Failed to process a session-accept", exc);
+
+ //send an error response;
+ JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(), sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(), Reason.INCOMPATIBLE_PARAMETERS,
+ exc.getClass().getName() + ": " + exc.getMessage());
+
+ setState(CallPeerState.FAILED, "Error: " + exc.getMessage());
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+
+ //tell everyone we are connected so that the audio notifications would
+ //stop
+ setState(CallPeerState.CONNECTED);
+
+ mediaHandler.start();
+
+ /*
+ * If video was added to the call after we sent the session-initiate
+ * to this peer, it needs to be added to this peer's session with a
+ * content-add.
+ */
+ sendModifyVideoContent();
+ }
+
+ /**
+ * Handles the specified session <tt>info</tt> packet according to its
+ * content.
+ *
+ * @param info the {@link SessionInfoPacketExtension} that we just received.
+ */
+ public void processSessionInfo(SessionInfoPacketExtension info)
+ {
+ switch (info.getType())
+ {
+ case ringing:
+ setState(CallPeerState.ALERTING_REMOTE_SIDE);
+ break;
+ case hold:
+ getMediaHandler().setRemotelyOnHold(true);
+ reevalRemoteHoldStatus();
+ break;
+ case unhold:
+ case active:
+ getMediaHandler().setRemotelyOnHold(false);
+ reevalRemoteHoldStatus();
+ break;
+ default:
+ logger.warn("Received SessionInfoPacketExtension of unknown type");
+ }
+ }
+
+ /**
+ * Processes the session initiation {@link JingleIQ} that we were created
+ * with, passing its content to the media handler and then sends either a
+ * "session-info/ringing" or a "session-terminate" response.
+ *
+ * @param sessionInitIQ The {@link JingleIQ} that created the session that
+ * we are handling here.
+ */
+ protected synchronized void processSessionInitiate(JingleIQ sessionInitIQ)
+ {
+ // Do initiate the session.
+ this.sessionInitIQ = sessionInitIQ;
+ this.initiator = true;
+
+ // This is the SDP offer that came from the initial session-initiate.
+ // Contrary to SIP, we are guaranteed to have content because XEP-0166
+ // says: "A session consists of at least one content type at a time."
+ List<ContentPacketExtension> offer = sessionInitIQ.getContentList();
+
+ try
+ {
+ getMediaHandler().processOffer(offer);
+
+ CoinPacketExtension coin = null;
+
+ for(PacketExtension ext : sessionInitIQ.getExtensions())
+ {
+ if(ext.getElementName().equals(
+ CoinPacketExtension.ELEMENT_NAME))
+ {
+ coin = (CoinPacketExtension)ext;
+ break;
+ }
+ }
+
+ /* does the call peer acts as a conference focus ? */
+ if(coin != null)
+ {
+ setConferenceFocus(Boolean.parseBoolean(
+ (String)coin.getAttribute("isfocus")));
+ }
+ }
+ catch(Exception ex)
+ {
+ logger.info("Failed to process an incoming session initiate", ex);
+
+ //send an error response;
+ String reasonText = "Error: " + ex.getMessage();
+ JingleIQ errResp
+ = JinglePacketFactory.createSessionTerminate(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(),
+ Reason.INCOMPATIBLE_PARAMETERS,
+ reasonText);
+
+ setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+ return;
+ }
+
+ // If we do not get the info about the remote peer yet. Get it right
+ // now.
+ if(this.getDiscoveryInfo() == null)
+ {
+ String calleeURI = sessionInitIQ.getFrom();
+ retrieveDiscoveryInfo(calleeURI);
+ }
+
+ //send a ringing response
+ if (logger.isTraceEnabled())
+ logger.trace("will send ringing response: ");
+
+ getProtocolProvider().getConnection().sendPacket(
+ JinglePacketFactory.createRinging(sessionInitIQ));
+
+ synchronized(sessionInitiateSyncRoot)
+ {
+ sessionInitiateProcessed = true;
+ sessionInitiateSyncRoot.notify();
+ }
+
+ //if this is a 3264 initiator, let's give them an early peek at our
+ //answer so that they could start ICE (SIP-2-Jingle gateways won't
+ //be able to send their candidates unless they have this)
+ DiscoverInfo discoverInfo = getDiscoveryInfo();
+ if ((discoverInfo != null)
+ && discoverInfo.containsFeature(
+ ProtocolProviderServiceJabberImpl.URN_IETF_RFC_3264))
+ {
+ getProtocolProvider().getConnection().sendPacket(
+ JinglePacketFactory.createDescriptionInfo(
+ sessionInitIQ.getTo(),
+ sessionInitIQ.getFrom(),
+ sessionInitIQ.getSID(),
+ getMediaHandler().getLocalContentList()));
+ }
+ }
+
+ /**
+ * Puts this peer into a {@link CallPeerState#DISCONNECTED}, indicating a
+ * reason to the user, if there is one.
+ *
+ * @param jingleIQ the {@link JingleIQ} that's terminating our session.
+ */
+ public void processSessionTerminate(JingleIQ jingleIQ)
+ {
+ String reasonStr = "Call ended by remote side.";
+ ReasonPacketExtension reasonExt = jingleIQ.getReason();
+
+ if(reasonExt != null)
+ {
+ Reason reason = reasonExt.getReason();
+
+ if(reason != null)
+ reasonStr += " Reason: " + reason.toString() + ".";
+
+ String text = reasonExt.getText();
+
+ if(text != null)
+ reasonStr += " " + text;
+ }
+
+ setState(CallPeerState.DISCONNECTED, reasonStr);
+ }
+
+ /**
+ * Processes a specific "XEP-0251: Jingle Session Transfer"
+ * <tt>transfer</tt> packet (extension).
+ *
+ * @param transfer the "XEP-0251: Jingle Session Transfer" transfer packet
+ * (extension) to process
+ * @throws OperationFailedException if anything goes wrong while processing
+ * the specified <tt>transfer</tt> packet (extension)
+ */
+ public void processTransfer(TransferPacketExtension transfer)
+ throws OperationFailedException
+ {
+ String attendantAddress = transfer.getFrom();
+
+ if (attendantAddress == null)
+ {
+ throw new OperationFailedException(
+ "Session transfer must contain a \'from\' attribute value.",
+ OperationFailedException.ILLEGAL_ARGUMENT);
+ }
+
+ String calleeAddress = transfer.getTo();
+
+ if (calleeAddress == null)
+ {
+ throw new OperationFailedException(
+ "Session transfer must contain a \'to\' attribute value.",
+ OperationFailedException.ILLEGAL_ARGUMENT);
+ }
+
+ // Checks if the transfer remote peer is contained by the roster of this
+ // account.
+ Roster roster = getProtocolProvider().getConnection().getRoster();
+ if(!roster.contains(StringUtils.parseBareAddress(calleeAddress)))
+ {
+ String failedMessage =
+ "Transfer impossible:\n"
+ + "Account roster does not contain transfer peer: "
+ + StringUtils.parseBareAddress(calleeAddress);
+ setState(CallPeerState.FAILED, failedMessage);
+ logger.info(failedMessage);
+ }
+
+ OperationSetBasicTelephonyJabberImpl basicTelephony
+ = (OperationSetBasicTelephonyJabberImpl)
+ getProtocolProvider()
+ .getOperationSet(OperationSetBasicTelephony.class);
+ CallJabberImpl calleeCall = new CallJabberImpl(basicTelephony);
+ TransferPacketExtension calleeTransfer = new TransferPacketExtension();
+ String sid = transfer.getSID();
+
+ calleeTransfer.setFrom(attendantAddress);
+ if (sid != null)
+ {
+ calleeTransfer.setSID(sid);
+ calleeTransfer.setTo(calleeAddress);
+ }
+ basicTelephony.createOutgoingCall(
+ calleeCall,
+ calleeAddress,
+ Arrays.asList(new PacketExtension[] { calleeTransfer }));
+ }
+
+ /**
+ * Processes the <tt>transport-info</tt> {@link JingleIQ}.
+ *
+ * @param jingleIQ the <tt>transport-info</tt> {@link JingleIQ} to process
+ */
+ public void processTransportInfo(JingleIQ jingleIQ)
+ {
+ /*
+ * The transport-info action is used to exchange transport candidates so
+ * it only concerns the mediaHandler.
+ */
+ try
+ {
+ if(isInitiator())
+ {
+ synchronized(sessionInitiateSyncRoot)
+ {
+ if(!sessionInitiateProcessed)
+ {
+ try
+ {
+ sessionInitiateSyncRoot.wait();
+ }
+ catch(InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+
+ getMediaHandler().processTransportInfo(
+ jingleIQ.getContentList());
+ }
+ catch (OperationFailedException ofe)
+ {
+ logger.warn("Failed to process an incoming transport-info", ofe);
+
+ //send an error response
+ String reasonText = "Error: " + ofe.getMessage();
+ JingleIQ errResp
+ = JinglePacketFactory.createSessionTerminate(
+ getProtocolProvider().getOurJID(),
+ peerJID,
+ sessionInitIQ.getSID(),
+ Reason.GENERAL_ERROR,
+ reasonText);
+
+ setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+
+ return;
+ }
+
+ synchronized(candSyncRoot)
+ {
+ candSyncRoot.notify();
+ }
+ }
+
+ /**
+ * Puts the <tt>CallPeer</tt> represented by this instance on or off hold.
+ *
+ * @param onHold <tt>true</tt> to have the <tt>CallPeer</tt> put on hold;
+ * <tt>false</tt>, otherwise
+ *
+ * @throws OperationFailedException if we fail to construct or send the
+ * INVITE request putting the remote side on/off hold.
+ */
+ public void putOnHold(boolean onHold)
+ throws OperationFailedException
+ {
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
+ mediaHandler.setLocallyOnHold(onHold);
+
+ SessionInfoType type;
+
+ if(onHold)
+ type = SessionInfoType.hold;
+ else
+ {
+ type = SessionInfoType.unhold;
+ getMediaHandler().reinitAllContents();
+ }
+
+ //we are now on hold and need to realize this before potentially
+ //spoiling it all with an exception while sending the packet :).
+ reevalLocalHoldStatus();
+
+ JingleIQ onHoldIQ = JinglePacketFactory.createSessionInfo(
+ getProtocolProvider().getOurJID(),
+ peerJID,
+ getSID(),
+ type);
+
+ getProtocolProvider().getConnection().sendPacket(onHoldIQ);
+ }
+
+ /**
+ * Send a <tt>content-add</tt> to add video setup.
+ */
+ private void sendAddVideoContent()
+ {
+ List<ContentPacketExtension> contents;
+
+ try
+ {
+ contents = getMediaHandler().createContentList(MediaType.VIDEO);
+ }
+ catch(Exception exc)
+ {
+ logger.warn("Failed to gather content for video type", exc);
+ return;
+ }
+
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+ JingleIQ contentIQ
+ = JinglePacketFactory.createContentAdd(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ getSID(),
+ contents);
+
+ protocolProvider.getConnection().sendPacket(contentIQ);
+ }
+
+ /**
+ * Sends a <tt>content</tt> message to reflect changes in the setup such as
+ * the local peer/user becoming a conference focus.
+ */
+ public void sendCoinSessionInfo()
+ {
+ JingleIQ sessionInfoIQ
+ = JinglePacketFactory.createSessionInfo(
+ getProtocolProvider().getOurJID(),
+ this.peerJID,
+ getSID());
+ CoinPacketExtension coinExt
+ = new CoinPacketExtension(getCall().isConferenceFocus());
+
+ sessionInfoIQ.addExtension(coinExt);
+ getProtocolProvider().getConnection().sendPacket(sessionInfoIQ);
+ }
+
+ /**
+ * Returns the <tt>MediaDirection</tt> that should be set for the content
+ * of type <tt>mediaType</tt> in the Jingle session for this
+ * <tt>CallPeer</tt>.
+ * If we are the focus of a conference and are doing RTP translation,
+ * takes into account the other <tt>CallPeer</tt>s in the <tt>Call</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> for which to return the
+ * <tt>MediaDirection</tt>
+ * @return the <tt>MediaDirection</tt> that should be used for the content
+ * of type <tt>mediaType</tt> in the Jingle session for this
+ * <tt>CallPeer</tt>.
+ */
+ private MediaDirection getDirectionForJingle(MediaType mediaType)
+ {
+ MediaDirection direction = MediaDirection.INACTIVE;
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
+ // If we are streaming media, the direction should allow sending
+ if ( (MediaType.AUDIO == mediaType &&
+ mediaHandler.isLocalAudioTransmissionEnabled()) ||
+ (MediaType.VIDEO == mediaType &&
+ isLocalVideoStreaming()))
+ direction = direction.or(MediaDirection.SENDONLY);
+
+ // If we are receiving media from this CallPeer, the direction should
+ // allow receiving
+ SendersEnum senders = getSenders(mediaType);
+ if (senders == null || senders == SendersEnum.both ||
+ (isInitiator() && senders == SendersEnum.initiator) ||
+ (!isInitiator() && senders == SendersEnum.responder))
+ direction = direction.or(MediaDirection.RECVONLY);
+
+ // If we are the focus of a conference and we are receiving media from
+ // another CallPeer in the same Call, the direction should allow sending
+ CallJabberImpl call = getCall();
+ if (call != null && call.isConferenceFocus())
+ {
+ for (CallPeerJabberImpl peer : call.getCallPeerList())
+ {
+ if (peer != this)
+ {
+ senders = peer.getSenders(mediaType);
+ if (senders == null || senders == SendersEnum.both ||
+ (peer.isInitiator()
+ && senders == SendersEnum.initiator) ||
+ (!peer.isInitiator()
+ && senders == SendersEnum.responder))
+ {
+ direction = direction.or(MediaDirection.SENDONLY);
+ break;
+ }
+ }
+ }
+ }
+
+ return direction;
+ }
+
+ /**
+ * Send, if necessary, a jingle <tt>content</tt> message to reflect change
+ * in video setup. Whether the jingle session should have a video content,
+ * and if so, the value of the <tt>senders</tt> field is determined
+ * based on whether we are streaming local video and, if we are the focus
+ * of a conference, on the other peers in the conference.
+ * The message can be content-modify if video content exists (and the
+ * <tt>senders</tt> field changes), content-add or content-remove.
+ *
+ * @return <tt>true</tt> if a jingle <tt>content</tt> message was sent.
+ */
+ public boolean sendModifyVideoContent()
+ {
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+ MediaDirection direction = getDirectionForJingle(MediaType.VIDEO);
+
+ ContentPacketExtension remoteContent
+ = mediaHandler.getLocalContent(MediaType.VIDEO.toString());
+
+ if (remoteContent == null)
+ {
+ if (direction == MediaDirection.INACTIVE)
+ {
+ // no video content, none needed
+ return false;
+ }
+ else
+ {
+ if (getState() == CallPeerState.CONNECTED)
+ {
+ if (logger.isInfoEnabled())
+ logger.info("Adding video content for " + this);
+ sendAddVideoContent();
+ return true;
+ }
+ return false;
+ }
+ }
+ else
+ {
+ if (direction == MediaDirection.INACTIVE)
+ {
+ sendRemoveVideoContent();
+ return true;
+ }
+ }
+
+ SendersEnum senders = getSenders(MediaType.VIDEO);
+ if (senders == null)
+ senders = SendersEnum.both;
+
+ SendersEnum newSenders = SendersEnum.none;
+ if (MediaDirection.SENDRECV == direction)
+ newSenders = SendersEnum.both;
+ else if (MediaDirection.RECVONLY == direction)
+ newSenders = isInitiator()
+ ? SendersEnum.initiator : SendersEnum.responder;
+ else if (MediaDirection.SENDONLY == direction)
+ newSenders = isInitiator()
+ ? SendersEnum.responder : SendersEnum.initiator;
+
+ /*
+ * Send Content-Modify
+ */
+ ContentPacketExtension ext = new ContentPacketExtension();
+ String remoteContentName = remoteContent.getName();
+
+ ext.setSenders(newSenders);
+ ext.setCreator(remoteContent.getCreator());
+ ext.setName(remoteContentName);
+
+ if (newSenders != senders)
+ {
+ if (logger.isInfoEnabled())
+ logger.info("Sending content modify, senders: "
+ + senders + "->" + newSenders);
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+ JingleIQ contentIQ
+ = JinglePacketFactory.createContentModify(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ getSID(),
+ ext);
+
+ protocolProvider.getConnection().sendPacket(contentIQ);
+ }
+
+ try
+ {
+ mediaHandler.reinitContent(remoteContentName, ext, false);
+ mediaHandler.start();
+ }
+ catch(Exception e)
+ {
+ logger.warn("Exception occurred during media reinitialization", e);
+ }
+
+ return (newSenders != senders);
+ }
+
+ /**
+ * Send a <tt>content</tt> message to reflect change in video setup (start
+ * or stop).
+ */
+ public void sendModifyVideoResolutionContent()
+ {
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+ ContentPacketExtension remoteContent
+ = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
+ ContentPacketExtension content;
+
+ logger.info("send modify-content to change resolution");
+
+ // send content-modify with RTP description
+
+ // create content list with resolution
+ try
+ {
+ content = mediaHandler.createContentForMedia(MediaType.VIDEO);
+ }
+ catch (Exception e)
+ {
+ logger.warn("Failed to gather content for video type", e);
+ return;
+ }
+
+ // if we are only receiving video senders is null
+ SendersEnum senders = remoteContent.getSenders();
+
+ if (senders != null)
+ content.setSenders(senders);
+
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+ JingleIQ contentIQ
+ = JinglePacketFactory.createContentModify(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ getSID(),
+ content);
+
+ protocolProvider.getConnection().sendPacket(contentIQ);
+
+ try
+ {
+ mediaHandler.reinitContent(remoteContent.getName(), content, false);
+ mediaHandler.start();
+ }
+ catch(Exception e)
+ {
+ logger.warn("Exception occurred when media reinitialization", e);
+ }
+ }
+
+ /**
+ * Send a <tt>content-remove</tt> to remove video setup.
+ */
+ private void sendRemoveVideoContent()
+ {
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+
+ ContentPacketExtension content = new ContentPacketExtension();
+ ContentPacketExtension remoteContent
+ = mediaHandler.getRemoteContent(MediaType.VIDEO.toString());
+ if (remoteContent == null)
+ return;
+ String remoteContentName = remoteContent.getName();
+
+ content.setName(remoteContentName);
+ content.setCreator(remoteContent.getCreator());
+ content.setSenders(remoteContent.getSenders());
+
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+ JingleIQ contentIQ
+ = JinglePacketFactory.createContentRemove(
+ protocolProvider.getOurJID(),
+ this.peerJID,
+ getSID(),
+ Arrays.asList(content));
+
+ protocolProvider.getConnection().sendPacket(contentIQ);
+ mediaHandler.removeContent(remoteContentName);
+ setSenders(MediaType.VIDEO, SendersEnum.none);
+ }
+
+ /**
+ * Sends local candidate addresses from the local peer to the remote peer
+ * using the <tt>transport-info</tt> {@link JingleIQ}.
+ *
+ * @param contents the local candidate addresses to be sent from the local
+ * peer to the remote peer using the <tt>transport-info</tt>
+ * {@link JingleIQ}
+ */
+ protected void sendTransportInfo(Iterable<ContentPacketExtension> contents)
+ {
+ // if the call is canceled, do not start sending candidates in
+ // transport-info
+ if(cancelled)
+ return;
+
+ JingleIQ transportInfo = new JingleIQ();
+
+ for (ContentPacketExtension content : contents)
+ transportInfo.addContent(content);
+
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+
+ transportInfo.setAction(JingleAction.TRANSPORT_INFO);
+ transportInfo.setFrom(protocolProvider.getOurJID());
+ transportInfo.setSID(getSID());
+ transportInfo.setTo(getAddress());
+ transportInfo.setType(IQ.Type.SET);
+
+ PacketCollector collector
+ = protocolProvider.getConnection().createPacketCollector(
+ new PacketIDFilter(transportInfo.getPacketID()));
+
+ protocolProvider.getConnection().sendPacket(transportInfo);
+ collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+ collector.cancel();
+ }
+
+ @Override
+ public void setState(CallPeerState newState, String reason, int reasonCode)
+ {
+ CallPeerState oldState = getState();
+ try
+ {
+ /*
+ * We need to dispose of the transport manager before the
+ * 'call' field is set to null, because if Jitsi Videobridge is in
+ * use, it (the call) is needed in order to expire the
+ * Videobridge channels.
+ */
+ if (CallPeerState.DISCONNECTED.equals(newState)
+ || CallPeerState.FAILED.equals(newState))
+ getMediaHandler().getTransportManager().close();
+ }
+ finally
+ {
+ super.setState(newState, reason, reasonCode);
+ }
+
+ if (CallPeerState.isOnHold(oldState)
+ && CallPeerState.CONNECTED.equals(newState))
+ {
+ try
+ {
+ getCall().modifyVideoContent();
+ }
+ catch (OperationFailedException ofe)
+ {
+ logger.error("Failed to update call video state after " +
+ "'hold' status removed for "+this);
+ }
+ }
+ }
+
+ /**
+ * Transfer (in the sense of call transfer) this <tt>CallPeer</tt> to a
+ * specific callee address which may optionally be participating in an
+ * active <tt>Call</tt>.
+ *
+ * @param to the address of the callee to transfer this <tt>CallPeer</tt> to
+ * @param sid the Jingle session ID of the active <tt>Call</tt> between the
+ * local peer and the callee in the case of attended transfer; <tt>null</tt>
+ * in the case of unattended transfer
+ * @throws OperationFailedException if something goes wrong
+ */
+ protected void transfer(String to, String sid)
+ throws OperationFailedException
+ {
+ JingleIQ transferSessionInfo = new JingleIQ();
+ ProtocolProviderServiceJabberImpl protocolProvider
+ = getProtocolProvider();
+
+ transferSessionInfo.setAction(JingleAction.SESSION_INFO);
+ transferSessionInfo.setFrom(protocolProvider.getOurJID());
+ transferSessionInfo.setSID(getSID());
+ transferSessionInfo.setTo(getAddress());
+ transferSessionInfo.setType(IQ.Type.SET);
+
+ TransferPacketExtension transfer = new TransferPacketExtension();
+
+ // Attended transfer.
+ if (sid != null)
+ {
+ /*
+ * Not really sure what the value of the "from" attribute of the
+ * "transfer" element should be but the examples in "XEP-0251:
+ * Jingle Session Transfer" has it in the case of attended transfer.
+ */
+ transfer.setFrom(protocolProvider.getOurJID());
+ transfer.setSID(sid);
+
+ // Puts on hold the 2 calls before making the attended transfer.
+ OperationSetBasicTelephonyJabberImpl basicTelephony
+ = (OperationSetBasicTelephonyJabberImpl)
+ protocolProvider.getOperationSet(
+ OperationSetBasicTelephony.class);
+ CallPeerJabberImpl callPeer = basicTelephony.getActiveCallPeer(sid);
+ if(callPeer != null)
+ {
+ if(!CallPeerState.isOnHold(callPeer.getState()))
+ {
+ callPeer.putOnHold(true);
+ }
+ }
+
+ if(!CallPeerState.isOnHold(this.getState()))
+ {
+ this.putOnHold(true);
+ }
+ }
+ transfer.setTo(to);
+
+ transferSessionInfo.addExtension(transfer);
+
+ Connection connection = protocolProvider.getConnection();
+ PacketCollector collector = connection.createPacketCollector(
+ new PacketIDFilter(transferSessionInfo.getPacketID()));
+ protocolProvider.getConnection().sendPacket(transferSessionInfo);
+
+ Packet result
+ = collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
+
+ if(result == null)
+ {
+ // Log the failed transfer call and notify the user.
+ throw new OperationFailedException(
+ "No response to the \"transfer\" request.",
+ OperationFailedException.ILLEGAL_ARGUMENT);
+ }
+ else if (((IQ) result).getType() != IQ.Type.RESULT)
+ {
+ // Log the failed transfer call and notify the user.
+ throw new OperationFailedException(
+ "Remote peer does not manage call \"transfer\"."
+ + "Response to the \"transfer\" request is: "
+ + ((IQ) result).getType(),
+ OperationFailedException.ILLEGAL_ARGUMENT);
+ }
+ else
+ {
+ String message = ((sid == null) ? "Unattended" : "Attended")
+ + " transfer to: "
+ + to;
+ // Implements the SIP behavior: once the transfer is accepted, the
+ // current call is closed.
+ hangup(
+ false,
+ message,
+ new ReasonPacketExtension(Reason.SUCCESS,
+ message,
+ new TransferredPacketExtension()));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getEntity()
+ {
+ return getAddress();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * In Jingle there isn't an actual "direction" parameter. We use the
+ * <tt>senders</tt> field to calculate the direction.
+ */
+ @Override
+ public MediaDirection getDirection(MediaType mediaType)
+ {
+ SendersEnum senders = getSenders(mediaType);
+
+ if (senders == SendersEnum.none)
+ {
+ return MediaDirection.INACTIVE;
+ }
+ else if (senders == null || senders == SendersEnum.both)
+ {
+ return MediaDirection.SENDRECV;
+ }
+ else if (senders == SendersEnum.initiator)
+ {
+ return
+ isInitiator()
+ ? MediaDirection.RECVONLY
+ : MediaDirection.SENDONLY;
+ }
+ else //senders == SendersEnum.responder
+ {
+ return
+ isInitiator()
+ ? MediaDirection.SENDONLY
+ : MediaDirection.RECVONLY;
+ }
+ }
+
+ /**
+ * Gets the current value of the <tt>senders</tt> field of the content with
+ * name <tt>mediaType</tt> in the Jingle session with this
+ * <tt>CallPeer</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> for which to get the current
+ * value of the <tt>senders</tt> field.
+ * @return the current value of the <tt>senders</tt> field of the content
+ * with name <tt>mediaType</tt> in the Jingle session with this
+ * <tt>CallPeer</tt>.
+ */
+ public SendersEnum getSenders(MediaType mediaType)
+ {
+ switch (mediaType)
+ {
+ case AUDIO:
+ return audioSenders;
+ case VIDEO:
+ return videoSenders;
+ default:
+ return SendersEnum.none;
+ }
+ }
+
+ /**
+ * Set the current value of the <tt>senders</tt> field of the content with
+ * name <tt>mediaType</tt> in the Jingle session with this <tt>CallPeer</tt>
+ * @param mediaType the <tt>MediaType</tt> for which to get the current
+ * value of the <tt>senders</tt> field.
+ * @param senders the value to set
+ */
+ public void setSenders(MediaType mediaType, SendersEnum senders)
+ {
+ switch(mediaType)
+ {
+ case AUDIO:
+ this.audioSenders = senders;
+ break;
+ case VIDEO:
+ this.videoSenders = senders;
+ break;
+ default:
+ throw new IllegalArgumentException("mediaType");
+ }
+ }
+
+ /**
+ * Gets the <tt>MediaType</tt> of <tt>content</tt>. If <tt>content</tt>
+ * does not have a <tt>description</tt> child and therefore not
+ * <tt>MediaType</tt> can be associated with it, tries to take the
+ * <tt>MediaType</tt> from the session's already established contents with
+ * the same name as <tt>content</tt>
+ * @param content the <tt>ContentPacketExtention</tt> for which to get the
+ * <tt>MediaType</tt>
+ * @return the <tt>MediaType</tt> of <tt>content</tt>.
+ */
+ public MediaType getMediaType(ContentPacketExtension content)
+ {
+ String contentName = content.getName();
+ if (contentName == null)
+ return null;
+
+ MediaType mediaType = JingleUtils.getMediaType(content);
+ if (mediaType == null)
+ {
+ CallPeerMediaHandlerJabberImpl mediaHandler = getMediaHandler();
+ for (MediaType m : MediaType.values())
+ {
+ ContentPacketExtension sessionContent
+ = mediaHandler.getRemoteContent(m.toString());
+ if (sessionContent == null)
+ sessionContent = mediaHandler.getLocalContent(m.toString());
+
+ if (sessionContent != null
+ && contentName.equals(sessionContent.getName()))
+ {
+ mediaType = m;
+ break;
+ }
+ }
+ }
+
+ return mediaType;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
index 2c8845d..979d696 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
@@ -937,19 +937,18 @@ public class CallPeerMediaHandlerJabberImpl
if (supportedTransports != null
&& supportedTransports.length > 0)
{
- for (int i = 0; i < supportedTransports.length; i++)
+ for(String supportedTransport : supportedTransports)
{
if (ProtocolProviderServiceJabberImpl.
URN_XMPP_JINGLE_ICE_UDP_1.
- equals(supportedTransports[i]))
+ equals(supportedTransport))
{
- transportManager
- = new IceUdpTransportManager(peer);
+ transportManager = new IceUdpTransportManager(peer);
break;
}
else if (ProtocolProviderServiceJabberImpl.
- URN_XMPP_JINGLE_RAW_UDP_0.
- equals(supportedTransports[i]))
+ URN_XMPP_JINGLE_RAW_UDP_0.
+ equals(supportedTransport))
{
transportManager
= new RawUdpTransportManager(peer);
@@ -1117,12 +1116,11 @@ public class CallPeerMediaHandlerJabberImpl
List<Component> visualComponents
= new LinkedList<Component>();
- for (int i = 0; i < remoteSSRCs.length; i++)
+ for(int remoteSSRC : remoteSSRCs)
{
- int remoteSSRC = remoteSSRCs[i];
Component visualComponent
- = videoStream.getVisualComponent(
- 0xFFFFFFFFL & remoteSSRC);
+ = videoStream.getVisualComponent(
+ 0xFFFFFFFFL & remoteSSRC);
if (visualComponent != null)
visualComponents.add(visualComponent);
@@ -1605,7 +1603,7 @@ public class CallPeerMediaHandlerJabberImpl
{
List<MediaFormat> fmts = supportedFormats;
- if(fmts.size() > 0)
+ if(!fmts.isEmpty())
{
MediaFormat fmt = fmts.get(0);
@@ -2108,21 +2106,17 @@ public class CallPeerMediaHandlerJabberImpl
* TODO The transportManager is going to be changed so it may need to be
* disposed of prior to the change.
*/
-
- if (xmlns.equals(
- ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_ICE_UDP_1))
+ switch (xmlns)
{
- transportManager = new IceUdpTransportManager(peer);
- }
- else if (xmlns.equals(
- ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0))
- {
- transportManager = new RawUdpTransportManager(peer);
- }
- else
- {
- throw new IllegalArgumentException(
- "Unsupported Jingle transport " + xmlns);
+ case ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_ICE_UDP_1:
+ transportManager = new IceUdpTransportManager(peer);
+ break;
+ case ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0:
+ transportManager = new RawUdpTransportManager(peer);
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported Jingle " +
+ "transport " + xmlns);
}
synchronized(transportManagerSyncRoot)
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java
index 8a6f3ed..6c4c358 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ChatRoomJabberImpl.java
@@ -609,7 +609,7 @@ public class ChatRoomJabberImpl
this.provider.getConnection().addPacketListener(
presenceListener,
new AndFilter(
- new FromMatchesFilter(multiUserChat.getRoom()),
+ FromMatchesFilter.create(multiUserChat.getRoom()),
new PacketTypeFilter(
org.jivesoftware.smack.packet.Presence.class)));
if(password == null)
@@ -868,7 +868,7 @@ public class ChatRoomJabberImpl
clearCachedConferenceDescriptionList();
- XMPPConnection connection = this.provider.getConnection();
+ Connection connection = this.provider.getConnection();
try
{
// if we are already disconnected
@@ -1896,6 +1896,23 @@ public class ChatRoomJabberImpl
}
/**
+ * Removes given <tt>PacketExtension</tt> from the MUC presence and
+ * publishes it immediately.
+ * @param extension the <tt>PacketExtension</tt> to be removed from the MUC
+ * presence.
+ */
+ public void removePresenceExtension(PacketExtension extension)
+ {
+ if (lastPresenceSent != null)
+ {
+ setPacketExtension(
+ lastPresenceSent, null, extension.getNamespace());
+
+ provider.getConnection().sendPacket(lastPresenceSent);
+ }
+ }
+
+ /**
* Returns the ids of the users that has the member role in the room.
* When the room is member only, this are the users allowed to join.
* @return the ids of the users that has the member role in the room.
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
index 1f4e3d8..c3c852c 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
@@ -58,11 +58,21 @@ public class IceUdpTransportManager
= Logger.getLogger(IceUdpTransportManager.class);
/**
+ * Default STUN server address.
+ */
+ protected static final String DEFAULT_STUN_SERVER_ADDRESS = "stun.jitsi.net";
+
+ /**
+ * Default STUN server port.
+ */
+ protected static final int DEFAULT_STUN_SERVER_PORT = 3478;
+
+ /**
* The ICE <tt>Component</tt> IDs in their common order used, for example,
* by <tt>DefaultStreamConnector</tt>, <tt>MediaStreamTarget</tt>.
*/
private static final int[] COMPONENT_IDS
- = new int[] { Component.RTP, Component.RTCP };
+ = new int[] { Component.RTP, Component.RTCP };
/**
* This is where we keep our answer between the time we get the offer and
@@ -76,15 +86,6 @@ public class IceUdpTransportManager
*/
protected final Agent iceAgent;
- /**
- * Default STUN server address.
- */
- protected static final String DEFAULT_STUN_SERVER_ADDRESS = "stun.jitsi.net";
-
- /**
- * Default STUN server port.
- */
- protected static final int DEFAULT_STUN_SERVER_PORT = 3478;
/**
* Creates a new instance of this transport manager, binding it to the
@@ -156,7 +157,10 @@ public class IceUdpTransportManager
// in case user has canceled the login window
if(credentials == null)
+ {
+ logger.info("Credentials were null. User has most likely canceled the login operation");
return null;
+ }
//extract the password the user passed us.
char[] pass = credentials.getPassword();
@@ -164,7 +168,10 @@ public class IceUdpTransportManager
// the user didn't provide us a password (i.e. canceled the
// operation)
if(pass == null)
+ {
+ logger.info("Password was null. User has most likely canceled the login operation");
return null;
+ }
password = new String(pass);
if (credentials.isPasswordPersistent())
@@ -390,28 +397,29 @@ public class IceUdpTransportManager
for (int i = 0; i < COMPONENT_IDS.length; i++)
{
Component component = stream.getComponent(COMPONENT_IDS[i]);
-
- if (component != null)
+ if (component == null)
{
- CandidatePair selectedPair = component.getSelectedPair();
-
- if (selectedPair != null)
- {
- DatagramSocket streamConnectorSocket
- = selectedPair.getLocalCandidate().
- getDatagramSocket();
+ continue;
+ }
- if (streamConnectorSocket != null)
- {
- streamConnectorSockets[i] = streamConnectorSocket;
- streamConnectorSocketCount++;
- }
- }
+ DatagramSocket streamConnectorSocket = component.getSocket();
+ if (streamConnectorSocket != null)
+ {
+ streamConnectorSockets[i] = streamConnectorSocket;
+ streamConnectorSocketCount++;
+ logger.trace("Added a streamConnectorSocket to the array " +
+ "StreamConnectorSocket and increased " +
+ "the count of streamConnectorSocketCount by one to " +
+ streamConnectorSocketCount);
}
}
+
if (streamConnectorSocketCount > 0)
+ {
return streamConnectorSockets;
+ }
}
+
return null;
}
@@ -742,20 +750,25 @@ public class IceUdpTransportManager
ex);
}
- //let's now update the next port var as best we can: we would assume
- //that all local candidates are bound on the same port and set it
- //to the one just above. if the assumption is wrong the next bind
- //would simply include one more bind retry.
+ // Attempt to minimize subsequent bind retries: see if we have allocated
+ // any ports from the dynamic range, and if so update the port tracker.
+ // Do NOT update the port tracker with non-dynamic ports (e.g. 4443
+ // coming from TCP) because this will force it to revert back it its
+ // configured min port. When maxPort is reached, allocation will begin
+ // from minPort again, so we don't have to worry about wraps.
try
{
- portTracker.setNextPort(
- 1
- + stream
- .getComponent(Component.RTCP)
- .getLocalCandidates()
- .get(0)
- .getTransportAddress()
- .getPort());
+ int maxAllocatedPort = getMaxAllocatedPort(
+ stream,
+ portTracker.getMinPort(),
+ portTracker.getMaxPort());
+
+ if(maxAllocatedPort > 0)
+ {
+ int nextPort = 1 + maxAllocatedPort;
+ portTracker.setNextPort(nextPort);
+ logger.debug("Updating the port tracker min port: " + nextPort);
+ }
}
catch(Throwable t)
{
@@ -768,6 +781,48 @@ public class IceUdpTransportManager
}
/**
+ * @return the highest local port used by any of the local candidates of
+ * {@code iceStream}, which falls in the range [{@code min}, {@code max}].
+ */
+ private int getMaxAllocatedPort(IceMediaStream iceStream, int min, int max)
+ {
+ return
+ Math.max(
+ getMaxAllocatedPort(
+ iceStream.getComponent(Component.RTP),
+ min, max),
+ getMaxAllocatedPort(
+ iceStream.getComponent(Component.RTCP),
+ min, max));
+ }
+
+ /**
+ * @return the highest local port used by any of the local candidates of
+ * {@code component}, which falls in the range [{@code min}, {@code max}].
+ */
+ private int getMaxAllocatedPort(Component component, int min, int max)
+ {
+ int maxAllocatedPort = -1;
+
+ if (component != null)
+ {
+ for (LocalCandidate candidate : component.getLocalCandidates())
+ {
+ int candidatePort = candidate.getTransportAddress().getPort();
+
+ if (min <= candidatePort
+ && candidatePort <= max
+ && maxAllocatedPort < candidatePort)
+ {
+ maxAllocatedPort = candidatePort;
+ }
+ }
+ }
+
+ return maxAllocatedPort;
+ }
+
+ /**
* Simply returns the list of local candidates that we gathered during the
* harvest.
*
@@ -898,8 +953,12 @@ public class IceUdpTransportManager
= transport.getChildExtensionsOfType(
CandidatePacketExtension.class);
- if (iceAgentStateIsRunning && (candidates.size() == 0))
+ if (iceAgentStateIsRunning && candidates.isEmpty())
+ {
+ logger.info("connectivity establishment has not been started " +
+ "because candidate list is empty");
return false;
+ }
String media = e.getKey();
IceMediaStream stream = iceAgent.getStream(media);
@@ -938,6 +997,12 @@ public class IceUdpTransportManager
if (candidate.getGeneration() != generation)
continue;
+ if (candidate.getIP() == null || "".equals(candidate.getIP()))
+ {
+ logger.warn("Skipped ICE candidate with empty IP");
+ continue;
+ }
+
Component component
= stream.getComponent(candidate.getComponent());
String relAddr;
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java b/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java
index a0387df..c3137ad 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/InfoRetreiver.java
@@ -151,7 +151,7 @@ public class InfoRetreiver
List<GenericDetail> result = new LinkedList<GenericDetail>();
try
{
- XMPPConnection connection = jabberProvider.getConnection();
+ Connection connection = jabberProvider.getConnection();
if(connection == null || !connection.isAuthenticated())
return null;
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java
index 438ea1b..de7578c 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberLoginStrategy.java
@@ -61,7 +61,7 @@ public interface JabberLoginStrategy
* @param resource the XMPP resource
* @return true to continue connecting, false to abort
*/
- public boolean login(XMPPConnection connection, String userName,
+ public boolean login(Connection connection, String userName,
String resource)
throws XMPPException;
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java
index 0ea9a54..f2473dd 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java
@@ -87,7 +87,7 @@ public class JingleNodesCandidate
* @return the <tt>RelayedCandidateDatagramSocket</tt> of this
* <tt>RelayedCandidate</tt>
*/
- public synchronized JingleNodesCandidateDatagramSocket
+ private synchronized JingleNodesCandidateDatagramSocket
getRelayedCandidateDatagramSocket()
{
if (jingleNodesCandidateDatagramSocket == null)
@@ -113,7 +113,7 @@ public class JingleNodesCandidate
* <tt>Candidate</tt>
*/
@Override
- public IceSocketWrapper getIceSocketWrapper()
+ protected IceSocketWrapper getCandidateIceSocketWrapper()
{
if (socket == null)
{
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java
index bb7d31b..d29be03 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java
@@ -36,7 +36,7 @@ import org.xmpp.jnodes.smack.*;
* @author Sebastien Vincent
*/
public class JingleNodesHarvester
- extends CandidateHarvester
+ extends AbstractCandidateHarvester
{
/**
* The <tt>Logger</tt> used by the <tt>JingleNodesHarvester</tt> class and
@@ -125,7 +125,7 @@ public class JingleNodesHarvester
}
}
- if (ciq != null && ciq.getRemoteport() > 0)
+ if (ciq != null)
{
ip = ciq.getHost();
port = ciq.getRemoteport();
@@ -136,6 +136,22 @@ public class JingleNodesHarvester
" local port: " + ciq.getLocalport());
}
+ if (ip == null || ciq.getRemoteport() == 0)
+ {
+ logger.warn("JN relay ignored because ip was null or port 0");
+ return candidates;
+ }
+
+ // Drop the scope or interface name if the relay sends it
+ // along in its IPv6 address. The scope/ifname is only valid on the
+ // host that owns the IP and we don't need it here.
+ int scopeIndex = ip.indexOf('%');
+ if (scopeIndex > 0)
+ {
+ logger.warn("Dropping scope from assumed IPv6 address " + ip);
+ ip = ip.substring(0, scopeIndex);
+ }
+
/* RTP */
TransportAddress relayedAddress = new TransportAddress(ip, port,
Transport.UDP);
@@ -160,6 +176,7 @@ public class JingleNodesHarvester
candidates.add(local);
}
}
+
return candidates;
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java b/src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java
index 09c9462..4825e01 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/LoginByClientCertificateStrategy.java
@@ -116,7 +116,7 @@ class LoginByClientCertificateStrategy
* accepted.
* @throws XMPPException
*/
- public boolean login(XMPPConnection connection, String userName,
+ public boolean login(Connection connection, String userName,
String resource)
throws XMPPException
{
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java b/src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java
index 43fc8a4..7034221 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/LoginByPasswordStrategy.java
@@ -115,7 +115,7 @@ public class LoginByPasswordStrategy
* @return always true.
* @throws XMPPException
*/
- public boolean login(XMPPConnection connection, String userName,
+ public boolean login(Connection connection, String userName,
String resource)
throws XMPPException
{
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java b/src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java
index 926848c..8c801c6 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/MobileIndicator.java
@@ -222,11 +222,13 @@ public class MobileIndicator
/**
* Caps for user has been changed.
* @param user the user (full JID)
+ * @param fullJids a list of all resources of the user (full JIDs)
* @param node the entity caps node#ver
* @param online indicates if the user for which we're notified is online
*/
@Override
- public void userCapsNodeAdded(String user, String node, boolean online)
+ public void userCapsNodeAdded(String user, ArrayList<String> fullJids,
+ String node, boolean online)
{
updateMobileIndicatorUsingCaps(user);
}
@@ -234,11 +236,13 @@ public class MobileIndicator
/**
* Caps for user has been changed.
* @param user the user (full JID)
+ * @param fullJids a list of all resources of the user (full JIDs)
* @param node the entity caps node#ver
* @param online indicates if the user for which we're notified is online
*/
@Override
- public void userCapsNodeRemoved(String user, String node, boolean online)
+ public void userCapsNodeRemoved(String user, ArrayList<String> fullJids,
+ String node, boolean online)
{
updateMobileIndicatorUsingCaps(user);
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java
index 42a3916..2f35fef 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java
@@ -815,6 +815,17 @@ public class OperationSetBasicInstantMessagingJabberImpl
ForwardedPacketExtension.class);
if(extensions.isEmpty())
return;
+
+ // according to xep-0280 all carbons should come from
+ // our bare jid
+ if (!msg.getFrom().equals(
+ StringUtils.parseBareAddress(
+ jabberProvider.getOurJID())))
+ {
+ logger.info("Received a carbon copy with wrong from!");
+ return;
+ }
+
ForwardedPacketExtension forwardedExt = extensions.get(0);
msg = forwardedExt.getMessage();
if(msg == null || msg.getBody() == null)
@@ -1109,7 +1120,7 @@ public class OperationSetBasicInstantMessagingJabberImpl
NewMailNotificationIQ.NAMESPACE,
new NewMailNotificationProvider());
- XMPPConnection connection = jabberProvider.getConnection();
+ Connection connection = jabberProvider.getConnection();
connection.addPacketListener(
new MailboxIQListener(), new PacketTypeFilter(MailboxIQ.class));
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
index cc36936..78027c0 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicTelephonyJabberImpl.java
@@ -280,7 +280,10 @@ public class OperationSetBasicTelephonyJabberImpl
Iterable<PacketExtension> sessionInitiateExtensions)
throws OperationFailedException
{
- return createOutgoingCall(call, calleeAddress, null, null);
+ if (calleeAddress.contains("/"))
+ return createOutgoingCall(call, calleeAddress, calleeAddress, null);
+ else
+ return createOutgoingCall(call, calleeAddress, null, null);
}
/**
@@ -774,7 +777,7 @@ public class OperationSetBasicTelephonyJabberImpl
*/
private void unsubscribeForJinglePackets()
{
- XMPPConnection connection = protocolProvider.getConnection();
+ Connection connection = protocolProvider.getConnection();
if(connection != null)
connection.removePacketListener(this);
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java
index 0a6edfe..59cc17c 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetContactCapabilitiesJabberImpl.java
@@ -26,7 +26,7 @@ import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.packet.*;
-import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.*;
/**
* Represents an <tt>OperationSet</tt> to query the <tt>OperationSet</tt>s
@@ -52,6 +52,19 @@ public class OperationSetContactCapabilitiesJabberImpl
= Logger.getLogger(OperationSetContactCapabilitiesJabberImpl.class);
/**
+ * The name of the property used to control whether to use
+ * all resources to show capabilities
+ */
+ public static final String PROP_XMPP_USE_ALL_RESOURCES_FOR_CAPABILITIES =
+ "net.java.sip.communicator.XMPP_USE_ALL_RESOURCES_FOR_CAPABILITIES";
+
+ /**
+ * The default value for the capabilities setting
+ */
+ public static final boolean USE_ALL_RESOURCES_FOR_CAPABILITIES_DEFAULT =
+ true;
+
+ /**
* The list of <tt>OperationSet</tt> capabilities presumed to be supported
* by a <tt>Contact</tt> when it is offline.
*/
@@ -276,6 +289,42 @@ public class OperationSetContactCapabilitiesJabberImpl
}
/**
+ * Gets the largest set of <tt>OperationSet</tt>s supported from a
+ * list of full JIDs. The returned <tt>OperationSet</tt>s are considered
+ * by the associated protocol provider to capabilities possessed by the
+ * specified <tt>contact</tt>.
+ *
+ * @param fullJids a list of full JIDs in which to find the resource with
+ * the most capabilities.
+ * @return the <tt>Map</tt> listing the most <tt>OperationSet</tt>s
+ * considered by the associated protocol provider to be supported by the
+ * specified <tt>contact</tt> (i.e. to be possessed as capabilities).
+ * Each supported <tt>OperationSet</tt> capability is represented by a
+ * <tt>Map.Entry</tt> with key equal to the <tt>OperationSet</tt> class
+ * name and value equal to the respective <tt>OperationSet</tt> instance
+ */
+ protected Map<String, OperationSet> getLargestSupportedOperationSet(
+ ArrayList<String> fullJids)
+ {
+ Map<String, OperationSet> supportedOperationSets =
+ new HashMap<String, OperationSet>();
+ if (fullJids!=null)
+ {
+ for (String fullJid : fullJids)
+ {
+ Map<String, OperationSet> newSupportedOperationSets=
+ getSupportedOperationSets(fullJid, true);
+ if (newSupportedOperationSets.size()>
+ supportedOperationSets.size())
+ {
+ supportedOperationSets = newSupportedOperationSets;
+ }
+ }
+ }
+ return supportedOperationSets;
+ }
+
+ /**
* Gets the <tt>OperationSet</tt> corresponding to the specified
* <tt>Class</tt> and supported by the specified <tt>Contact</tt>. If the
* returned value is non-<tt>null</tt>, it indicates that the
@@ -387,17 +436,19 @@ public class OperationSetContactCapabilitiesJabberImpl
* record for a specific user about the caps node the user has.
*
* @param user the user (full JID)
+ * @param fullJids a list of all resources of the user (full JIDs)
* @param node the entity caps node#ver
* @param online indicates if the user is currently online
* @see UserCapsNodeListener#userCapsNodeAdded(String, String, boolean)
*/
- public void userCapsNodeAdded(String user, String node, boolean online)
+ public void userCapsNodeAdded(String user, ArrayList<String> fullJids,
+ String node, boolean online)
{
/*
* It doesn't matter to us whether a caps node has been added or removed
* for the specified user because we report all changes.
*/
- userCapsNodeRemoved(user, node, online);
+ userCapsNodeChanged(user, fullJids, node, online);
}
/**
@@ -405,45 +456,86 @@ public class OperationSetContactCapabilitiesJabberImpl
* record for a specific user about the caps node the user has.
*
* @param user the user (full JID)
+ * @param fullJids a list of all resources of the user (full JIDs)
+ * @param node the entity caps node#ver
+ * @param online indicates if the user is currently online
+ * @see UserCapsNodeListener#userCapsNodeAdded(String, String, boolean)
+ */
+ public void userCapsNodeRemoved(String user, ArrayList<String> fullJids,
+ String node, boolean online)
+ {
+ /*
+ * It doesn't matter to us whether a caps node has been added or removed
+ * for the specified user because we report all changes.
+ */
+ userCapsNodeChanged(user, fullJids, node, online);
+ }
+
+ /**
+ * Notifies this listener that an <tt>EntityCapsManager</tt> has changed a
+ * record for a specific user about the caps node the user has.
+ *
+ * @param user the user (full JID)
+ * @param fullJids a list of all resources of the user (full JIDs)
* @param node the entity caps node#ver
* @param online indicates if the given user is online
- * @see UserCapsNodeListener#userCapsNodeRemoved(String, String, boolean)
*/
- public void userCapsNodeRemoved(String user, String node, boolean online)
+ public void userCapsNodeChanged(String user, ArrayList<String> fullJids,
+ String node, boolean online)
{
OperationSetPresence opsetPresence
- = parentProvider.getOperationSet(OperationSetPresence.class);
-
- if (opsetPresence != null)
- {
- String jid = StringUtils.parseBareAddress(user);
- Contact contact = opsetPresence.findContactByID(jid);
-
- // If the contact isn't null and is online we try to discover the
- // new set of operation sets and to notify interested parties.
- // Otherwise we ignore the event.
- if (contact != null)
+ = parentProvider.getOperationSet(OperationSetPresence.class);
+ if (opsetPresence != null) {
+ if(JabberActivator.getConfigurationService()
+ .getBoolean(
+ PROP_XMPP_USE_ALL_RESOURCES_FOR_CAPABILITIES,
+ USE_ALL_RESOURCES_FOR_CAPABILITIES_DEFAULT)
+ && !fullJids.isEmpty())
{
- if(online)
+ String bareJid = StringUtils.parseBareAddress(user);
+ Contact contact = opsetPresence.findContactByID(bareJid);
+ if (contact != null)
{
- // when going online we have received a presence
- // and make sure we discover this particular jid
- // for getSupportedOperationSets
fireContactCapabilitiesEvent(
contact,
- ContactCapabilitiesEvent.SUPPORTED_OPERATION_SETS_CHANGED,
- getSupportedOperationSets(user,
- online));
+ ContactCapabilitiesEvent.
+ SUPPORTED_OPERATION_SETS_CHANGED,
+ getLargestSupportedOperationSet(fullJids));
}
- else
+ }
+ else
+ {
+ String jid = StringUtils.parseBareAddress(user);
+ Contact contact = opsetPresence.findContactByID(jid);
+
+ // If the contact isn't null and is online we try to discover
+ // the new set of operation sets and to notify interested
+ // parties. Otherwise we ignore the event.
+ if (contact != null)
{
- // when offline, we use the contact, and selecting
- // the most connected jid
- // for getSupportedOperationSets
- fireContactCapabilitiesEvent(
- contact,
- ContactCapabilitiesEvent.SUPPORTED_OPERATION_SETS_CHANGED,
- getSupportedOperationSets(contact));
+ if(online)
+ {
+ // when going online we have received a presence
+ // and make sure we discover this particular jid
+ // for getSupportedOperationSets
+ fireContactCapabilitiesEvent(
+ contact,
+ ContactCapabilitiesEvent.
+ SUPPORTED_OPERATION_SETS_CHANGED,
+ getSupportedOperationSets(user,
+ online));
+ }
+ else
+ {
+ // when offline, we use the contact, and selecting
+ // the most connected jid
+ // for getSupportedOperationSets
+ fireContactCapabilitiesEvent(
+ contact,
+ ContactCapabilitiesEvent.
+ SUPPORTED_OPERATION_SETS_CHANGED,
+ getSupportedOperationSets(contact));
+ }
}
}
}
@@ -460,7 +552,8 @@ public class OperationSetContactCapabilitiesJabberImpl
{
// If the user goes offline we ensure to remove the caps node.
if (capsManager != null
- && evt.getNewStatus().getStatus() < PresenceStatus.ONLINE_THRESHOLD)
+ && evt.getNewStatus().getStatus() < PresenceStatus.ONLINE_THRESHOLD
+ && !evt.isResourceChanged())
{
capsManager.removeContactCapsNode(evt.getSourceContact());
}
@@ -469,31 +562,59 @@ public class OperationSetContactCapabilitiesJabberImpl
/**
* Fires event that contact capabilities has changed.
* @param user the user to search for its contact.
+ * @param fullJids a list of all resources of the user (full JIDs)
*/
- public void fireContactCapabilitiesChanged(String user)
+ public void fireContactCapabilitiesChanged(String user,
+ ArrayList<String> fullJids)
{
- OperationSetPresence opsetPresence
+ if(!JabberActivator.getConfigurationService()
+ .getBoolean(
+ PROP_XMPP_USE_ALL_RESOURCES_FOR_CAPABILITIES,
+ USE_ALL_RESOURCES_FOR_CAPABILITIES_DEFAULT)
+ || fullJids.isEmpty())
+ {
+ OperationSetPresence opsetPresence
= parentProvider.getOperationSet(OperationSetPresence.class);
- if (opsetPresence != null)
+ if (opsetPresence != null)
+ {
+ String userID = StringUtils.parseBareAddress(user);
+ Contact contact = opsetPresence.findContactByID(userID);
+
+ // this called by received discovery info for particular jid
+ // so we use its online and opsets for this particular jid
+ boolean online = false;
+ Presence presence = parentProvider.getConnection().getRoster()
+ .getPresence(user);
+ if(presence != null)
+ online = presence.isAvailable();
+
+ if(contact != null)
+ {
+ fireContactCapabilitiesEvent(
+ contact,
+ ContactCapabilitiesEvent.
+ SUPPORTED_OPERATION_SETS_CHANGED,
+ getSupportedOperationSets(user, online));
+ }
+ }
+ }
+ else
{
- String userID = StringUtils.parseBareAddress(user);
- Contact contact = opsetPresence.findContactByID(userID);
-
- // this called by received discovery info for particular jid
- // so we use its online and opsets for this particular jid
- boolean online = false;
- Presence presence = parentProvider.getConnection().getRoster()
- .getPresence(user);
- if(presence != null)
- online = presence.isAvailable();
-
- if(contact != null)
+ OperationSetPresence opsetPresence
+ = parentProvider.getOperationSet(OperationSetPresence.class);
+ if (opsetPresence != null)
{
- fireContactCapabilitiesEvent(
- contact,
- ContactCapabilitiesEvent.SUPPORTED_OPERATION_SETS_CHANGED,
- getSupportedOperationSets(user, online));
+ String bareJid = StringUtils.parseBareAddress(user);
+ Contact contact = opsetPresence.findContactByID(bareJid);
+ if(contact != null)
+ {
+ fireContactCapabilitiesEvent(
+ contact,
+ ContactCapabilitiesEvent.
+ SUPPORTED_OPERATION_SETS_CHANGED,
+ getLargestSupportedOperationSet(fullJids));
+ }
}
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java
index 0fb6979..7ea4453 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetJitsiMeetToolsJabberImpl.java
@@ -72,6 +72,16 @@ public class OperationSetJitsiMeetToolsJabberImpl
* {@inheritDoc}
*/
@Override
+ public void removePresenceExtension(ChatRoom chatRoom,
+ PacketExtension extension)
+ {
+ ((ChatRoomJabberImpl)chatRoom).removePresenceExtension(extension);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void setPresenceStatus(ChatRoom chatRoom, String statusMessage)
{
((ChatRoomJabberImpl)chatRoom).publishPresenceStatus(statusMessage);
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java
index 80bbb4e..cf53906 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetMultiUserChatJabberImpl.java
@@ -471,10 +471,10 @@ public class OperationSetMultiUserChatJabberImpl
* Almost all <tt>MultiUserChat</tt> methods require an xmpp connection
* param so I added this method only for the sake of utility.
*
- * @return the XMPPConnection currently in use by the jabber provider or
+ * @return the XMPP connection currently in use by the jabber provider or
* null if jabber provider has yet to be initialized.
*/
- private XMPPConnection getXmppConnection()
+ private Connection getXmppConnection()
{
return (jabberProvider == null)
? null
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java
index 69c168c..c502824 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetPersistentPresenceJabberImpl.java
@@ -691,7 +691,7 @@ public class OperationSetPersistentPresenceJabberImpl
*/
assertConnected();
- XMPPConnection xmppConnection = parentProvider.getConnection();
+ Connection xmppConnection = parentProvider.getConnection();
if (xmppConnection == null)
{
@@ -1124,7 +1124,7 @@ public class OperationSetPersistentPresenceJabberImpl
ssContactList.cleanup();
- XMPPConnection connection = parentProvider.getConnection();
+ Connection connection = parentProvider.getConnection();
if(connection != null)
{
connection.removePacketListener(subscribtionPacketListener);
@@ -1528,6 +1528,19 @@ public class OperationSetPersistentPresenceJabberImpl
o2, parentProvider).getStatus()
- jabberStatusToPresenceStatus(
o1, parentProvider).getStatus();
+ // We have run out of "logical" ways to order
+ // the presences inside the TreeSet. We have
+ // make sure we are consinstent with equals.
+ // We do this by comparing the unique resource
+ // names. If this evaluates to 0 again, then we
+ // can safely assume this presence object
+ // represents the same resource and by that the
+ // same client.
+ if(res == 0)
+ {
+ res = o1.getFrom().compareTo(
+ o2.getFrom());
+ }
}
return res;
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java
index e81b43a..e5b83e7 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetTelephonyConferencingJabberImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,585 +15,585 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.service.protocol.media.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.util.xml.*;
-import org.jivesoftware.smack.*;
-import org.jivesoftware.smack.filter.*;
-import org.jivesoftware.smack.packet.*;
-import org.jivesoftware.smack.packet.IQ.Type;
-import org.jivesoftware.smack.util.*;
-import org.jivesoftware.smackx.packet.*;
-
-/**
- * Implements <tt>OperationSetTelephonyConferencing</tt> for Jabber.
- *
- * @author Lyubomir Marinov
- * @author Sebastien Vincent
- * @author Boris Grozev
- * @author Pawel Domas
- */
-public class OperationSetTelephonyConferencingJabberImpl
- extends AbstractOperationSetTelephonyConferencing<
- ProtocolProviderServiceJabberImpl,
- OperationSetBasicTelephonyJabberImpl,
- CallJabberImpl,
- CallPeerJabberImpl,
- String>
- implements RegistrationStateChangeListener,
- PacketListener,
- PacketFilter
-
-{
- /**
- * The <tt>Logger</tt> used by the
- * <tt>OperationSetTelephonyConferencingJabberImpl</tt> class and its
- * instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(OperationSetTelephonyConferencingJabberImpl.class);
-
- /**
- * The minimum interval in milliseconds between COINs sent to a single
- * <tt>CallPeer</tt>.
- */
- private static final int COIN_MIN_INTERVAL = 200;
-
- /**
- * Property used to disable COIN notifications.
- */
- public static final String DISABLE_COIN_PROP_NAME
- = "net.java.sip.communicator.impl.protocol.jabber.DISABLE_COIN";
-
- /**
- * Synchronization object.
- */
- private final Object lock = new Object();
-
- /**
- * Field indicates whether COIN notification are disabled or not.
- */
- private boolean isCoinDisabled = false;
-
- /**
- * Initializes a new <tt>OperationSetTelephonyConferencingJabberImpl</tt>
- * instance which is to provide telephony conferencing services for the
- * specified Jabber <tt>ProtocolProviderService</tt> implementation.
- *
- * @param parentProvider the Jabber <tt>ProtocolProviderService</tt>
- * implementation which has requested the creation of the new instance and
- * for which the new instance is to provide telephony conferencing services
- */
- public OperationSetTelephonyConferencingJabberImpl(
- ProtocolProviderServiceJabberImpl parentProvider)
- {
- super(parentProvider);
-
- this.isCoinDisabled
- = JabberActivator.getConfigurationService()
- .getBoolean(DISABLE_COIN_PROP_NAME, false);
- }
-
- /**
- * Notifies all <tt>CallPeer</tt>s associated with a specific <tt>Call</tt>
- * about changes in the telephony conference-related information. In
- * contrast, {@link #notifyAll()} notifies all <tt>CallPeer</tt>s associated
- * with the telephony conference in which a specific <tt>Call</tt> is
- * participating.
- *
- * @param call the <tt>Call</tt> whose <tt>CallPeer</tt>s are to be notified
- * about changes in the telephony conference-related information
- */
- @Override
- protected void notifyCallPeers(Call call)
- {
- if (!isCoinDisabled && call.isConferenceFocus())
- {
- synchronized (lock)
- {
- // send conference-info to all CallPeers of the specified call.
- for (Iterator<? extends CallPeer> i = call.getCallPeers();
- i.hasNext();)
- {
- notify(i.next());
- }
- }
- }
- }
-
- /**
- * Notifies a specific <tt>CallPeer</tt> about changes in the telephony
- * conference-related information.
- *
- * @param callPeer the <tt>CallPeer</tt> to notify.
- */
- private void notify(CallPeer callPeer)
- {
- if(!(callPeer instanceof CallPeerJabberImpl))
- return;
-
- //Don't send COINs to peers with might not be ready to accept COINs yet
- CallPeerState peerState = callPeer.getState();
- if (peerState == CallPeerState.CONNECTING
- || peerState == CallPeerState.UNKNOWN
- || peerState == CallPeerState.INITIATING_CALL
- || peerState == CallPeerState.DISCONNECTED
- || peerState == CallPeerState.FAILED)
- return;
-
- final CallPeerJabberImpl callPeerJabber = (CallPeerJabberImpl)callPeer;
-
- final long timeSinceLastCoin = System.currentTimeMillis()
- - callPeerJabber.getLastConferenceInfoSentTimestamp();
- if (timeSinceLastCoin < COIN_MIN_INTERVAL)
- {
- if (callPeerJabber.isConfInfoScheduled())
- return;
-
- logger.info("Scheduling to send a COIN to " + callPeerJabber);
- callPeerJabber.setConfInfoScheduled(true);
- new Thread(new Runnable(){
- @Override
- public void run()
- {
- try
- {
- Thread.sleep(1 + COIN_MIN_INTERVAL - timeSinceLastCoin);
- }
- catch (InterruptedException ie) {}
-
- OperationSetTelephonyConferencingJabberImpl.this
- .notify(callPeerJabber);
- }
- }).start();
-
- return;
- }
-
- // check that callPeer supports COIN before sending him a
- // conference-info
- String to = getBasicTelephony().getFullCalleeURI(callPeer.getAddress());
-
- // XXX if this generates actual disco#info requests we might want to
- // cache it.
- try
- {
- DiscoverInfo discoverInfo
- = parentProvider.getDiscoveryManager().discoverInfo(to);
-
- if (!discoverInfo.containsFeature(
- ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_COIN))
- {
- logger.info(callPeer.getAddress() + " does not support COIN");
- callPeerJabber.setConfInfoScheduled(false);
- return;
- }
- }
- catch (XMPPException xmppe)
- {
- logger.warn("Failed to retrieve DiscoverInfo for " + to, xmppe);
- }
-
- ConferenceInfoDocument currentConfInfo
- = getCurrentConferenceInfo(callPeerJabber);
- ConferenceInfoDocument lastSentConfInfo
- = callPeerJabber.getLastConferenceInfoSent();
-
- ConferenceInfoDocument diff;
-
- if (lastSentConfInfo == null)
- diff = currentConfInfo;
- else
- diff = getConferenceInfoDiff(lastSentConfInfo, currentConfInfo);
-
- if (diff != null)
- {
- int newVersion
- = lastSentConfInfo == null
- ? 1
- : lastSentConfInfo.getVersion() + 1;
- diff.setVersion(newVersion);
-
- IQ iq = getConferenceInfo(callPeerJabber, diff);
-
- if (iq != null)
- {
- parentProvider.getConnection().sendPacket(iq);
-
- // We save currentConfInfo, because it is of state "full", while
- // diff could be a partial
- currentConfInfo.setVersion(newVersion);
- callPeerJabber.setLastConferenceInfoSent(currentConfInfo);
- callPeerJabber.setLastConferenceInfoSentTimestamp(
- System.currentTimeMillis());
- }
- }
- callPeerJabber.setConfInfoScheduled(false);
- }
-
- /**
- * Generates the conference-info IQ to be sent to a specific
- * <tt>CallPeer</tt> in order to notify it of the current state of the
- * conference managed by the local peer.
- *
- * @param callPeer the <tt>CallPeer</tt> to generate conference-info XML for
- * @param confInfo the <tt>ConferenceInformationDocument</tt> which is to be
- * included in the IQ
- * @return the conference-info IQ to be sent to the specified
- * <tt>callPeer</tt> in order to notify it of the current state of the
- * conference managed by the local peer
- */
- private IQ getConferenceInfo(CallPeerJabberImpl callPeer,
- final ConferenceInfoDocument confInfo)
- {
- String callPeerSID = callPeer.getSID();
-
- if (callPeerSID == null)
- return null;
-
- IQ iq = new IQ(){
- @Override
- public String getChildElementXML()
- {
- return confInfo.toXml();
- }
- };
-
- CallJabberImpl call = callPeer.getCall();
-
- iq.setFrom(call.getProtocolProvider().getOurJID());
- iq.setTo(callPeer.getAddress());
- iq.setType(Type.SET);
-
- return iq;
- }
-
- /**
- * Implementation of method <tt>registrationStateChange</tt> from
- * interface RegistrationStateChangeListener for setting up (or down)
- * our <tt>JingleManager</tt> when an <tt>XMPPConnection</tt> is available
- *
- * @param evt the event received
- */
- @Override
- public void registrationStateChanged(RegistrationStateChangeEvent evt)
- {
- super.registrationStateChanged(evt);
-
- RegistrationState registrationState = evt.getNewState();
-
- if (RegistrationState.REGISTERED.equals(registrationState))
- {
- if(logger.isDebugEnabled())
- logger.debug("Subscribes to Coin packets");
- subscribeForCoinPackets();
- }
- else if (RegistrationState.UNREGISTERED.equals(registrationState))
- {
- if(logger.isDebugEnabled())
- logger.debug("Unsubscribes to Coin packets");
- unsubscribeForCoinPackets();
- }
- }
-
- /**
- * Creates a new outgoing <tt>Call</tt> into which conference callees are to
- * be invited by this <tt>OperationSetTelephonyConferencing</tt>.
- *
- * @return a new outgoing <tt>Call</tt> into which conference callees are to
- * be invited by this <tt>OperationSetTelephonyConferencing</tt>
- * @throws OperationFailedException if anything goes wrong
- */
- @Override
- protected CallJabberImpl createOutgoingCall()
- throws OperationFailedException
- {
- return new CallJabberImpl(getBasicTelephony());
- }
-
- /**
- * {@inheritDoc}
- *
- * Implements the protocol-dependent part of the logic of inviting a callee
- * to a <tt>Call</tt>. The protocol-independent part of that logic is
- * implemented by
- * {@link AbstractOperationSetTelephonyConferencing#inviteCalleeToCall(String,Call)}.
- */
- @Override
- protected CallPeer doInviteCalleeToCall(
- String calleeAddress,
- CallJabberImpl call)
- throws OperationFailedException
- {
- return
- getBasicTelephony().createOutgoingCall(
- call,
- calleeAddress,
- Arrays.asList(
- new PacketExtension[]
- {
- new CoinPacketExtension(true)
- }));
- }
-
- /**
- * Parses a <tt>String</tt> value which represents a callee address
- * specified by the user into an object which is to actually represent the
- * callee during the invitation to a conference <tt>Call</tt>.
- *
- * @param calleeAddressString a <tt>String</tt> value which represents a
- * callee address to be parsed into an object which is to actually represent
- * the callee during the invitation to a conference <tt>Call</tt>
- * @return an object which is to actually represent the specified
- * <tt>calleeAddressString</tt> during the invitation to a conference
- * <tt>Call</tt>
- * @throws OperationFailedException if parsing the specified
- * <tt>calleeAddressString</tt> fails
- */
- @Override
- protected String parseAddressString(String calleeAddressString)
- throws OperationFailedException
- {
- return getBasicTelephony().getFullCalleeURI(calleeAddressString);
- }
-
- /**
- * Subscribes us to notifications about incoming Coin packets.
- */
- private void subscribeForCoinPackets()
- {
- parentProvider.getConnection().addPacketListener(this, this);
- }
-
- /**
- * Unsubscribes us from notifications about incoming Coin packets.
- */
- private void unsubscribeForCoinPackets()
- {
- XMPPConnection connection = parentProvider.getConnection();
-
- if (connection != null)
- connection.removePacketListener(this);
- }
-
- /**
- * Tests whether or not the specified packet should be handled by this
- * operation set. This method is called by smack prior to packet delivery
- * and it would only accept <tt>CoinIQ</tt>s.
- *
- * @param packet the packet to test.
- * @return true if and only if <tt>packet</tt> passes the filter.
- */
- public boolean accept(Packet packet)
- {
- return (packet instanceof CoinIQ);
- }
-
- /**
- * Handles incoming jingle packets and passes them to the corresponding
- * method based on their action.
- *
- * @param packet the packet to process.
- */
- public void processPacket(Packet packet)
- {
- CoinIQ coinIQ = (CoinIQ) packet;
- String errorMessage = null;
-
- //first ack all "set" requests.
- IQ.Type type = coinIQ.getType();
- if (type == IQ.Type.SET)
- {
- IQ ack = IQ.createResultIQ(coinIQ);
-
- parentProvider.getConnection().sendPacket(ack);
- }
- else if(type == IQ.Type.ERROR)
- {
- XMPPError error = coinIQ.getError();
- if(error != null)
- {
- String msg = error.getMessage();
- errorMessage = ((msg != null)? (msg + " ") : "")
- + "Error code: " + error.getCode();
- }
-
- logger.error("Received error in COIN packet. "+errorMessage);
- }
-
- String sid = coinIQ.getSID();
-
- if (sid != null)
- {
- CallPeerJabberImpl callPeer
- = getBasicTelephony().getActiveCallsRepository().findCallPeer(
- sid);
-
-
- if (callPeer != null)
- {
- if(type == IQ.Type.ERROR)
- {
- callPeer.fireConferenceMemberErrorEvent(errorMessage);
- return;
- }
-
- if (logger.isDebugEnabled())
- logger.debug("Processing COIN from " + coinIQ.getFrom()
- + " (version=" + coinIQ.getVersion() + ")");
-
- handleCoin(callPeer, coinIQ);
- }
- }
- }
-
- /**
- * Handles a specific <tt>CoinIQ</tt> sent from a specific
- * <tt>CallPeer</tt>.
- *
- * @param callPeer the <tt>CallPeer</tt> from which the specified
- * <tt>CoinIQ</tt> was sent
- * @param coinIQ the <tt>CoinIQ</tt> which was sent from the specified
- * <tt>callPeer</tt>
- */
- private void handleCoin(CallPeerJabberImpl callPeer, CoinIQ coinIQ)
- {
- try
- {
- setConferenceInfoXML(callPeer, coinIQ.getChildElementXML());
- }
- catch (XMLException e)
- {
- logger.error("Could not handle received COIN from " + callPeer
- + ": " + coinIQ);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * For COINs (XEP-0298), we use the attributes of the
- * <tt>conference-info</tt> element to piggyback a Jingle SID. This is
- * temporary and should be removed once we choose a better way to pass the
- * SID.
- */
- @Override
- protected ConferenceInfoDocument getCurrentConferenceInfo(
- MediaAwareCallPeer<?,?,?> callPeer)
- {
- ConferenceInfoDocument confInfo
- = super.getCurrentConferenceInfo(callPeer);
-
- if (callPeer instanceof CallPeerJabberImpl
- && confInfo != null)
- {
- confInfo.setSid(((CallPeerJabberImpl)callPeer).getSID());
- }
- return confInfo;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected String getLocalEntity(CallPeer callPeer)
- {
- JingleIQ sessionIQ = ((CallPeerJabberImpl)callPeer).getSessionIQ();
- String from = sessionIQ.getFrom();
- String chatRoomName = StringUtils.parseBareAddress(from);
- OperationSetMultiUserChatJabberImpl opSetMUC
- = (OperationSetMultiUserChatJabberImpl)
- parentProvider.getOperationSet(OperationSetMultiUserChat.class);
- ChatRoom room = null;
- if(opSetMUC != null)
- room = opSetMUC.getChatRoom(chatRoomName);
-
- if(room != null)
- return "xmpp:" + chatRoomName + "/" + room.getUserNickname();
-
- return "xmpp:" + parentProvider.getOurJID();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected String getLocalDisplayName()
- {
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * The URI of the returned <tt>ConferenceDescription</tt> is the occupant
- * JID with which we have joined the room.
- *
- * If a Videobridge is available for our <tt>ProtocolProviderService</tt>
- * we use it. TODO: this should be relaxed when we refactor the Videobridge
- * implementation, so that any Videobridge (on any protocol provider) can
- * be used.
- */
- @Override
- public ConferenceDescription setupConference(final ChatRoom chatRoom)
- {
- OperationSetVideoBridge videoBridge
- = parentProvider.getOperationSet(OperationSetVideoBridge.class);
- boolean isVideobridge = (videoBridge != null) && videoBridge.isActive();
-
- CallJabberImpl call = new CallJabberImpl(getBasicTelephony());
- call.setAutoAnswer(true);
-
- String uri = "xmpp:" + chatRoom.getIdentifier() +
- "/" + chatRoom.getUserNickname();
-
- ConferenceDescription cd
- = new ConferenceDescription(uri, call.getCallID());
-
- call.addCallChangeListener(new CallChangeListener()
- {
- @Override
- public void callStateChanged(CallChangeEvent ev)
- {
- if(CallState.CALL_ENDED.equals(ev.getNewValue()))
- chatRoom.publishConference(null, null);
- }
-
- @Override
- public void callPeerRemoved(CallPeerEvent ev)
- {
- }
-
- @Override
- public void callPeerAdded(CallPeerEvent ev)
- {
- }
- });
- if (isVideobridge)
- {
- call.setConference(new MediaAwareCallConference(true));
-
- //For Jitsi Videobridge we set the transports to RAW-UDP, otherwise
- //we leave them empty (meaning both RAW-UDP and ICE could be used)
- cd.addTransport(
- ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0);
- }
-
- if (logger.isInfoEnabled())
- {
- logger.info("Setup a conference with uri=" + uri + " and callid=" +
- call.getCallID() + ". Videobridge in use: " + isVideobridge);
- }
-
- return cd;
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.util.*;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*;
+import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.media.*;
+import net.java.sip.communicator.util.*;
+
+import org.jitsi.util.xml.*;
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.packet.IQ.Type;
+import org.jivesoftware.smack.util.*;
+import org.jivesoftware.smackx.packet.*;
+
+/**
+ * Implements <tt>OperationSetTelephonyConferencing</tt> for Jabber.
+ *
+ * @author Lyubomir Marinov
+ * @author Sebastien Vincent
+ * @author Boris Grozev
+ * @author Pawel Domas
+ */
+public class OperationSetTelephonyConferencingJabberImpl
+ extends AbstractOperationSetTelephonyConferencing<
+ ProtocolProviderServiceJabberImpl,
+ OperationSetBasicTelephonyJabberImpl,
+ CallJabberImpl,
+ CallPeerJabberImpl,
+ String>
+ implements RegistrationStateChangeListener,
+ PacketListener,
+ PacketFilter
+
+{
+ /**
+ * The <tt>Logger</tt> used by the
+ * <tt>OperationSetTelephonyConferencingJabberImpl</tt> class and its
+ * instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(OperationSetTelephonyConferencingJabberImpl.class);
+
+ /**
+ * The minimum interval in milliseconds between COINs sent to a single
+ * <tt>CallPeer</tt>.
+ */
+ private static final int COIN_MIN_INTERVAL = 200;
+
+ /**
+ * Property used to disable COIN notifications.
+ */
+ public static final String DISABLE_COIN_PROP_NAME
+ = "net.java.sip.communicator.impl.protocol.jabber.DISABLE_COIN";
+
+ /**
+ * Synchronization object.
+ */
+ private final Object lock = new Object();
+
+ /**
+ * Field indicates whether COIN notification are disabled or not.
+ */
+ private boolean isCoinDisabled = false;
+
+ /**
+ * Initializes a new <tt>OperationSetTelephonyConferencingJabberImpl</tt>
+ * instance which is to provide telephony conferencing services for the
+ * specified Jabber <tt>ProtocolProviderService</tt> implementation.
+ *
+ * @param parentProvider the Jabber <tt>ProtocolProviderService</tt>
+ * implementation which has requested the creation of the new instance and
+ * for which the new instance is to provide telephony conferencing services
+ */
+ public OperationSetTelephonyConferencingJabberImpl(
+ ProtocolProviderServiceJabberImpl parentProvider)
+ {
+ super(parentProvider);
+
+ this.isCoinDisabled
+ = JabberActivator.getConfigurationService()
+ .getBoolean(DISABLE_COIN_PROP_NAME, false);
+ }
+
+ /**
+ * Notifies all <tt>CallPeer</tt>s associated with a specific <tt>Call</tt>
+ * about changes in the telephony conference-related information. In
+ * contrast, {@link #notifyAll()} notifies all <tt>CallPeer</tt>s associated
+ * with the telephony conference in which a specific <tt>Call</tt> is
+ * participating.
+ *
+ * @param call the <tt>Call</tt> whose <tt>CallPeer</tt>s are to be notified
+ * about changes in the telephony conference-related information
+ */
+ @Override
+ protected void notifyCallPeers(Call call)
+ {
+ if (!isCoinDisabled && call.isConferenceFocus())
+ {
+ synchronized (lock)
+ {
+ // send conference-info to all CallPeers of the specified call.
+ for (Iterator<? extends CallPeer> i = call.getCallPeers();
+ i.hasNext();)
+ {
+ notify(i.next());
+ }
+ }
+ }
+ }
+
+ /**
+ * Notifies a specific <tt>CallPeer</tt> about changes in the telephony
+ * conference-related information.
+ *
+ * @param callPeer the <tt>CallPeer</tt> to notify.
+ */
+ private void notify(CallPeer callPeer)
+ {
+ if(!(callPeer instanceof CallPeerJabberImpl))
+ return;
+
+ //Don't send COINs to peers with might not be ready to accept COINs yet
+ CallPeerState peerState = callPeer.getState();
+ if (peerState == CallPeerState.CONNECTING
+ || peerState == CallPeerState.UNKNOWN
+ || peerState == CallPeerState.INITIATING_CALL
+ || peerState == CallPeerState.DISCONNECTED
+ || peerState == CallPeerState.FAILED)
+ return;
+
+ final CallPeerJabberImpl callPeerJabber = (CallPeerJabberImpl)callPeer;
+
+ final long timeSinceLastCoin = System.currentTimeMillis()
+ - callPeerJabber.getLastConferenceInfoSentTimestamp();
+ if (timeSinceLastCoin < COIN_MIN_INTERVAL)
+ {
+ if (callPeerJabber.isConfInfoScheduled())
+ return;
+
+ logger.info("Scheduling to send a COIN to " + callPeerJabber);
+ callPeerJabber.setConfInfoScheduled(true);
+ new Thread(new Runnable(){
+ @Override
+ public void run()
+ {
+ try
+ {
+ Thread.sleep(1 + COIN_MIN_INTERVAL - timeSinceLastCoin);
+ }
+ catch (InterruptedException ie) {}
+
+ OperationSetTelephonyConferencingJabberImpl.this
+ .notify(callPeerJabber);
+ }
+ }).start();
+
+ return;
+ }
+
+ // check that callPeer supports COIN before sending him a
+ // conference-info
+ String to = getBasicTelephony().getFullCalleeURI(callPeer.getAddress());
+
+ // XXX if this generates actual disco#info requests we might want to
+ // cache it.
+ try
+ {
+ DiscoverInfo discoverInfo
+ = parentProvider.getDiscoveryManager().discoverInfo(to);
+
+ if (!discoverInfo.containsFeature(
+ ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_COIN))
+ {
+ logger.info(callPeer.getAddress() + " does not support COIN");
+ callPeerJabber.setConfInfoScheduled(false);
+ return;
+ }
+ }
+ catch (XMPPException xmppe)
+ {
+ logger.warn("Failed to retrieve DiscoverInfo for " + to, xmppe);
+ }
+
+ ConferenceInfoDocument currentConfInfo
+ = getCurrentConferenceInfo(callPeerJabber);
+ ConferenceInfoDocument lastSentConfInfo
+ = callPeerJabber.getLastConferenceInfoSent();
+
+ ConferenceInfoDocument diff;
+
+ if (lastSentConfInfo == null)
+ diff = currentConfInfo;
+ else
+ diff = getConferenceInfoDiff(lastSentConfInfo, currentConfInfo);
+
+ if (diff != null)
+ {
+ int newVersion
+ = lastSentConfInfo == null
+ ? 1
+ : lastSentConfInfo.getVersion() + 1;
+ diff.setVersion(newVersion);
+
+ IQ iq = getConferenceInfo(callPeerJabber, diff);
+
+ if (iq != null)
+ {
+ parentProvider.getConnection().sendPacket(iq);
+
+ // We save currentConfInfo, because it is of state "full", while
+ // diff could be a partial
+ currentConfInfo.setVersion(newVersion);
+ callPeerJabber.setLastConferenceInfoSent(currentConfInfo);
+ callPeerJabber.setLastConferenceInfoSentTimestamp(
+ System.currentTimeMillis());
+ }
+ }
+ callPeerJabber.setConfInfoScheduled(false);
+ }
+
+ /**
+ * Generates the conference-info IQ to be sent to a specific
+ * <tt>CallPeer</tt> in order to notify it of the current state of the
+ * conference managed by the local peer.
+ *
+ * @param callPeer the <tt>CallPeer</tt> to generate conference-info XML for
+ * @param confInfo the <tt>ConferenceInformationDocument</tt> which is to be
+ * included in the IQ
+ * @return the conference-info IQ to be sent to the specified
+ * <tt>callPeer</tt> in order to notify it of the current state of the
+ * conference managed by the local peer
+ */
+ private IQ getConferenceInfo(CallPeerJabberImpl callPeer,
+ final ConferenceInfoDocument confInfo)
+ {
+ String callPeerSID = callPeer.getSID();
+
+ if (callPeerSID == null)
+ return null;
+
+ IQ iq = new IQ(){
+ @Override
+ public String getChildElementXML()
+ {
+ return confInfo.toXml();
+ }
+ };
+
+ CallJabberImpl call = callPeer.getCall();
+
+ iq.setFrom(call.getProtocolProvider().getOurJID());
+ iq.setTo(callPeer.getAddress());
+ iq.setType(Type.SET);
+
+ return iq;
+ }
+
+ /**
+ * Implementation of method <tt>registrationStateChange</tt> from
+ * interface RegistrationStateChangeListener for setting up (or down)
+ * our <tt>JingleManager</tt> when an <tt>XMPPConnection</tt> is available
+ *
+ * @param evt the event received
+ */
+ @Override
+ public void registrationStateChanged(RegistrationStateChangeEvent evt)
+ {
+ super.registrationStateChanged(evt);
+
+ RegistrationState registrationState = evt.getNewState();
+
+ if (RegistrationState.REGISTERED.equals(registrationState))
+ {
+ if(logger.isDebugEnabled())
+ logger.debug("Subscribes to Coin packets");
+ subscribeForCoinPackets();
+ }
+ else if (RegistrationState.UNREGISTERED.equals(registrationState))
+ {
+ if(logger.isDebugEnabled())
+ logger.debug("Unsubscribes to Coin packets");
+ unsubscribeForCoinPackets();
+ }
+ }
+
+ /**
+ * Creates a new outgoing <tt>Call</tt> into which conference callees are to
+ * be invited by this <tt>OperationSetTelephonyConferencing</tt>.
+ *
+ * @return a new outgoing <tt>Call</tt> into which conference callees are to
+ * be invited by this <tt>OperationSetTelephonyConferencing</tt>
+ * @throws OperationFailedException if anything goes wrong
+ */
+ @Override
+ protected CallJabberImpl createOutgoingCall()
+ throws OperationFailedException
+ {
+ return new CallJabberImpl(getBasicTelephony());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Implements the protocol-dependent part of the logic of inviting a callee
+ * to a <tt>Call</tt>. The protocol-independent part of that logic is
+ * implemented by
+ * {@link AbstractOperationSetTelephonyConferencing#inviteCalleeToCall(String,Call)}.
+ */
+ @Override
+ protected CallPeer doInviteCalleeToCall(
+ String calleeAddress,
+ CallJabberImpl call)
+ throws OperationFailedException
+ {
+ return
+ getBasicTelephony().createOutgoingCall(
+ call,
+ calleeAddress,
+ Arrays.asList(
+ new PacketExtension[]
+ {
+ new CoinPacketExtension(true)
+ }));
+ }
+
+ /**
+ * Parses a <tt>String</tt> value which represents a callee address
+ * specified by the user into an object which is to actually represent the
+ * callee during the invitation to a conference <tt>Call</tt>.
+ *
+ * @param calleeAddressString a <tt>String</tt> value which represents a
+ * callee address to be parsed into an object which is to actually represent
+ * the callee during the invitation to a conference <tt>Call</tt>
+ * @return an object which is to actually represent the specified
+ * <tt>calleeAddressString</tt> during the invitation to a conference
+ * <tt>Call</tt>
+ * @throws OperationFailedException if parsing the specified
+ * <tt>calleeAddressString</tt> fails
+ */
+ @Override
+ protected String parseAddressString(String calleeAddressString)
+ throws OperationFailedException
+ {
+ return getBasicTelephony().getFullCalleeURI(calleeAddressString);
+ }
+
+ /**
+ * Subscribes us to notifications about incoming Coin packets.
+ */
+ private void subscribeForCoinPackets()
+ {
+ parentProvider.getConnection().addPacketListener(this, this);
+ }
+
+ /**
+ * Unsubscribes us from notifications about incoming Coin packets.
+ */
+ private void unsubscribeForCoinPackets()
+ {
+ Connection connection = parentProvider.getConnection();
+
+ if (connection != null)
+ connection.removePacketListener(this);
+ }
+
+ /**
+ * Tests whether or not the specified packet should be handled by this
+ * operation set. This method is called by smack prior to packet delivery
+ * and it would only accept <tt>CoinIQ</tt>s.
+ *
+ * @param packet the packet to test.
+ * @return true if and only if <tt>packet</tt> passes the filter.
+ */
+ public boolean accept(Packet packet)
+ {
+ return (packet instanceof CoinIQ);
+ }
+
+ /**
+ * Handles incoming jingle packets and passes them to the corresponding
+ * method based on their action.
+ *
+ * @param packet the packet to process.
+ */
+ public void processPacket(Packet packet)
+ {
+ CoinIQ coinIQ = (CoinIQ) packet;
+ String errorMessage = null;
+
+ //first ack all "set" requests.
+ IQ.Type type = coinIQ.getType();
+ if (type == IQ.Type.SET)
+ {
+ IQ ack = IQ.createResultIQ(coinIQ);
+
+ parentProvider.getConnection().sendPacket(ack);
+ }
+ else if(type == IQ.Type.ERROR)
+ {
+ XMPPError error = coinIQ.getError();
+ if(error != null)
+ {
+ String msg = error.getMessage();
+ errorMessage = ((msg != null)? (msg + " ") : "")
+ + "Error code: " + error.getCode();
+ }
+
+ logger.error("Received error in COIN packet. "+errorMessage);
+ }
+
+ String sid = coinIQ.getSID();
+
+ if (sid != null)
+ {
+ CallPeerJabberImpl callPeer
+ = getBasicTelephony().getActiveCallsRepository().findCallPeer(
+ sid);
+
+
+ if (callPeer != null)
+ {
+ if(type == IQ.Type.ERROR)
+ {
+ callPeer.fireConferenceMemberErrorEvent(errorMessage);
+ return;
+ }
+
+ if (logger.isDebugEnabled())
+ logger.debug("Processing COIN from " + coinIQ.getFrom()
+ + " (version=" + coinIQ.getVersion() + ")");
+
+ handleCoin(callPeer, coinIQ);
+ }
+ }
+ }
+
+ /**
+ * Handles a specific <tt>CoinIQ</tt> sent from a specific
+ * <tt>CallPeer</tt>.
+ *
+ * @param callPeer the <tt>CallPeer</tt> from which the specified
+ * <tt>CoinIQ</tt> was sent
+ * @param coinIQ the <tt>CoinIQ</tt> which was sent from the specified
+ * <tt>callPeer</tt>
+ */
+ private void handleCoin(CallPeerJabberImpl callPeer, CoinIQ coinIQ)
+ {
+ try
+ {
+ setConferenceInfoXML(callPeer, coinIQ.getChildElementXML());
+ }
+ catch (XMLException e)
+ {
+ logger.error("Could not handle received COIN from " + callPeer
+ + ": " + coinIQ);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * For COINs (XEP-0298), we use the attributes of the
+ * <tt>conference-info</tt> element to piggyback a Jingle SID. This is
+ * temporary and should be removed once we choose a better way to pass the
+ * SID.
+ */
+ @Override
+ protected ConferenceInfoDocument getCurrentConferenceInfo(
+ MediaAwareCallPeer<?,?,?> callPeer)
+ {
+ ConferenceInfoDocument confInfo
+ = super.getCurrentConferenceInfo(callPeer);
+
+ if (callPeer instanceof CallPeerJabberImpl
+ && confInfo != null)
+ {
+ confInfo.setSid(((CallPeerJabberImpl)callPeer).getSID());
+ }
+ return confInfo;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String getLocalEntity(CallPeer callPeer)
+ {
+ JingleIQ sessionIQ = ((CallPeerJabberImpl)callPeer).getSessionIQ();
+ String from = sessionIQ.getFrom();
+ String chatRoomName = StringUtils.parseBareAddress(from);
+ OperationSetMultiUserChatJabberImpl opSetMUC
+ = (OperationSetMultiUserChatJabberImpl)
+ parentProvider.getOperationSet(OperationSetMultiUserChat.class);
+ ChatRoom room = null;
+ if(opSetMUC != null)
+ room = opSetMUC.getChatRoom(chatRoomName);
+
+ if(room != null)
+ return "xmpp:" + chatRoomName + "/" + room.getUserNickname();
+
+ return "xmpp:" + parentProvider.getOurJID();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected String getLocalDisplayName()
+ {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The URI of the returned <tt>ConferenceDescription</tt> is the occupant
+ * JID with which we have joined the room.
+ *
+ * If a Videobridge is available for our <tt>ProtocolProviderService</tt>
+ * we use it. TODO: this should be relaxed when we refactor the Videobridge
+ * implementation, so that any Videobridge (on any protocol provider) can
+ * be used.
+ */
+ @Override
+ public ConferenceDescription setupConference(final ChatRoom chatRoom)
+ {
+ OperationSetVideoBridge videoBridge
+ = parentProvider.getOperationSet(OperationSetVideoBridge.class);
+ boolean isVideobridge = (videoBridge != null) && videoBridge.isActive();
+
+ CallJabberImpl call = new CallJabberImpl(getBasicTelephony());
+ call.setAutoAnswer(true);
+
+ String uri = "xmpp:" + chatRoom.getIdentifier() +
+ "/" + chatRoom.getUserNickname();
+
+ ConferenceDescription cd
+ = new ConferenceDescription(uri, call.getCallID());
+
+ call.addCallChangeListener(new CallChangeListener()
+ {
+ @Override
+ public void callStateChanged(CallChangeEvent ev)
+ {
+ if(CallState.CALL_ENDED.equals(ev.getNewValue()))
+ chatRoom.publishConference(null, null);
+ }
+
+ @Override
+ public void callPeerRemoved(CallPeerEvent ev)
+ {
+ }
+
+ @Override
+ public void callPeerAdded(CallPeerEvent ev)
+ {
+ }
+ });
+ if (isVideobridge)
+ {
+ call.setConference(new MediaAwareCallConference(true));
+
+ //For Jitsi Videobridge we set the transports to RAW-UDP, otherwise
+ //we leave them empty (meaning both RAW-UDP and ICE could be used)
+ cd.addTransport(
+ ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0);
+ }
+
+ if (logger.isInfoEnabled())
+ {
+ logger.info("Setup a conference with uri=" + uri + " and callid=" +
+ call.getCallID() + ". Videobridge in use: " + isVideobridge);
+ }
+
+ return cd;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java
index b7e13ea..5d3dd8b 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoBridgeImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,282 +15,282 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.service.protocol.media.*;
-import net.java.sip.communicator.util.*;
-
-import org.jivesoftware.smack.*;
-import org.jivesoftware.smack.filter.*;
-import org.jivesoftware.smack.packet.*;
-
-/**
- * Implements <tt>OperationSetVideoBridge</tt> for Jabber.
- *
- * @author Yana Stamcheva
- * @author Lyubomir Marinov
- */
-public class OperationSetVideoBridgeImpl
- implements OperationSetVideoBridge,
- PacketFilter,
- PacketListener,
- RegistrationStateChangeListener
-{
- /**
- * The <tt>Logger</tt> used by the <tt>OperationSetVideoBridgeImpl</tt>
- * class and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(OperationSetVideoBridgeImpl.class);
-
- /**
- * The <tt>ProtocolProviderService</tt> implementation which initialized
- * this instance, owns it and is often referred to as its parent.
- */
- private final ProtocolProviderServiceJabberImpl protocolProvider;
-
- /**
- * Creates an instance of <tt>OperationSetVideoBridgeImpl</tt> by
- * specifying the parent <tt>ProtocolProviderService</tt> announcing this
- * operation set.
- *
- * @param protocolProvider the parent Jabber protocol provider
- */
- public OperationSetVideoBridgeImpl(
- ProtocolProviderServiceJabberImpl protocolProvider)
- {
- this.protocolProvider = protocolProvider;
- this.protocolProvider.addRegistrationStateChangeListener(this);
- }
-
- /**
- * Implements {@link PacketFilter}. Determines whether this instance is
- * interested in a specific {@link Packet}.
- * <tt>OperationSetVideoBridgeImpl</tt> returns <tt>true</tt> if the
- * specified <tt>packet</tt> is a {@link ColibriConferenceIQ}; otherwise,
- * <tt>false</tt>.
- *
- * @param packet the <tt>Packet</tt> to be determined whether this instance
- * is interested in it
- * @return <tt>true</tt> if the specified <tt>packet</tt> is a
- * <tt>ColibriConferenceIQ</tt>; otherwise, <tt>false</tt>
- */
- public boolean accept(Packet packet)
- {
- return (packet instanceof ColibriConferenceIQ);
- }
-
- /**
- * Creates a conference call with the specified callees as call peers via a
- * video bridge provided by the parent Jabber provider.
- *
- * @param callees the list of addresses that we should call
- * @return the newly created conference call containing all CallPeers
- * @throws OperationFailedException if establishing the conference call
- * fails
- * @throws OperationNotSupportedException if the provider does not have any
- * conferencing features.
- */
- public Call createConfCall(String[] callees)
- throws OperationFailedException,
- OperationNotSupportedException
- {
- return
- protocolProvider
- .getOperationSet(OperationSetTelephonyConferencing.class)
- .createConfCall(
- callees,
- new MediaAwareCallConference(true));
- }
-
- /**
- * Invites the callee represented by the specified uri to an already
- * existing call using a video bridge provided by the parent Jabber provider.
- * The difference between this method and createConfCall is that
- * inviteCalleeToCall allows a user to add new peers to an already
- * established conference.
- *
- * @param uri the callee to invite to an existing conf call.
- * @param call the call that we should invite the callee to.
- * @return the CallPeer object corresponding to the callee represented by
- * the specified uri.
- * @throws OperationFailedException if inviting the specified callee to the
- * specified call fails
- * @throws OperationNotSupportedException if allowing additional callees to
- * a pre-established call is not supported.
- */
- public CallPeer inviteCalleeToCall(String uri, Call call)
- throws OperationFailedException,
- OperationNotSupportedException
- {
- return
- protocolProvider
- .getOperationSet(OperationSetTelephonyConferencing.class)
- .inviteCalleeToCall(uri, call);
- }
-
- /**
- * Indicates if there's an active video bridge available at this moment. The
- * Jabber provider may announce support for video bridge, but it should not
- * be used for calling until it becomes actually active.
- *
- * @return <tt>true</tt> to indicate that there's currently an active
- * available video bridge, <tt>false</tt> - otherwise
- */
- public boolean isActive()
- {
- String jitsiVideobridge = protocolProvider.getJitsiVideobridge();
-
- return ((jitsiVideobridge != null) && (jitsiVideobridge.length() > 0));
- }
-
- /**
- * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has
- * been received.
- *
- * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been
- * received
- */
- private void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ)
- {
- /*
- * The application is not a Jitsi Videobridge server, it is a client.
- * Consequently, the specified ColibriConferenceIQ is sent to it in
- * relation to the part of the application's functionality which makes
- * requests to a Jitsi Videobridge server i.e. CallJabberImpl.
- *
- * Additionally, the method processColibriConferenceIQ is presently tasked
- * with processing ColibriConferenceIQ requests only. They are SET IQs
- * sent by the Jitsi Videobridge server to notify the application about
- * updates in the states of (colibri) conferences organized by the
- * application.
- */
- if (IQ.Type.SET.equals(conferenceIQ.getType())
- && conferenceIQ.getID() != null)
- {
- OperationSetBasicTelephony<?> basicTelephony
- = protocolProvider.getOperationSet(
- OperationSetBasicTelephony.class);
-
- if (basicTelephony != null)
- {
- Iterator<? extends Call> i = basicTelephony.getActiveCalls();
-
- while (i.hasNext())
- {
- Call call = i.next();
-
- if (call instanceof CallJabberImpl)
- {
- CallJabberImpl callJabberImpl = (CallJabberImpl) call;
- MediaAwareCallConference conference
- = callJabberImpl.getConference();
-
- if ((conference != null)
- && conference.isJitsiVideobridge())
- {
- /*
- * TODO We may want to disallow rogue CallJabberImpl
- * instances which may throw an exception to prevent
- * the conferenceIQ from reaching the CallJabberImpl
- * instance which it was meant for.
- */
- if (callJabberImpl.processColibriConferenceIQ(
- conferenceIQ))
- break;
- }
- }
- }
- }
- }
- }
-
- /**
- * Implements {@link PacketListener}. Notifies this instance that a specific
- * {@link Packet} (which this instance has already expressed interest into
- * by returning <tt>true</tt> from {@link #accept(Packet)}) has been
- * received.
- *
- * @param packet the <tt>Packet</tt> which has been received and which this
- * instance is given a chance to process
- */
- public void processPacket(Packet packet)
- {
- /*
- * As we do elsewhere, acknowledge the receipt of the Packet first and
- * then go about our business with it.
- */
- IQ iq = (IQ) packet;
-
- if (iq.getType() == IQ.Type.SET)
- protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq));
-
- /*
- * Now that the acknowledging is out of the way, do go about our
- * business with the Packet.
- */
- ColibriConferenceIQ conferenceIQ = (ColibriConferenceIQ) iq;
- boolean interrupted = false;
-
- try
- {
- processColibriConferenceIQ(conferenceIQ);
- }
- catch (Throwable t)
- {
- logger.error(
- "An error occurred during the processing of a "
- + packet.getClass().getName() + " packet",
- t);
-
- if (t instanceof InterruptedException)
- {
- /*
- * We cleared the interrupted state of the current Thread by
- * catching the InterruptedException. However, we do not really
- * care whether the current Thread has been interrupted - we
- * caught the InterruptedException because we want to swallow
- * any Throwable. Consequently, we should better restore the
- * interrupted state.
- */
- interrupted = true;
- }
- else if (t instanceof ThreadDeath)
- throw (ThreadDeath) t;
- }
- if (interrupted)
- Thread.currentThread().interrupt();
- }
-
- /**
- * {@inheritDoc}
- *
- * Implements {@link RegistrationStateChangeListener}. Notifies this
- * instance that there has been a change in the <tt>RegistrationState</tt>
- * of {@link #protocolProvider}. Subscribes this instance to
- * {@link ColibriConferenceIQ}s as soon as <tt>protocolProvider</tt> is
- * registered and unsubscribes it as soon as <tt>protocolProvider</tt> is
- * unregistered.
- */
- public void registrationStateChanged(RegistrationStateChangeEvent ev)
- {
- RegistrationState registrationState = ev.getNewState();
-
- if (RegistrationState.REGISTERED.equals(registrationState))
- {
- protocolProvider.getConnection().addPacketListener(this, this);
- }
- else if (RegistrationState.UNREGISTERED.equals(registrationState))
- {
- XMPPConnection connection = protocolProvider.getConnection();
-
- if (connection != null)
- connection.removePacketListener(this);
- }
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.util.*;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.media.*;
+import net.java.sip.communicator.util.*;
+
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+
+/**
+ * Implements <tt>OperationSetVideoBridge</tt> for Jabber.
+ *
+ * @author Yana Stamcheva
+ * @author Lyubomir Marinov
+ */
+public class OperationSetVideoBridgeImpl
+ implements OperationSetVideoBridge,
+ PacketFilter,
+ PacketListener,
+ RegistrationStateChangeListener
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>OperationSetVideoBridgeImpl</tt>
+ * class and its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(OperationSetVideoBridgeImpl.class);
+
+ /**
+ * The <tt>ProtocolProviderService</tt> implementation which initialized
+ * this instance, owns it and is often referred to as its parent.
+ */
+ private final ProtocolProviderServiceJabberImpl protocolProvider;
+
+ /**
+ * Creates an instance of <tt>OperationSetVideoBridgeImpl</tt> by
+ * specifying the parent <tt>ProtocolProviderService</tt> announcing this
+ * operation set.
+ *
+ * @param protocolProvider the parent Jabber protocol provider
+ */
+ public OperationSetVideoBridgeImpl(
+ ProtocolProviderServiceJabberImpl protocolProvider)
+ {
+ this.protocolProvider = protocolProvider;
+ this.protocolProvider.addRegistrationStateChangeListener(this);
+ }
+
+ /**
+ * Implements {@link PacketFilter}. Determines whether this instance is
+ * interested in a specific {@link Packet}.
+ * <tt>OperationSetVideoBridgeImpl</tt> returns <tt>true</tt> if the
+ * specified <tt>packet</tt> is a {@link ColibriConferenceIQ}; otherwise,
+ * <tt>false</tt>.
+ *
+ * @param packet the <tt>Packet</tt> to be determined whether this instance
+ * is interested in it
+ * @return <tt>true</tt> if the specified <tt>packet</tt> is a
+ * <tt>ColibriConferenceIQ</tt>; otherwise, <tt>false</tt>
+ */
+ public boolean accept(Packet packet)
+ {
+ return (packet instanceof ColibriConferenceIQ);
+ }
+
+ /**
+ * Creates a conference call with the specified callees as call peers via a
+ * video bridge provided by the parent Jabber provider.
+ *
+ * @param callees the list of addresses that we should call
+ * @return the newly created conference call containing all CallPeers
+ * @throws OperationFailedException if establishing the conference call
+ * fails
+ * @throws OperationNotSupportedException if the provider does not have any
+ * conferencing features.
+ */
+ public Call createConfCall(String[] callees)
+ throws OperationFailedException,
+ OperationNotSupportedException
+ {
+ return
+ protocolProvider
+ .getOperationSet(OperationSetTelephonyConferencing.class)
+ .createConfCall(
+ callees,
+ new MediaAwareCallConference(true));
+ }
+
+ /**
+ * Invites the callee represented by the specified uri to an already
+ * existing call using a video bridge provided by the parent Jabber provider.
+ * The difference between this method and createConfCall is that
+ * inviteCalleeToCall allows a user to add new peers to an already
+ * established conference.
+ *
+ * @param uri the callee to invite to an existing conf call.
+ * @param call the call that we should invite the callee to.
+ * @return the CallPeer object corresponding to the callee represented by
+ * the specified uri.
+ * @throws OperationFailedException if inviting the specified callee to the
+ * specified call fails
+ * @throws OperationNotSupportedException if allowing additional callees to
+ * a pre-established call is not supported.
+ */
+ public CallPeer inviteCalleeToCall(String uri, Call call)
+ throws OperationFailedException,
+ OperationNotSupportedException
+ {
+ return
+ protocolProvider
+ .getOperationSet(OperationSetTelephonyConferencing.class)
+ .inviteCalleeToCall(uri, call);
+ }
+
+ /**
+ * Indicates if there's an active video bridge available at this moment. The
+ * Jabber provider may announce support for video bridge, but it should not
+ * be used for calling until it becomes actually active.
+ *
+ * @return <tt>true</tt> to indicate that there's currently an active
+ * available video bridge, <tt>false</tt> - otherwise
+ */
+ public boolean isActive()
+ {
+ String jitsiVideobridge = protocolProvider.getJitsiVideobridge();
+
+ return ((jitsiVideobridge != null) && (jitsiVideobridge.length() > 0));
+ }
+
+ /**
+ * Notifies this instance that a specific <tt>ColibriConferenceIQ</tt> has
+ * been received.
+ *
+ * @param conferenceIQ the <tt>ColibriConferenceIQ</tt> which has been
+ * received
+ */
+ private void processColibriConferenceIQ(ColibriConferenceIQ conferenceIQ)
+ {
+ /*
+ * The application is not a Jitsi Videobridge server, it is a client.
+ * Consequently, the specified ColibriConferenceIQ is sent to it in
+ * relation to the part of the application's functionality which makes
+ * requests to a Jitsi Videobridge server i.e. CallJabberImpl.
+ *
+ * Additionally, the method processColibriConferenceIQ is presently tasked
+ * with processing ColibriConferenceIQ requests only. They are SET IQs
+ * sent by the Jitsi Videobridge server to notify the application about
+ * updates in the states of (colibri) conferences organized by the
+ * application.
+ */
+ if (IQ.Type.SET.equals(conferenceIQ.getType())
+ && conferenceIQ.getID() != null)
+ {
+ OperationSetBasicTelephony<?> basicTelephony
+ = protocolProvider.getOperationSet(
+ OperationSetBasicTelephony.class);
+
+ if (basicTelephony != null)
+ {
+ Iterator<? extends Call> i = basicTelephony.getActiveCalls();
+
+ while (i.hasNext())
+ {
+ Call call = i.next();
+
+ if (call instanceof CallJabberImpl)
+ {
+ CallJabberImpl callJabberImpl = (CallJabberImpl) call;
+ MediaAwareCallConference conference
+ = callJabberImpl.getConference();
+
+ if ((conference != null)
+ && conference.isJitsiVideobridge())
+ {
+ /*
+ * TODO We may want to disallow rogue CallJabberImpl
+ * instances which may throw an exception to prevent
+ * the conferenceIQ from reaching the CallJabberImpl
+ * instance which it was meant for.
+ */
+ if (callJabberImpl.processColibriConferenceIQ(
+ conferenceIQ))
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Implements {@link PacketListener}. Notifies this instance that a specific
+ * {@link Packet} (which this instance has already expressed interest into
+ * by returning <tt>true</tt> from {@link #accept(Packet)}) has been
+ * received.
+ *
+ * @param packet the <tt>Packet</tt> which has been received and which this
+ * instance is given a chance to process
+ */
+ public void processPacket(Packet packet)
+ {
+ /*
+ * As we do elsewhere, acknowledge the receipt of the Packet first and
+ * then go about our business with it.
+ */
+ IQ iq = (IQ) packet;
+
+ if (iq.getType() == IQ.Type.SET)
+ protocolProvider.getConnection().sendPacket(IQ.createResultIQ(iq));
+
+ /*
+ * Now that the acknowledging is out of the way, do go about our
+ * business with the Packet.
+ */
+ ColibriConferenceIQ conferenceIQ = (ColibriConferenceIQ) iq;
+ boolean interrupted = false;
+
+ try
+ {
+ processColibriConferenceIQ(conferenceIQ);
+ }
+ catch (Throwable t)
+ {
+ logger.error(
+ "An error occurred during the processing of a "
+ + packet.getClass().getName() + " packet",
+ t);
+
+ if (t instanceof InterruptedException)
+ {
+ /*
+ * We cleared the interrupted state of the current Thread by
+ * catching the InterruptedException. However, we do not really
+ * care whether the current Thread has been interrupted - we
+ * caught the InterruptedException because we want to swallow
+ * any Throwable. Consequently, we should better restore the
+ * interrupted state.
+ */
+ interrupted = true;
+ }
+ else if (t instanceof ThreadDeath)
+ throw (ThreadDeath) t;
+ }
+ if (interrupted)
+ Thread.currentThread().interrupt();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Implements {@link RegistrationStateChangeListener}. Notifies this
+ * instance that there has been a change in the <tt>RegistrationState</tt>
+ * of {@link #protocolProvider}. Subscribes this instance to
+ * {@link ColibriConferenceIQ}s as soon as <tt>protocolProvider</tt> is
+ * registered and unsubscribes it as soon as <tt>protocolProvider</tt> is
+ * unregistered.
+ */
+ public void registrationStateChanged(RegistrationStateChangeEvent ev)
+ {
+ RegistrationState registrationState = ev.getNewState();
+
+ if (RegistrationState.REGISTERED.equals(registrationState))
+ {
+ protocolProvider.getConnection().addPacketListener(this, this);
+ }
+ else if (RegistrationState.UNREGISTERED.equals(registrationState))
+ {
+ Connection connection = protocolProvider.getConnection();
+
+ if (connection != null)
+ connection.removePacketListener(this);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java
index f38d0bc..b219d68 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java
@@ -248,7 +248,7 @@ public class OutgoingFileTransferJabberImpl
ThumbnailIQ thumbnailIQ = (ThumbnailIQ) packet;
String thumbnailIQCid = thumbnailIQ.getCid();
- XMPPConnection connection = protocolProvider.getConnection();
+ Connection connection = protocolProvider.getConnection();
if ((thumbnailIQCid != null)
&& thumbnailIQCid.equals(thumbnailElement.getCid()))
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java
index 4f9fc5f..86666f3 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java
@@ -21,6 +21,7 @@ import java.util.*;
import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.jabber.*;
import org.jivesoftware.smack.provider.*;
import org.jivesoftware.smack.util.*;
import org.osgi.framework.*;
@@ -46,7 +47,14 @@ public class ProtocolProviderFactoryJabberImpl
{
try
{
+
+ // Set the extension provider manager for classes that use
+ // it directly
ProviderManager.setInstance(new ProviderManagerExt());
+ // Set the Smack interop implementation for the classes that need
+ // to support Smackv4 interoperation
+ AbstractSmackInteroperabilityLayer.setImplementationClass(
+ SmackV3InteroperabilityLayer.class);
}
catch(Throwable t)
{
@@ -179,7 +187,7 @@ public class ProtocolProviderFactoryJabberImpl
ProtocolProviderServiceJabberImpl service =
new ProtocolProviderServiceJabberImpl();
- service.initialize(userID, accountID);
+ service.initialize(userID, (JabberAccountID) accountID);
return service;
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
index 2ea5c9c..47b9b75 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
@@ -32,6 +32,7 @@ import net.java.sip.communicator.impl.protocol.jabber.extensions.carbon.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*;
+import net.java.sip.communicator.impl.protocol.jabber.extensions.jibri.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.keepalive.*;
@@ -41,6 +42,7 @@ import net.java.sip.communicator.service.certificate.*;
import net.java.sip.communicator.service.dns.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.protocol.jabber.*;
import net.java.sip.communicator.service.protocol.jabberconstants.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.Logger;
@@ -152,6 +154,14 @@ public class ProtocolProviderServiceJabberImpl
public static final String URN_IETF_RFC_3264 = "urn:ietf:rfc:3264";
/**
+ * http://xmpp.org/extensions/xep-0092.html Software Version.
+ *
+ */
+ // Used in JVB
+ @SuppressWarnings("unused")
+ public static final String URN_XMPP_IQ_VERSION = "jabber:iq:version";
+
+ /**
* Jingle's Discovery Info URN for "XEP-0294: Jingle RTP Header Extensions
* Negotiation" support.
*/
@@ -211,7 +221,7 @@ public class ProtocolProviderServiceJabberImpl
/**
* Used to connect to a XMPP server.
*/
- private XMPPConnection connection;
+ private Connection connection;
/**
* The socket address of the XMPP server.
@@ -231,7 +241,7 @@ public class ProtocolProviderServiceJabberImpl
/**
* The identifier of the account that this provider represents.
*/
- private AccountID accountID = null;
+ private JabberAccountID accountID = null;
/**
* Used when we need to re-register
@@ -598,7 +608,7 @@ public class ProtocolProviderServiceJabberImpl
*/
public boolean isSignalingTransportSecure()
{
- return connection != null && connection.isUsingTLS();
+ return connection.isSecureConnection();
}
/**
@@ -613,7 +623,7 @@ public class ProtocolProviderServiceJabberImpl
if(connection != null && connection.isConnected())
{
// Transport using a secure connection.
- if(connection.isUsingTLS())
+ if(isSignalingTransportSecure())
{
return TransportProtocol.TLS;
}
@@ -1112,11 +1122,24 @@ public class ProtocolProviderServiceJabberImpl
JabberLoginStrategy loginStrategy)
throws XMPPException
{
- ConnectionConfiguration confConn = new ConnectionConfiguration(
- address.getAddress().getHostAddress(),
- address.getPort(),
- serviceName, proxy
- );
+ // BOSH or TCP ?
+ ConnectionConfiguration confConn;
+ String boshURL = accountID.getBoshUrl();
+ boolean isBosh = !org.jitsi.util.StringUtils.isNullOrEmpty(boshURL);
+
+ if (isBosh)
+ {
+ confConn = new BOSHConfiguration(serviceName);
+ ((BOSHConfiguration)confConn).setBoshUrl(boshURL);
+ }
+ else
+ {
+ confConn
+ = new ConnectionConfiguration(
+ address.getAddress().getHostAddress(),
+ address.getPort(),
+ serviceName, proxy);
+ }
// if we have OperationSetPersistentPresence skip sending initial
// presence while login is executed, the OperationSet will take care
@@ -1144,7 +1167,11 @@ public class ProtocolProviderServiceJabberImpl
disconnectAndCleanConnection();
}
- connection = new XMPPConnection(confConn);
+ connection
+ = isBosh
+ ? new XMPPBOSHConnection((BOSHConfiguration)confConn)
+ : new XMPPConnection(confConn);
+
this.address = address;
try
@@ -1194,13 +1221,16 @@ public class ProtocolProviderServiceJabberImpl
throw new XMPPException("Error creating custom trust manager", e);
}
- if(debugger == null)
+ // FIXME rework debugger to work with Connection if possible
+ if(debugger == null && connection instanceof XMPPConnection)
+ {
debugger = new SmackPacketDebugger();
- // sets the debugger
- debugger.setConnection(connection);
- connection.addPacketListener(debugger, null);
- connection.addPacketInterceptor(debugger, null);
+ // sets the debugger
+ debugger.setConnection((XMPPConnection) connection);
+ connection.addPacketListener(debugger, null);
+ connection.addPacketInterceptor(debugger, null);
+ }
connection.connect();
@@ -1247,9 +1277,10 @@ public class ProtocolProviderServiceJabberImpl
}
else
{
- if (connection.getSocket() instanceof SSLSocket)
+ final SSLSocket sslSocket = getSSLSocket();
+
+ if (sslSocket != null)
{
- final SSLSocket sslSocket = (SSLSocket) connection.getSocket();
StringBuilder buff = new StringBuilder();
buff.append("Chosen TLS protocol and algorithm:\n")
.append("Protocol: ").append(sslSocket.getSession()
@@ -1538,7 +1569,7 @@ public class ProtocolProviderServiceJabberImpl
* @see net.java.sip.communicator.service.protocol.AccountID
*/
protected void initialize(String screenname,
- AccountID accountID)
+ JabberAccountID accountID)
{
synchronized(initializationLock)
{
@@ -1732,6 +1763,12 @@ public class ProtocolProviderServiceJabberImpl
ColibriConferenceIQ.NAMESPACE,
new ColibriIQProvider());
+ providerManager.addIQProvider(
+ JibriIq.ELEMENT_NAME,
+ JibriIq.NAMESPACE,
+ new JibriIqProvider()
+ );
+
providerManager.addExtensionProvider(
ConferenceDescriptionPacketExtension.ELEMENT_NAME,
ConferenceDescriptionPacketExtension.NAMESPACE,
@@ -2025,11 +2062,98 @@ public class ProtocolProviderServiceJabberImpl
}
/**
- * Returns the <tt>XMPPConnection</tt>opened by this provider
- * @return a reference to the <tt>XMPPConnection</tt> last opened by this
+ * Validates the node part of a JID and returns an error message if
+ * applicable and a suggested correction.
+ *
+ * @param contactId the contact identifier to validate
+ * @param result Must be supplied as an empty a list. Implementors add
+ * items:
+ * <ol>
+ * <li>is the error message if applicable
+ * <li>a suggested correction. Index 1 is optional and can only
+ * be present if there was a validation failure.
+ * </ol>
+ * @return true if the contact id is valid, false otherwise
+ */
+ @Override
+ public boolean validateContactAddress(String contactId, List<String> result)
+ {
+ if (result == null)
+ {
+ throw new IllegalArgumentException("result must be an empty list");
+ }
+
+ result.clear();
+ try
+ {
+ contactId = contactId.trim();
+ if (contactId.length() == 0)
+ {
+ result.add(JabberActivator.getResources().getI18NString(
+ "impl.protocol.jabber.INVALID_ADDRESS", new String[]
+ { contactId }));
+ // no suggestion for an empty id
+ return false;
+ }
+
+ String user = contactId;
+ String remainder = "";
+ int at = contactId.indexOf('@');
+ if (at > -1)
+ {
+ user = contactId.substring(0, at);
+ remainder = contactId.substring(at);
+ }
+
+ // <conforming-char> ::= #x21 | [#x23-#x25] | [#x28-#x2E] |
+ // [#x30-#x39] | #x3B | #x3D | #x3F |
+ // [#x41-#x7E] | [#x80-#xD7FF] |
+ // [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+ boolean valid = true;
+ String suggestion = "";
+ for (char c : user.toCharArray())
+ {
+ if (!(c == 0x21 || (c >= 0x23 && c <= 0x25)
+ || (c >= 0x28 && c <= 0x2e) || (c >= 0x30 && c <= 0x39)
+ || c == 0x3b || c == 0x3d || c == 0x3f
+ || (c >= 0x41 && c <= 0x7e) || (c >= 0x80 && c <= 0xd7ff)
+ || (c >= 0xe000 && c <= 0xfffd)))
+ {
+ valid = false;
+ }
+ else
+ {
+ suggestion += c;
+ }
+ }
+
+ if (!valid)
+ {
+ result.add(JabberActivator.getResources().getI18NString(
+ "impl.protocol.jabber.INVALID_ADDRESS", new String[]
+ { contactId }));
+ result.add(suggestion + remainder);
+ return false;
+ }
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ result.add(JabberActivator.getResources().getI18NString(
+ "impl.protocol.jabber.INVALID_ADDRESS", new String[]
+ { contactId }));
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the <tt>Connection</tt>opened by this provider
+ * @return a reference to the <tt>Connection</tt> last opened by this
* provider.
*/
- public XMPPConnection getConnection()
+ public Connection getConnection()
{
return connection;
}
@@ -2315,18 +2439,16 @@ public class ProtocolProviderServiceJabberImpl
*/
public boolean isFeatureListSupported(String jid, String... features)
{
- boolean isFeatureListSupported = true;
-
try
{
if(discoveryManager == null)
- return isFeatureListSupported;
+ return false;
DiscoverInfo featureInfo =
discoveryManager.discoverInfoNonBlocking(jid);
if(featureInfo == null)
- return isFeatureListSupported;
+ return false;
for (String feature : features)
{
@@ -2334,17 +2456,19 @@ public class ProtocolProviderServiceJabberImpl
{
// If one is not supported we return false and don't check
// the others.
- isFeatureListSupported = false;
- break;
+ return false;
}
}
+
+ return true;
}
catch (XMPPException e)
{
if (logger.isDebugEnabled())
logger.debug("Failed to retrive discovery info.", e);
}
- return isFeatureListSupported;
+
+ return false;
}
/**
@@ -2386,7 +2510,7 @@ public class ProtocolProviderServiceJabberImpl
*/
public String getFullJid(String bareJid)
{
- XMPPConnection connection = getConnection();
+ Connection connection = getConnection();
// when we are not connected there is no full jid
if (connection != null && connection.isConnected())
@@ -2590,12 +2714,21 @@ public class ProtocolProviderServiceJabberImpl
*/
public void startJingleNodesDiscovery()
{
+ if (!(connection instanceof XMPPConnection))
+ {
+ logger.warn(
+ "Jingle node discovery currently will work only with " +
+ "TCP XMPP connection");
+ return;
+ }
+
// Jingle Nodes Service Initialization
+ final XMPPConnection xmppConnection = (XMPPConnection) connection;
final JabberAccountIDImpl accID = (JabberAccountIDImpl)getAccountID();
- final SmackServiceNode service = new SmackServiceNode(connection,
- 60000);
+ final SmackServiceNode service
+ = new SmackServiceNode(xmppConnection, 60000);
// make sure SmackServiceNode will clean up when connection is closed
- connection.addConnectionListener(service);
+ xmppConnection.addConnectionListener(service);
for(JingleNodeDescriptor desc : accID.getJingleNodes())
{
@@ -2611,7 +2744,7 @@ public class ProtocolProviderServiceJabberImpl
new Thread(new JingleNodesServiceDiscovery(
service,
- connection,
+ xmppConnection,
accID,
jingleNodesSyncRoot))
.start();
@@ -2739,7 +2872,7 @@ public class ProtocolProviderServiceJabberImpl
*/
private void setTrafficClass()
{
- Socket s = connection.getSocket();
+ Socket s = getSocket();
if(s != null)
{
@@ -2774,7 +2907,7 @@ public class ProtocolProviderServiceJabberImpl
*/
public String getJitsiVideobridge()
{
- XMPPConnection connection = getConnection();
+ Connection connection = getConnection();
if (connection != null)
{
@@ -2865,21 +2998,23 @@ public class ProtocolProviderServiceJabberImpl
}
/**
+ * Obtains XMPP connection's socket.
+ * @return <tt>Socket</tt> instance used by the underlying XMPP connection
+ * or <tt>null</tt> if "non socket" type of transport is currently used.
+ */
+ private Socket getSocket()
+ {
+ return connection != null ? connection.getSocket() : null;
+ }
+
+ /**
* Return the SSL socket (if TLS used).
* @return The SSL socket or null if not used
*/
- public SSLSocket getSSLSocket()
+ SSLSocket getSSLSocket()
{
- final SSLSocket result;
- final Socket socket = connection.getSocket();
- if (socket instanceof SSLSocket)
- {
- result = (SSLSocket) socket;
- }
- else
- {
- result = null;
- }
- return result;
+ final Socket socket = getSocket();
+
+ return (socket instanceof SSLSocket) ? (SSLSocket) socket : null;
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
index 0aa4fb6..4bd49a9 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,529 +15,529 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.net.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
-import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
-import net.java.sip.communicator.service.protocol.*;
-
-import org.jitsi.service.neomedia.*;
-import org.jivesoftware.smack.packet.*;
-
-/**
- * A {@link TransportManagerJabberImpl} implementation that would only gather a
- * single candidate pair (i.e. RTP and RTCP).
- *
- * @author Emil Ivov
- * @author Lyubomir Marinov
- * @author Hristo Terezov
- */
-public class RawUdpTransportManager
- extends TransportManagerJabberImpl
-{
- /**
- * The list of <tt>ContentPacketExtension</tt>s which represents the local
- * counterpart of the negotiation between the local and the remote peers.
- */
- private List<ContentPacketExtension> local;
-
- /**
- * The collection of <tt>ContentPacketExtension</tt>s which represents the
- * remote counterpart of the negotiation between the local and the remote
- * peers.
- */
- private final List<Iterable<ContentPacketExtension>> remotes
- = new LinkedList<Iterable<ContentPacketExtension>>();
-
- /**
- * Creates a new instance of this transport manager, binding it to the
- * specified peer.
- *
- * @param callPeer the {@link CallPeer} whose traffic we will be taking
- * care of.
- */
- public RawUdpTransportManager(CallPeerJabberImpl callPeer)
- {
- super(callPeer);
- }
-
- /**
- * {@inheritDoc}
- */
- protected PacketExtension createTransport(String media)
- throws OperationFailedException
- {
- MediaType mediaType = MediaType.parseString(media);
-
- return createTransport(mediaType, getStreamConnector(mediaType));
- }
-
- /**
- * Creates a raw UDP transport element according to a specific
- * <tt>StreamConnector</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
- * uses the specified <tt>connector</tt> or <tt>channel</tt>
- * @param connector the <tt>StreamConnector</tt> to be described within the
- * transport element
- * @return a {@link RawUdpTransportPacketExtension} containing the RTP and
- * RTCP candidates of the specified <tt>connector</tt>
- */
- private RawUdpTransportPacketExtension createTransport(
- MediaType mediaType,
- StreamConnector connector)
- {
- RawUdpTransportPacketExtension ourTransport
- = new RawUdpTransportPacketExtension();
- int generation = getCurrentGeneration();
-
- // create and add candidates that correspond to the stream connector
- // RTP
- CandidatePacketExtension rtpCand = new CandidatePacketExtension();
-
- rtpCand.setComponent(CandidatePacketExtension.RTP_COMPONENT_ID);
- rtpCand.setGeneration(generation);
- rtpCand.setID(getNextID());
- rtpCand.setType(CandidateType.host);
-
- DatagramSocket dataSocket = connector.getDataSocket();
-
- rtpCand.setIP(dataSocket.getLocalAddress().getHostAddress());
- rtpCand.setPort(dataSocket.getLocalPort());
-
- ourTransport.addCandidate(rtpCand);
-
- // RTCP
- CandidatePacketExtension rtcpCand = new CandidatePacketExtension();
-
- rtcpCand.setComponent(CandidatePacketExtension.RTCP_COMPONENT_ID);
- rtcpCand.setGeneration(generation);
- rtcpCand.setID(getNextID());
- rtcpCand.setType(CandidateType.host);
-
- DatagramSocket controlSocket = connector.getControlSocket();
-
- rtcpCand.setIP(controlSocket.getLocalAddress().getHostAddress());
- rtcpCand.setPort(controlSocket.getLocalPort());
-
- ourTransport.addCandidate(rtcpCand);
-
- return ourTransport;
- }
-
- /**
- * {@inheritDoc}
- */
- protected PacketExtension createTransportPacketExtension()
- {
- return new RawUdpTransportPacketExtension();
- }
-
- /**
- * Implements {@link TransportManagerJabberImpl#getStreamTarget(MediaType)}.
- * Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
- * the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
- * is to have its <tt>target</tt> set to the returned
- * <tt>MediaStreamTarget</tt>
- * @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
- * of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
- * @see TransportManagerJabberImpl#getStreamTarget(MediaType)
- */
- @Override
- public MediaStreamTarget getStreamTarget(MediaType mediaType)
- {
- ColibriConferenceIQ.Channel channel
- = getColibriChannel(mediaType, true /* local */);
- MediaStreamTarget streamTarget = null;
-
- if (channel == null)
- {
- String media = mediaType.toString();
-
- for (Iterable<ContentPacketExtension> remote : remotes)
- {
- for (ContentPacketExtension content : remote)
- {
- RtpDescriptionPacketExtension rtpDescription
- = content.getFirstChildOfType(
- RtpDescriptionPacketExtension.class);
-
- if (media.equals(rtpDescription.getMedia()))
- {
- streamTarget
- = JingleUtils.extractDefaultTarget(content);
- break;
- }
- }
- }
- }
- else
- {
- IceUdpTransportPacketExtension transport = channel.getTransport();
-
- if (transport != null)
- streamTarget = JingleUtils.extractDefaultTarget(transport);
- if (streamTarget == null)
- {
- /*
- * For the purposes of compatibility with legacy Jitsi
- * Videobridge, support the channel attributes host, rtpPort and
- * rtcpPort.
- */
- @SuppressWarnings("deprecation")
- String host = channel.getHost();
-
- if (host != null)
- {
- @SuppressWarnings("deprecation")
- int rtpPort = channel.getRTPPort();
- @SuppressWarnings("deprecation")
- int rtcpPort = channel.getRTCPPort();
-
- streamTarget
- = new MediaStreamTarget(
- new InetSocketAddress(host, rtpPort),
- new InetSocketAddress(host, rtcpPort));
- }
- }
- }
- return streamTarget;
- }
-
- /**
- * Implements {@link TransportManagerJabberImpl#getXmlNamespace()}. Gets the
- * XML namespace of the Jingle transport implemented by this
- * <tt>TransportManagerJabberImpl</tt>.
- *
- * @return the XML namespace of the Jingle transport implemented by this
- * <tt>TransportManagerJabberImpl</tt>
- * @see TransportManagerJabberImpl#getXmlNamespace()
- */
- @Override
- public String getXmlNamespace()
- {
- return ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0;
- }
-
- /**
- * Removes a content with a specific name from the transport-related part of
- * the session represented by this <tt>TransportManagerJabberImpl</tt> which
- * may have been reported through previous calls to the
- * <tt>startCandidateHarvest</tt> and
- * <tt>startConnectivityEstablishment</tt> methods.
- *
- * @param name the name of the content to be removed from the
- * transport-related part of the session represented by this
- * <tt>TransportManagerJabberImpl</tt>
- * @see TransportManagerJabberImpl#removeContent(String)
- */
- @Override
- public void removeContent(String name)
- {
- if (local != null)
- removeContent(local, name);
-
- removeRemoteContent(name);
- }
-
- /**
- * Removes a content with a specific name from the remote counterpart of the
- * negotiation between the local and the remote peers.
- *
- * @param name the name of the content to be removed from the remote
- * counterpart of the negotiation between the local and the remote peers
- */
- private void removeRemoteContent(String name)
- {
- for (Iterator<Iterable<ContentPacketExtension>> remoteIter
- = remotes.iterator();
- remoteIter.hasNext();)
- {
- Iterable<ContentPacketExtension> remote = remoteIter.next();
-
- /*
- * Once the remote content is removed, make sure that we are not
- * retaining sets which do not have any contents.
- */
- if ((removeContent(remote, name) != null)
- && !remote.iterator().hasNext())
- {
- remoteIter.remove();
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- protected PacketExtension startCandidateHarvest(
- ContentPacketExtension theirContent,
- ContentPacketExtension ourContent,
- TransportInfoSender transportInfoSender,
- String media)
- throws OperationFailedException
- {
- return createTransportForStartCandidateHarvest(media);
- }
-
- /**
- * Starts transport candidate harvest. This method should complete rapidly
- * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
- * are necessary, they should be executed in a separate thread. Candidate
- * harvest would then need to be concluded in the
- * {@link #wrapupCandidateHarvest()} method which would be called once we
- * absolutely need the candidates.
- *
- * @param theirOffer a media description offer that we've received from the
- * remote party and that we should use in case we need to know what
- * transports our peer is using.
- * @param ourAnswer the content descriptions that we should be adding our
- * transport lists to (although not necessarily in this very instance).
- * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
- * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
- * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
- * <tt>TransportManagerJabberImpl</tt> wishes to utilize
- * <tt>transport-info</tt>. Local candidate addresses sent by this
- * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
- * expected to not be included in the result of
- * {@link #wrapupCandidateHarvest()}.
- *
- * @throws OperationFailedException if we fail to allocate a port number.
- * @see TransportManagerJabberImpl#startCandidateHarvest(List, List,
- * TransportInfoSender)
- */
- @Override
- public void startCandidateHarvest(
- List<ContentPacketExtension> theirOffer,
- List<ContentPacketExtension> ourAnswer,
- TransportInfoSender transportInfoSender)
- throws OperationFailedException
- {
- this.local = ourAnswer;
-
- super.startCandidateHarvest(theirOffer, ourAnswer, transportInfoSender);
- }
-
- /**
- * Overrides the super implementation in order to remember the remote
- * counterpart of the negotiation between the local and the remote peer for
- * subsequent calls to {@link #getStreamTarget(MediaType)}.
- *
- * @param remote the collection of <tt>ContentPacketExtension</tt>s which
- * represents the remote counterpart of the negotiation between the local
- * and the remote peer
- * @return <tt>true</tt> because <tt>RawUdpTransportManager</tt> does not
- * perform connectivity checks
- * @see TransportManagerJabberImpl#startConnectivityEstablishment(Iterable)
- */
- @Override
- public boolean startConnectivityEstablishment(
- Iterable<ContentPacketExtension> remote)
- {
- if ((remote != null) && !remotes.contains(remote))
- {
- /*
- * The state of the session in Jingle is maintained by each peer and
- * is modified by content-add and content-remove. The remotes field
- * of this RawUdpTransportManager represents the state of the
- * session with respect to the remote peer. When the remote peer
- * tells us about a specific set of contents, make sure that it is
- * the only record we will have with respect to the specified set of
- * contents.
- */
- for (ContentPacketExtension content : remote)
- removeRemoteContent(content.getName());
-
- remotes.add(remote);
- }
-
- return super.startConnectivityEstablishment(remote);
- }
-
- /**
- * Simply returns the list of local candidates that we gathered during the
- * harvest. This is a raw UDP transport manager so there's no real wrapping
- * up to do.
- *
- * @return the list of local candidates that we gathered during the harvest
- * @see TransportManagerJabberImpl#wrapupCandidateHarvest()
- */
- @Override
- public List<ContentPacketExtension> wrapupCandidateHarvest()
- {
- return local;
- }
-
- /**
- * Returns the extended type of the candidate selected if this transport
- * manager is using ICE.
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return The extended type of the candidate selected if this transport
- * manager is using ICE. Otherwise, returns null.
- */
- @Override
- public String getICECandidateExtendedType(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the current state of ICE processing.
- *
- * @return the current state of ICE processing.
- */
- @Override
- public String getICEState()
- {
- return null;
- }
-
- /**
- * Returns the ICE local host address.
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE local host address if this transport
- * manager is using ICE. Otherwise, returns null.
- */
- @Override
- public InetSocketAddress getICELocalHostAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE remote host address.
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE remote host address if this transport
- * manager is using ICE. Otherwise, returns null.
- */
- @Override
- public InetSocketAddress getICERemoteHostAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE local reflexive address (server or peer reflexive).
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE local reflexive address. May be null if this transport
- * manager is not using ICE or if there is no reflexive address for the
- * local candidate used.
- */
- @Override
- public InetSocketAddress getICELocalReflexiveAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE remote reflexive address (server or peer reflexive).
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE remote reflexive address. May be null if this transport
- * manager is not using ICE or if there is no reflexive address for the
- * remote candidate used.
- */
- @Override
- public InetSocketAddress getICERemoteReflexiveAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE local relayed address (server or peer relayed).
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE local relayed address. May be null if this transport
- * manager is not using ICE or if there is no relayed address for the
- * local candidate used.
- */
- @Override
- public InetSocketAddress getICELocalRelayedAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the ICE remote relayed address (server or peer relayed).
- *
- * @param streamName The stream name (AUDIO, VIDEO);
- *
- * @return the ICE remote relayed address. May be null if this transport
- * manager is not using ICE or if there is no relayed address for the
- * remote candidate used.
- */
- @Override
- public InetSocketAddress getICERemoteRelayedAddress(String streamName)
- {
- return null;
- }
-
- /**
- * Returns the total harvesting time (in ms) for all harvesters.
- *
- * @return The total harvesting time (in ms) for all the harvesters. 0 if
- * the ICE agent is null, or if the agent has nevers harvested.
- */
- @Override
- public long getTotalHarvestingTime()
- {
- return 0;
- }
-
- /**
- * Returns the harvesting time (in ms) for the harvester given in parameter.
- *
- * @param harvesterName The class name if the harvester.
- *
- * @return The harvesting time (in ms) for the harvester given in parameter.
- * 0 if this harvester does not exists, if the ICE agent is null, or if the
- * agent has never harvested with this harvester.
- */
- @Override
- public long getHarvestingTime(String harvesterName)
- {
- return 0;
- }
-
- /**
- * Returns the number of harvesting for this agent.
- *
- * @return The number of harvesting for this agent.
- */
- @Override
- public int getNbHarvesting()
- {
- return 0;
- }
-
- /**
- * Returns the number of harvesting time for the harvester given in
- * parameter.
- *
- * @param harvesterName The class name if the harvester.
- *
- * @return The number of harvesting time for the harvester given in
- * parameter.
- */
- @Override
- public int getNbHarvesting(String harvesterName)
- {
- return 0;
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.net.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
+import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
+import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
+import net.java.sip.communicator.service.protocol.*;
+
+import org.jitsi.service.neomedia.*;
+import org.jivesoftware.smack.packet.*;
+
+/**
+ * A {@link TransportManagerJabberImpl} implementation that would only gather a
+ * single candidate pair (i.e. RTP and RTCP).
+ *
+ * @author Emil Ivov
+ * @author Lyubomir Marinov
+ * @author Hristo Terezov
+ */
+public class RawUdpTransportManager
+ extends TransportManagerJabberImpl
+{
+ /**
+ * The list of <tt>ContentPacketExtension</tt>s which represents the local
+ * counterpart of the negotiation between the local and the remote peers.
+ */
+ private List<ContentPacketExtension> local;
+
+ /**
+ * The collection of <tt>ContentPacketExtension</tt>s which represents the
+ * remote counterpart of the negotiation between the local and the remote
+ * peers.
+ */
+ private final List<Iterable<ContentPacketExtension>> remotes
+ = new LinkedList<Iterable<ContentPacketExtension>>();
+
+ /**
+ * Creates a new instance of this transport manager, binding it to the
+ * specified peer.
+ *
+ * @param callPeer the {@link CallPeer} whose traffic we will be taking
+ * care of.
+ */
+ public RawUdpTransportManager(CallPeerJabberImpl callPeer)
+ {
+ super(callPeer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected PacketExtension createTransport(String media)
+ throws OperationFailedException
+ {
+ MediaType mediaType = MediaType.parseString(media);
+
+ return createTransport(mediaType, getStreamConnector(mediaType));
+ }
+
+ /**
+ * Creates a raw UDP transport element according to a specific
+ * <tt>StreamConnector</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
+ * uses the specified <tt>connector</tt> or <tt>channel</tt>
+ * @param connector the <tt>StreamConnector</tt> to be described within the
+ * transport element
+ * @return a {@link RawUdpTransportPacketExtension} containing the RTP and
+ * RTCP candidates of the specified <tt>connector</tt>
+ */
+ private RawUdpTransportPacketExtension createTransport(
+ MediaType mediaType,
+ StreamConnector connector)
+ {
+ RawUdpTransportPacketExtension ourTransport
+ = new RawUdpTransportPacketExtension();
+ int generation = getCurrentGeneration();
+
+ // create and add candidates that correspond to the stream connector
+ // RTP
+ CandidatePacketExtension rtpCand = new CandidatePacketExtension();
+
+ rtpCand.setComponent(CandidatePacketExtension.RTP_COMPONENT_ID);
+ rtpCand.setGeneration(generation);
+ rtpCand.setID(getNextID());
+ rtpCand.setType(CandidateType.host);
+
+ DatagramSocket dataSocket = connector.getDataSocket();
+
+ rtpCand.setIP(dataSocket.getLocalAddress().getHostAddress());
+ rtpCand.setPort(dataSocket.getLocalPort());
+
+ ourTransport.addCandidate(rtpCand);
+
+ // RTCP
+ CandidatePacketExtension rtcpCand = new CandidatePacketExtension();
+
+ rtcpCand.setComponent(CandidatePacketExtension.RTCP_COMPONENT_ID);
+ rtcpCand.setGeneration(generation);
+ rtcpCand.setID(getNextID());
+ rtcpCand.setType(CandidateType.host);
+
+ DatagramSocket controlSocket = connector.getControlSocket();
+
+ rtcpCand.setIP(controlSocket.getLocalAddress().getHostAddress());
+ rtcpCand.setPort(controlSocket.getLocalPort());
+
+ ourTransport.addCandidate(rtcpCand);
+
+ return ourTransport;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected PacketExtension createTransportPacketExtension()
+ {
+ return new RawUdpTransportPacketExtension();
+ }
+
+ /**
+ * Implements {@link TransportManagerJabberImpl#getStreamTarget(MediaType)}.
+ * Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
+ * the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
+ * is to have its <tt>target</tt> set to the returned
+ * <tt>MediaStreamTarget</tt>
+ * @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
+ * of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
+ * @see TransportManagerJabberImpl#getStreamTarget(MediaType)
+ */
+ @Override
+ public MediaStreamTarget getStreamTarget(MediaType mediaType)
+ {
+ ColibriConferenceIQ.Channel channel
+ = getColibriChannel(mediaType, true /* local */);
+ MediaStreamTarget streamTarget = null;
+
+ if (channel == null)
+ {
+ String media = mediaType.toString();
+
+ for (Iterable<ContentPacketExtension> remote : remotes)
+ {
+ for (ContentPacketExtension content : remote)
+ {
+ RtpDescriptionPacketExtension rtpDescription
+ = content.getFirstChildOfType(
+ RtpDescriptionPacketExtension.class);
+
+ if (media.equals(rtpDescription.getMedia()))
+ {
+ streamTarget
+ = JingleUtils.extractDefaultTarget(content);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ IceUdpTransportPacketExtension transport = channel.getTransport();
+
+ if (transport != null)
+ streamTarget = JingleUtils.extractDefaultTarget(transport);
+ if (streamTarget == null)
+ {
+ /*
+ * For the purposes of compatibility with legacy Jitsi
+ * Videobridge, support the channel attributes host, rtpPort and
+ * rtcpPort.
+ */
+ @SuppressWarnings("deprecation")
+ String host = channel.getHost();
+
+ if (host != null)
+ {
+ @SuppressWarnings("deprecation")
+ int rtpPort = channel.getRTPPort();
+ @SuppressWarnings("deprecation")
+ int rtcpPort = channel.getRTCPPort();
+
+ streamTarget
+ = new MediaStreamTarget(
+ new InetSocketAddress(host, rtpPort),
+ new InetSocketAddress(host, rtcpPort));
+ }
+ }
+ }
+ return streamTarget;
+ }
+
+ /**
+ * Implements {@link TransportManagerJabberImpl#getXmlNamespace()}. Gets the
+ * XML namespace of the Jingle transport implemented by this
+ * <tt>TransportManagerJabberImpl</tt>.
+ *
+ * @return the XML namespace of the Jingle transport implemented by this
+ * <tt>TransportManagerJabberImpl</tt>
+ * @see TransportManagerJabberImpl#getXmlNamespace()
+ */
+ @Override
+ public String getXmlNamespace()
+ {
+ return ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RAW_UDP_0;
+ }
+
+ /**
+ * Removes a content with a specific name from the transport-related part of
+ * the session represented by this <tt>TransportManagerJabberImpl</tt> which
+ * may have been reported through previous calls to the
+ * <tt>startCandidateHarvest</tt> and
+ * <tt>startConnectivityEstablishment</tt> methods.
+ *
+ * @param name the name of the content to be removed from the
+ * transport-related part of the session represented by this
+ * <tt>TransportManagerJabberImpl</tt>
+ * @see TransportManagerJabberImpl#removeContent(String)
+ */
+ @Override
+ public void removeContent(String name)
+ {
+ if (local != null)
+ removeContent(local, name);
+
+ removeRemoteContent(name);
+ }
+
+ /**
+ * Removes a content with a specific name from the remote counterpart of the
+ * negotiation between the local and the remote peers.
+ *
+ * @param name the name of the content to be removed from the remote
+ * counterpart of the negotiation between the local and the remote peers
+ */
+ private void removeRemoteContent(String name)
+ {
+ for (Iterator<Iterable<ContentPacketExtension>> remoteIter
+ = remotes.iterator();
+ remoteIter.hasNext();)
+ {
+ Iterable<ContentPacketExtension> remote = remoteIter.next();
+
+ /*
+ * Once the remote content is removed, make sure that we are not
+ * retaining sets which do not have any contents.
+ */
+ if ((removeContent(remote, name) != null)
+ && !remote.iterator().hasNext())
+ {
+ remoteIter.remove();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected PacketExtension startCandidateHarvest(
+ ContentPacketExtension theirContent,
+ ContentPacketExtension ourContent,
+ TransportInfoSender transportInfoSender,
+ String media)
+ throws OperationFailedException
+ {
+ return createTransportForStartCandidateHarvest(media);
+ }
+
+ /**
+ * Starts transport candidate harvest. This method should complete rapidly
+ * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
+ * are necessary, they should be executed in a separate thread. Candidate
+ * harvest would then need to be concluded in the
+ * {@link #wrapupCandidateHarvest()} method which would be called once we
+ * absolutely need the candidates.
+ *
+ * @param theirOffer a media description offer that we've received from the
+ * remote party and that we should use in case we need to know what
+ * transports our peer is using.
+ * @param ourAnswer the content descriptions that we should be adding our
+ * transport lists to (although not necessarily in this very instance).
+ * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
+ * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
+ * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
+ * <tt>TransportManagerJabberImpl</tt> wishes to utilize
+ * <tt>transport-info</tt>. Local candidate addresses sent by this
+ * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
+ * expected to not be included in the result of
+ * {@link #wrapupCandidateHarvest()}.
+ *
+ * @throws OperationFailedException if we fail to allocate a port number.
+ * @see TransportManagerJabberImpl#startCandidateHarvest(List, List,
+ * TransportInfoSender)
+ */
+ @Override
+ public void startCandidateHarvest(
+ List<ContentPacketExtension> theirOffer,
+ List<ContentPacketExtension> ourAnswer,
+ TransportInfoSender transportInfoSender)
+ throws OperationFailedException
+ {
+ this.local = ourAnswer;
+
+ super.startCandidateHarvest(theirOffer, ourAnswer, transportInfoSender);
+ }
+
+ /**
+ * Overrides the super implementation in order to remember the remote
+ * counterpart of the negotiation between the local and the remote peer for
+ * subsequent calls to {@link #getStreamTarget(MediaType)}.
+ *
+ * @param remote the collection of <tt>ContentPacketExtension</tt>s which
+ * represents the remote counterpart of the negotiation between the local
+ * and the remote peer
+ * @return <tt>true</tt> because <tt>RawUdpTransportManager</tt> does not
+ * perform connectivity checks
+ * @see TransportManagerJabberImpl#startConnectivityEstablishment(Iterable)
+ */
+ @Override
+ public boolean startConnectivityEstablishment(
+ Iterable<ContentPacketExtension> remote)
+ {
+ if ((remote != null) && !remotes.contains(remote))
+ {
+ /*
+ * The state of the session in Jingle is maintained by each peer and
+ * is modified by content-add and content-remove. The remotes field
+ * of this RawUdpTransportManager represents the state of the
+ * session with respect to the remote peer. When the remote peer
+ * tells us about a specific set of contents, make sure that it is
+ * the only record we will have with respect to the specified set of
+ * contents.
+ */
+ for (ContentPacketExtension content : remote)
+ removeRemoteContent(content.getName());
+
+ remotes.add(remote);
+ }
+
+ return super.startConnectivityEstablishment(remote);
+ }
+
+ /**
+ * Simply returns the list of local candidates that we gathered during the
+ * harvest. This is a raw UDP transport manager so there's no real wrapping
+ * up to do.
+ *
+ * @return the list of local candidates that we gathered during the harvest
+ * @see TransportManagerJabberImpl#wrapupCandidateHarvest()
+ */
+ @Override
+ public List<ContentPacketExtension> wrapupCandidateHarvest()
+ {
+ return local;
+ }
+
+ /**
+ * Returns the extended type of the candidate selected if this transport
+ * manager is using ICE.
+ *
+ * @param streamName The stream name (AUDIO, VIDEO);
+ *
+ * @return The extended type of the candidate selected if this transport
+ * manager is using ICE. Otherwise, returns null.
+ */
+ @Override
+ public String getICECandidateExtendedType(String streamName)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the current state of ICE processing.
+ *
+ * @return the current state of ICE processing.
+ */
+ @Override
+ public String getICEState()
+ {
+ return null;
+ }
+
+ /**
+ * Returns the ICE local host address.
+ *
+ * @param streamName The stream name (AUDIO, VIDEO);
+ *
+ * @return the ICE local host address if this transport
+ * manager is using ICE. Otherwise, returns null.
+ */
+ @Override
+ public InetSocketAddress getICELocalHostAddress(String streamName)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the ICE remote host address.
+ *
+ * @param streamName The stream name (AUDIO, VIDEO);
+ *
+ * @return the ICE remote host address if this transport
+ * manager is using ICE. Otherwise, returns null.
+ */
+ @Override
+ public InetSocketAddress getICERemoteHostAddress(String streamName)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the ICE local reflexive address (server or peer reflexive).
+ *
+ * @param streamName The stream name (AUDIO, VIDEO);
+ *
+ * @return the ICE local reflexive address. May be null if this transport
+ * manager is not using ICE or if there is no reflexive address for the
+ * local candidate used.
+ */
+ @Override
+ public InetSocketAddress getICELocalReflexiveAddress(String streamName)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the ICE remote reflexive address (server or peer reflexive).
+ *
+ * @param streamName The stream name (AUDIO, VIDEO);
+ *
+ * @return the ICE remote reflexive address. May be null if this transport
+ * manager is not using ICE or if there is no reflexive address for the
+ * remote candidate used.
+ */
+ @Override
+ public InetSocketAddress getICERemoteReflexiveAddress(String streamName)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the ICE local relayed address (server or peer relayed).
+ *
+ * @param streamName The stream name (AUDIO, VIDEO);
+ *
+ * @return the ICE local relayed address. May be null if this transport
+ * manager is not using ICE or if there is no relayed address for the
+ * local candidate used.
+ */
+ @Override
+ public InetSocketAddress getICELocalRelayedAddress(String streamName)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the ICE remote relayed address (server or peer relayed).
+ *
+ * @param streamName The stream name (AUDIO, VIDEO);
+ *
+ * @return the ICE remote relayed address. May be null if this transport
+ * manager is not using ICE or if there is no relayed address for the
+ * remote candidate used.
+ */
+ @Override
+ public InetSocketAddress getICERemoteRelayedAddress(String streamName)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the total harvesting time (in ms) for all harvesters.
+ *
+ * @return The total harvesting time (in ms) for all the harvesters. 0 if
+ * the ICE agent is null, or if the agent has nevers harvested.
+ */
+ @Override
+ public long getTotalHarvestingTime()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the harvesting time (in ms) for the harvester given in parameter.
+ *
+ * @param harvesterName The class name if the harvester.
+ *
+ * @return The harvesting time (in ms) for the harvester given in parameter.
+ * 0 if this harvester does not exists, if the ICE agent is null, or if the
+ * agent has never harvested with this harvester.
+ */
+ @Override
+ public long getHarvestingTime(String harvesterName)
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the number of harvesting for this agent.
+ *
+ * @return The number of harvesting for this agent.
+ */
+ @Override
+ public int getNbHarvesting()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the number of harvesting time for the harvester given in
+ * parameter.
+ *
+ * @param harvesterName The class name if the harvester.
+ *
+ * @return The number of harvesting time for the harvester given in
+ * parameter.
+ */
+ @Override
+ public int getNbHarvesting(String harvesterName)
+ {
+ return 0;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java
index 9fdbea5..de2ce3e 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ScServiceDiscoveryManager.java
@@ -27,6 +27,7 @@ import net.java.sip.communicator.util.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.util.*;
import org.jivesoftware.smackx.*;
import org.jivesoftware.smackx.packet.*;
@@ -79,9 +80,9 @@ public class ScServiceDiscoveryManager
private final ProtocolProviderService parentProvider;
/**
- * The {@link XMPPConnection} that this manager is responsible for.
+ * The {@link Connection} that this manager is responsible for.
*/
- private final XMPPConnection connection;
+ private final Connection connection;
/**
* A local copy that we keep in sync with {@link ServiceDiscoveryManager}'s
@@ -129,7 +130,7 @@ public class ScServiceDiscoveryManager
*/
public ScServiceDiscoveryManager(
ProtocolProviderService parentProvider,
- XMPPConnection connection,
+ Connection connection,
String[] featuresToRemove,
String[] featuresToAdd,
boolean cacheNonCaps)
@@ -791,7 +792,11 @@ public class ScServiceDiscoveryManager
// fire event
if(fireEvent && capabilitiesOpSet != null)
{
- capabilitiesOpSet.fireContactCapabilitiesChanged(entityID);
+ capabilitiesOpSet.fireContactCapabilitiesChanged(
+ entityID,
+ capsManager.getFullJidsByBareJid(
+ StringUtils.parseBareAddress(entityID))
+ );
}
}
catch(XMPPException ex)
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/SmackV3InteroperabilityLayer.java b/src/net/java/sip/communicator/impl/protocol/jabber/SmackV3InteroperabilityLayer.java
new file mode 100644
index 0000000..d5af26f
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/SmackV3InteroperabilityLayer.java
@@ -0,0 +1,91 @@
+/*
+ * 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.jabber;
+
+import net.java.sip.communicator.service.protocol.jabber.*;
+import org.jivesoftware.smack.provider.*;
+
+/**
+ * Smack v3 interoperation layer
+ *
+ * @author Maksym Kulish
+ */
+public class SmackV3InteroperabilityLayer
+ extends AbstractSmackInteroperabilityLayer
+{
+
+ /**
+ * A SmackV3 ProviderManager instance
+ */
+ private ProviderManager providerManager = ProviderManager.getInstance();
+
+ /**
+ * A default constructor
+ */
+ public SmackV3InteroperabilityLayer() {}
+
+ /**
+ * Add <tt>PacketExtensionProvider</tt> to the list of known
+ * providers
+ *
+ * @param elementName The element name where the matching is happening
+ * @param namespace The XML namespace used in that element
+ * @param provider <tt>PacketExtensionProvider</tt> implementation to be
+ * used
+ */
+ @Override
+ public void addExtensionProvider(
+ String elementName, String namespace, Object provider)
+ {
+ providerManager.addExtensionProvider(elementName, namespace, provider);
+ }
+
+ /**
+ * Add <tt>IQProvider</tt> to the list of known
+ * providers
+ *
+ * @param elementName The element name where the matching is happening
+ * @param namespace The XML namespace used in that element
+ * @param provider <tt>IQProvider</tt> implementation to be
+ * used
+ */
+ @Override
+ public void addIQProvider(
+ String elementName, String namespace, Object provider)
+ {
+ providerManager.addIQProvider(elementName, namespace, provider);
+ }
+
+ /**
+ * Get the <tt>PacketExtensionProvider</tt> for given element name and XML
+ * namespace
+ *
+ * @param elementName The element name where the matching is happening
+ * @param namespace The XML namespace used in that element
+ * @return <tt>PacketExtensionProvider</tt> implementation to be
+ * used
+ */
+ @Override
+ public PacketExtensionProvider getExtensionProvider(
+ String elementName, String namespace)
+ {
+ return (PacketExtensionProvider)providerManager
+ .getExtensionProvider(elementName, namespace);
+ }
+
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java
index f7f47c6..41e8c05 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/TransportManagerJabberImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,963 +15,963 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.jabber;
-
-import java.net.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
-import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
-import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.media.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.service.neomedia.*;
-import org.jivesoftware.smack.packet.*;
-
-/**
- * <tt>TransportManager</tt>s gather local candidates for incoming and outgoing
- * calls. Their work starts by calling a start method which, using the remote
- * peer's session description, would start the harvest. Calling a second wrapup
- * method would deliver the candidate harvest, possibly after blocking if it has
- * not yet completed.
- *
- * @author Emil Ivov
- * @author Lyubomir Marinov
- */
-public abstract class TransportManagerJabberImpl
- extends TransportManager<CallPeerJabberImpl>
-{
- /**
- * The <tt>Logger</tt> used by the <tt>TransportManagerJabberImpl</tt> class
- * and its instances to print debug messages.
- */
- private static final Logger logger
- = Logger.getLogger(TransportManagerJabberImpl.class);
-
- /**
- * The ID that we will be assigning to our next candidate. We use
- * <tt>int</tt>s for interoperability reasons (Emil: I believe that GTalk
- * uses <tt>int</tt>s. If that turns out not to be the case we can stop
- * using <tt>int</tt>s here if that's an issue).
- */
- private static int nextID = 1;
-
- /**
- * The information pertaining to the Jisti Videobridge conference which the
- * local peer represented by this instance is a focus of. It gives a view of
- * the whole Jitsi Videobridge conference managed by the associated
- * <tt>CallJabberImpl</tt> which provides information specific to this
- * <tt>TransportManager</tt> only.
- */
- private ColibriConferenceIQ colibri;
-
- /**
- * The generation of the candidates we are currently generating
- */
- private int currentGeneration = 0;
-
- /**
- * The indicator which determines whether this <tt>TransportManager</tt>
- * instance is responsible to establish the connectivity with the associated
- * Jitsi Videobridge (in case it is being employed at all).
- */
- boolean isEstablishingConnectivityWithJitsiVideobridge = false;
-
- /**
- * The indicator which determines whether this <tt>TransportManager</tt>
- * instance is yet to start establishing the connectivity with the
- * associated Jitsi Videobridge (in case it is being employed at all).
- */
- boolean startConnectivityEstablishmentWithJitsiVideobridge = false;
-
- /**
- * Creates a new instance of this transport manager, binding it to the
- * specified peer.
- *
- * @param callPeer the {@link CallPeer} whose traffic we will be taking
- * care of.
- */
- protected TransportManagerJabberImpl(CallPeerJabberImpl callPeer)
- {
- super(callPeer);
- }
-
- /**
- * Returns the <tt>InetAddress</tt> that is most likely to be to be used
- * as a next hop when contacting the specified <tt>destination</tt>. This is
- * an utility method that is used whenever we have to choose one of our
- * local addresses to put in the Via, Contact or (in the case of no
- * registrar accounts) From headers.
- *
- * @param peer the CallPeer that we would contact.
- *
- * @return the <tt>InetAddress</tt> that is most likely to be to be used
- * as a next hop when contacting the specified <tt>destination</tt>.
- *
- * @throws IllegalArgumentException if <tt>destination</tt> is not a valid
- * host/IP/FQDN
- */
- @Override
- protected InetAddress getIntendedDestination(CallPeerJabberImpl peer)
- {
- return peer.getProtocolProvider().getNextHop();
- }
-
- /**
- * Returns the ID that we will be assigning to the next candidate we create.
- *
- * @return the next ID to use with a candidate.
- */
- protected String getNextID()
- {
- int nextID;
-
- synchronized (TransportManagerJabberImpl.class)
- {
- nextID = TransportManagerJabberImpl.nextID++;
- }
- return Integer.toString(nextID);
- }
-
- /**
- * Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
- * the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
- * is to have its <tt>target</tt> set to the returned
- * <tt>MediaStreamTarget</tt>
- * @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
- * of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
- */
- public abstract MediaStreamTarget getStreamTarget(MediaType mediaType);
-
- /**
- * Gets the XML namespace of the Jingle transport implemented by this
- * <tt>TransportManagerJabberImpl</tt>.
- *
- * @return the XML namespace of the Jingle transport implemented by this
- * <tt>TransportManagerJabberImpl</tt>
- */
- public abstract String getXmlNamespace();
-
- /**
- * Returns the generation that our current candidates belong to.
- *
- * @return the generation that we should assign to candidates that we are
- * currently advertising.
- */
- protected int getCurrentGeneration()
- {
- return currentGeneration;
- }
-
- /**
- * Increments the generation that we are assigning candidates.
- */
- protected void incrementGeneration()
- {
- currentGeneration++;
- }
-
- /**
- * Sends transport-related information received from the remote peer to the
- * associated Jiitsi Videobridge in order to update the (remote)
- * <tt>ColibriConferenceIQ.Channel</tt> associated with this
- * <tt>TransportManager</tt> instance.
- *
- * @param map a <tt>Map</tt> of media-IceUdpTransportPacketExtension pairs
- * which represents the transport-related information which has been
- * received from the remote peer and which is to be sent to the associated
- * Jitsi Videobridge
- */
- protected void sendTransportInfoToJitsiVideobridge(
- Map<String,IceUdpTransportPacketExtension> map)
- {
- CallPeerJabberImpl peer = getCallPeer();
- boolean initiator = !peer.isInitiator();
- ColibriConferenceIQ conferenceRequest = null;
-
- for (Map.Entry<String,IceUdpTransportPacketExtension> e
- : map.entrySet())
- {
- String media = e.getKey();
- MediaType mediaType = MediaType.parseString(media);
- ColibriConferenceIQ.Channel channel
- = getColibriChannel(mediaType, false /* remote */);
-
- if (channel != null)
- {
- IceUdpTransportPacketExtension transport;
-
- try
- {
- transport = cloneTransportAndCandidates(e.getValue());
- }
- catch (OperationFailedException ofe)
- {
- transport = null;
- }
- if (transport == null)
- continue;
-
- ColibriConferenceIQ.Channel channelRequest
- = new ColibriConferenceIQ.Channel();
-
- channelRequest.setID(channel.getID());
- channelRequest.setInitiator(initiator);
- channelRequest.setTransport(transport);
-
- if (conferenceRequest == null)
- {
- if (colibri == null)
- break;
- else
- {
- String id = colibri.getID();
-
- if ((id == null) || (id.length() == 0))
- break;
- else
- {
- conferenceRequest = new ColibriConferenceIQ();
- conferenceRequest.setID(id);
- conferenceRequest.setTo(colibri.getFrom());
- conferenceRequest.setType(IQ.Type.SET);
- }
- }
- }
- conferenceRequest.getOrCreateContent(media).addChannel(
- channelRequest);
- }
- }
- if (conferenceRequest != null)
- {
- peer.getProtocolProvider().getConnection().sendPacket(
- conferenceRequest);
- }
- }
-
- /**
- * Starts transport candidate harvest for a specific
- * <tt>ContentPacketExtension</tt> that we are going to offer or answer
- * with.
- *
- * @param theirContent the <tt>ContentPacketExtension</tt> offered by the
- * remote peer to which we are going to answer with <tt>ourContent</tt> or
- * <tt>null</tt> if <tt>ourContent</tt> will be an offer to the remote peer
- * @param ourContent the <tt>ContentPacketExtension</tt> for which transport
- * candidate harvest is to be started
- * @param transportInfoSender a <tt>TransportInfoSender</tt> if the
- * harvested transport candidates are to be sent in a
- * <tt>transport-info</tt> rather than in <tt>ourContent</tt>; otherwise,
- * <tt>null</tt>
- * @param media the media of the <tt>RtpDescriptionPacketExtension</tt>
- * child of <tt>ourContent</tt>
- * @return a <tt>PacketExtension</tt> to be added as a child to
- * <tt>ourContent</tt>; otherwise, <tt>null</tt>
- * @throws OperationFailedException if anything goes wrong while starting
- * transport candidate harvest for the specified <tt>ourContent</tt>
- */
- protected abstract PacketExtension startCandidateHarvest(
- ContentPacketExtension theirContent,
- ContentPacketExtension ourContent,
- TransportInfoSender transportInfoSender,
- String media)
- throws OperationFailedException;
-
- /**
- * Starts transport candidate harvest. This method should complete rapidly
- * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
- * are necessary, they should be executed in a separate thread. Candidate
- * harvest would then need to be concluded in the
- * {@link #wrapupCandidateHarvest()} method which would be called once we
- * absolutely need the candidates.
- *
- * @param theirOffer a media description offer that we've received from the
- * remote party and that we should use in case we need to know what
- * transports our peer is using.
- * @param ourAnswer the content descriptions that we should be adding our
- * transport lists to (although not necessarily in this very instance).
- * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
- * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
- * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
- * <tt>TransportManagerJabberImpl</tt> wishes to utilize
- * <tt>transport-info</tt>. Local candidate addresses sent by this
- * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
- * expected to not be included in the result of
- * {@link #wrapupCandidateHarvest()}.
- *
- * @throws OperationFailedException if we fail to allocate a port number.
- */
- public void startCandidateHarvest(
- List<ContentPacketExtension> theirOffer,
- List<ContentPacketExtension> ourAnswer,
- TransportInfoSender transportInfoSender)
- throws OperationFailedException
- {
- CallPeerJabberImpl peer = getCallPeer();
- CallJabberImpl call = peer.getCall();
- boolean isJitsiVideobridge = call.getConference().isJitsiVideobridge();
- List<ContentPacketExtension> cpes
- = (theirOffer == null) ? ourAnswer : theirOffer;
-
- /*
- * If Jitsi Videobridge is to be used, determine which channels are to
- * be allocated and attempt to allocate them now.
- */
- if (isJitsiVideobridge)
- {
- Map<ContentPacketExtension,ContentPacketExtension> contentMap
- = new LinkedHashMap
- <ContentPacketExtension,ContentPacketExtension>();
-
- for (ContentPacketExtension cpe : cpes)
- {
- MediaType mediaType = JingleUtils.getMediaType(cpe);
-
- /*
- * The existence of a content for the mediaType and regardless
- * of the existence of channels in it signals that a channel
- * allocation request has already been sent for that mediaType.
- */
- if ((colibri == null)
- || (colibri.getContent(mediaType.toString()) == null))
- {
- ContentPacketExtension local, remote;
-
- if (cpes == ourAnswer)
- {
- local = cpe;
- remote
- = (theirOffer == null)
- ? null
- : findContentByName(theirOffer, cpe.getName());
- }
- else
- {
- local = findContentByName(ourAnswer, cpe.getName());
- remote = cpe;
- }
- contentMap.put(local, remote);
- }
- }
- if (!contentMap.isEmpty())
- {
- /*
- * We are about to request the channel allocations for the media
- * types found in contentMap. Regardless of the response, we do
- * not want to repeat these requests.
- */
- if (colibri == null)
- colibri = new ColibriConferenceIQ();
- for (Map.Entry<ContentPacketExtension,ContentPacketExtension> e
- : contentMap.entrySet())
- {
- ContentPacketExtension cpe = e.getValue();
-
- if (cpe == null)
- cpe = e.getKey();
-
- colibri.getOrCreateContent(
- JingleUtils.getMediaType(cpe).toString());
- }
-
- ColibriConferenceIQ conferenceResult
- = call.createColibriChannels(peer, contentMap);
-
- if (conferenceResult != null)
- {
- String videobridgeID = colibri.getID();
- String conferenceResultID = conferenceResult.getID();
-
- if (videobridgeID == null)
- colibri.setID(conferenceResultID);
- else if (!videobridgeID.equals(conferenceResultID))
- throw new IllegalStateException("conference.id");
-
- String videobridgeFrom = conferenceResult.getFrom();
-
- if ((videobridgeFrom != null)
- && (videobridgeFrom.length() != 0))
- {
- colibri.setFrom(videobridgeFrom);
- }
-
- for (ColibriConferenceIQ.Content contentResult
- : conferenceResult.getContents())
- {
- ColibriConferenceIQ.Content content
- = colibri.getOrCreateContent(
- contentResult.getName());
-
- for (ColibriConferenceIQ.Channel channelResult
- : contentResult.getChannels())
- {
- if (content.getChannel(channelResult.getID())
- == null)
- {
- content.addChannel(channelResult);
- }
- }
- }
- }
- else
- {
- /*
- * The call fails if the createColibriChannels method fails
- * which may happen if the conference packet times out or it
- * can't be built.
- */
- ProtocolProviderServiceJabberImpl
- .throwOperationFailedException(
- "Failed to allocate colibri channel.",
- OperationFailedException.GENERAL_ERROR,
- null,
- logger);
- }
- }
- }
-
- for (ContentPacketExtension cpe : cpes)
- {
- String contentName = cpe.getName();
- ContentPacketExtension ourContent
- = findContentByName(ourAnswer, contentName);
-
- //it might be that we decided not to reply to this content
- if (ourContent != null)
- {
- ContentPacketExtension theirContent
- = (theirOffer == null)
- ? null
- : findContentByName(theirOffer, contentName);
- RtpDescriptionPacketExtension rtpDesc
- = ourContent.getFirstChildOfType(
- RtpDescriptionPacketExtension.class);
- String media = rtpDesc.getMedia();
- PacketExtension pe
- = startCandidateHarvest(
- theirContent,
- ourContent,
- transportInfoSender,
- media);
-
- if (pe != null)
- ourContent.addChildExtension(pe);
- }
- }
- }
-
- /**
- * Starts transport candidate harvest. This method should complete rapidly
- * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
- * are necessary, they should be executed in a separate thread. Candidate
- * harvest would then need to be concluded in the
- * {@link #wrapupCandidateHarvest()} method which would be called once we
- * absolutely need the candidates.
- *
- * @param ourOffer the content descriptions that we should be adding our
- * transport lists to (although not necessarily in this very instance).
- * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
- * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
- * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
- * <tt>TransportManagerJabberImpl</tt> wishes to utilize
- * <tt>transport-info</tt>. Local candidate addresses sent by this
- * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
- * expected to not be included in the result of
- * {@link #wrapupCandidateHarvest()}.
- * @throws OperationFailedException if we fail to allocate a port number.
- */
- public void startCandidateHarvest(
- List<ContentPacketExtension> ourOffer,
- TransportInfoSender transportInfoSender)
- throws OperationFailedException
- {
- startCandidateHarvest(
- /* theirOffer */ null,
- ourOffer,
- transportInfoSender);
- }
-
- /**
- * Notifies the transport manager that it should conclude candidate
- * harvesting as soon as possible and return the lists of candidates
- * gathered so far.
- *
- * @return the content list that we received earlier (possibly cloned into
- * a new instance) and that we have updated with transport lists.
- */
- public abstract List<ContentPacketExtension> wrapupCandidateHarvest();
-
- /**
- * Looks through the <tt>cpExtList</tt> and returns the {@link
- * ContentPacketExtension} with the specified name.
- *
- * @param cpExtList the list that we will be searching for a specific
- * content.
- * @param name the name of the content element we are looking for.
- * @return the {@link ContentPacketExtension} with the specified name or
- * <tt>null</tt> if no such content element exists.
- */
- public static ContentPacketExtension findContentByName(
- Iterable<ContentPacketExtension> cpExtList,
- String name)
- {
- for(ContentPacketExtension cpExt : cpExtList)
- {
- if(cpExt.getName().equals(name))
- return cpExt;
- }
- return null;
- }
-
- /**
- * Starts the connectivity establishment of this
- * <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between
- * the local and the remote peers given the remote counterpart of the
- * negotiation between them.
- *
- * @param remote the collection of <tt>ContentPacketExtension</tt>s which
- * represents the remote counterpart of the negotiation between the local
- * and the remote peer
- * @return <tt>true</tt> if connectivity establishment has been started in
- * response to the call; otherwise, <tt>false</tt>.
- * <tt>TransportManagerJabberImpl</tt> implementations which do not perform
- * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The
- * default implementation does not perform connectivity checks and always
- * returns <tt>true</tt>.
- */
- public boolean startConnectivityEstablishment(
- Iterable<ContentPacketExtension> remote)
- {
- return true;
- }
-
- /**
- * Starts the connectivity establishment of this
- * <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between
- * the local and the remote peers given the remote counterpart of the
- * negotiation between them.
- *
- * @param remote a <tt>Map</tt> of
- * media-<tt>IceUdpTransportPacketExtension</tt> pairs which represents the
- * remote counterpart of the negotiation between the local and the remote
- * peers
- * @return <tt>true</tt> if connectivity establishment has been started in
- * response to the call; otherwise, <tt>false</tt>.
- * <tt>TransportManagerJabberImpl</tt> implementations which do not perform
- * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The
- * default implementation does not perform connectivity checks and always
- * returns <tt>true</tt>.
- */
- protected boolean startConnectivityEstablishment(
- Map<String,IceUdpTransportPacketExtension> remote)
- {
- return true;
- }
-
- /**
- * Notifies this <tt>TransportManagerJabberImpl</tt> that it should conclude
- * any started connectivity establishment.
- *
- * @throws OperationFailedException if anything goes wrong with connectivity
- * establishment (i.e. ICE failed, ...)
- */
- public void wrapupConnectivityEstablishment()
- throws OperationFailedException
- {
- }
-
- /**
- * Removes a content with a specific name from the transport-related part of
- * the session represented by this <tt>TransportManagerJabberImpl</tt> which
- * may have been reported through previous calls to the
- * <tt>startCandidateHarvest</tt> and
- * <tt>startConnectivityEstablishment</tt> methods.
- * <p>
- * <b>Note</b>: Because <tt>TransportManager</tt> deals with
- * <tt>MediaType</tt>s, not content names and
- * <tt>TransportManagerJabberImpl</tt> does not implement translating from
- * content name to <tt>MediaType</tt>, implementers are expected to call
- * {@link TransportManager#closeStreamConnector(MediaType)}.
- * </p>
- *
- * @param name the name of the content to be removed from the
- * transport-related part of the session represented by this
- * <tt>TransportManagerJabberImpl</tt>
- */
- public abstract void removeContent(String name);
-
- /**
- * Removes a content with a specific name from a specific collection of
- * contents and closes any associated <tt>StreamConnector</tt>.
- *
- * @param contents the collection of contents to remove the content with the
- * specified name from
- * @param name the name of the content to remove
- * @return the removed <tt>ContentPacketExtension</tt> if any; otherwise,
- * <tt>null</tt>
- */
- protected ContentPacketExtension removeContent(
- Iterable<ContentPacketExtension> contents,
- String name)
- {
- for (Iterator<ContentPacketExtension> contentIter = contents.iterator();
- contentIter.hasNext();)
- {
- ContentPacketExtension content = contentIter.next();
-
- if (name.equals(content.getName()))
- {
- contentIter.remove();
-
- // closeStreamConnector
- MediaType mediaType = JingleUtils.getMediaType(content);
- if (mediaType != null)
- {
- closeStreamConnector(mediaType);
- }
-
- return content;
- }
- }
- return null;
- }
-
- /**
- * Clones a specific <tt>IceUdpTransportPacketExtension</tt> and its
- * candidates.
- *
- * @param src the <tt>IceUdpTransportPacketExtension</tt> to be cloned
- * @return a new <tt>IceUdpTransportPacketExtension</tt> instance which has
- * the same run-time type, attributes, namespace, text and candidates as the
- * specified <tt>src</tt>
- * @throws OperationFailedException if an error occurs during the cloing of
- * the specified <tt>src</tt> and its candidates
- */
- static IceUdpTransportPacketExtension cloneTransportAndCandidates(
- IceUdpTransportPacketExtension src)
- throws OperationFailedException
- {
- try
- {
- return IceUdpTransportPacketExtension
- .cloneTransportAndCandidates(src);
- }
- catch (Exception e)
- {
- ProtocolProviderServiceJabberImpl
- .throwOperationFailedException(
- "Failed to close transport and candidates.",
- OperationFailedException.GENERAL_ERROR,
- e,
- logger);
-
- }
- return null;
- }
-
- /**
- * Releases the resources acquired by this <tt>TransportManager</tt> and
- * prepares it for garbage collection.
- */
- public void close()
- {
- for (MediaType mediaType : MediaType.values())
- closeStreamConnector(mediaType);
- }
-
- /**
- * Closes a specific <tt>StreamConnector</tt> associated with a specific
- * <tt>MediaType</tt>. If this <tt>TransportManager</tt> has a reference to
- * the specified <tt>streamConnector</tt>, it remains.
- * Also expires the <tt>ColibriConferenceIQ.Channel</tt> associated with
- * the closed <tt>StreamConnector</tt>.
- *
- * @param mediaType the <tt>MediaType</tt> associated with the specified
- * <tt>streamConnector</tt>
- * @param streamConnector the <tt>StreamConnector</tt> to be closed
- */
- @Override
- protected void closeStreamConnector(
- MediaType mediaType,
- StreamConnector streamConnector)
- {
- try
- {
- boolean superCloseStreamConnector = true;
-
- if (streamConnector instanceof ColibriStreamConnector)
- {
- CallPeerJabberImpl peer = getCallPeer();
-
- if (peer != null)
- {
- CallJabberImpl call = peer.getCall();
-
- if (call != null)
- {
- superCloseStreamConnector = false;
- call.closeColibriStreamConnector(
- peer,
- mediaType,
- (ColibriStreamConnector) streamConnector);
- }
- }
- }
- if (superCloseStreamConnector)
- super.closeStreamConnector(mediaType, streamConnector);
- }
- finally
- {
- /*
- * Expire the ColibriConferenceIQ.Channel associated with the closed
- * StreamConnector.
- */
- if (colibri != null)
- {
- ColibriConferenceIQ.Content content
- = colibri.getContent(mediaType.toString());
-
- if (content != null)
- {
- List<ColibriConferenceIQ.Channel> channels
- = content.getChannels();
-
- if (channels.size() == 2)
- {
- ColibriConferenceIQ requestConferenceIQ
- = new ColibriConferenceIQ();
-
- requestConferenceIQ.setID(colibri.getID());
-
- ColibriConferenceIQ.Content requestContent
- = requestConferenceIQ.getOrCreateContent(
- content.getName());
-
- requestContent.addChannel(channels.get(1 /* remote */));
-
- /*
- * Regardless of whether the request to expire the
- * Channel associated with mediaType succeeds, consider
- * the Channel in question expired. Since
- * RawUdpTransportManager allocates a single channel per
- * MediaType, consider the whole Content expired.
- */
- colibri.removeContent(content);
-
- CallPeerJabberImpl peer = getCallPeer();
-
- if (peer != null)
- {
- CallJabberImpl call = peer.getCall();
-
- if (call != null)
- {
- call.expireColibriChannels(
- peer,
- requestConferenceIQ);
- }
- }
- }
- }
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * Adds support for telephony conferences utilizing the Jitsi Videobridge
- * server-side technology.
- *
- * @see #doCreateStreamConnector(MediaType)
- */
- @Override
- protected StreamConnector createStreamConnector(final MediaType mediaType)
- throws OperationFailedException
- {
- ColibriConferenceIQ.Channel channel
- = getColibriChannel(mediaType, true /* local */);
-
- if (channel != null)
- {
- CallPeerJabberImpl peer = getCallPeer();
- CallJabberImpl call = peer.getCall();
- StreamConnector streamConnector
- = call.createColibriStreamConnector(
- peer,
- mediaType,
- channel,
- new StreamConnectorFactory()
- {
- public StreamConnector createStreamConnector()
- {
- try
- {
- return doCreateStreamConnector(mediaType);
- }
- catch (OperationFailedException ofe)
- {
- return null;
- }
- }
- });
-
- if (streamConnector != null)
- return streamConnector;
- }
-
- return doCreateStreamConnector(mediaType);
- }
-
- protected abstract PacketExtension createTransport(String media)
- throws OperationFailedException;
-
- protected PacketExtension createTransportForStartCandidateHarvest(
- String media)
- throws OperationFailedException
- {
- PacketExtension pe = null;
-
- if (getCallPeer().isJitsiVideobridge())
- {
- MediaType mediaType = MediaType.parseString(media);
- ColibriConferenceIQ.Channel channel
- = getColibriChannel(mediaType, false /* remote */);
-
- if (channel != null)
- pe = cloneTransportAndCandidates(channel.getTransport());
- }
- else
- pe = createTransport(media);
- return pe;
- }
-
- /**
- * Initializes a new <tt>PacketExtension</tt> instance appropriate to the
- * type of Jingle transport represented by this <tt>TransportManager</tt>.
- * The new instance is not initialized with any attributes or child
- * extensions.
- *
- * @return a new <tt>PacketExtension</tt> instance appropriate to the type
- * of Jingle transport represented by this <tt>TransportManager</tt>
- */
- protected abstract PacketExtension createTransportPacketExtension();
-
- /**
- * Creates a media <tt>StreamConnector</tt> for a stream of a specific
- * <tt>MediaType</tt>. The minimum and maximum of the media port boundaries
- * are taken into account.
- *
- * @param mediaType the <tt>MediaType</tt> of the stream for which a
- * <tt>StreamConnector</tt> is to be created
- * @return a <tt>StreamConnector</tt> for the stream of the specified
- * <tt>mediaType</tt>
- * @throws OperationFailedException if the binding of the sockets fails
- */
- protected StreamConnector doCreateStreamConnector(MediaType mediaType)
- throws OperationFailedException
- {
- return super.createStreamConnector(mediaType);
- }
-
- /**
- * Finds a <tt>TransportManagerJabberImpl</tt> participating in a telephony
- * conference utilizing the Jitsi Videobridge server-side technology that
- * this instance is participating in which is establishing the connectivity
- * with the Jitsi Videobridge server (as opposed to a <tt>CallPeer</tt>).
- *
- * @return a <tt>TransportManagerJabberImpl</tt> which is participating in
- * a telephony conference utilizing the Jitsi Videobridge server-side
- * technology that this instance is participating in which is establishing
- * the connectivity with the Jitsi Videobridge server (as opposed to a
- * <tt>CallPeer</tt>).
- */
- TransportManagerJabberImpl
- findTransportManagerEstablishingConnectivityWithJitsiVideobridge()
- {
- Call call = getCallPeer().getCall();
- TransportManagerJabberImpl transportManager = null;
-
- if (call != null)
- {
- CallConference conference = call.getConference();
-
- if ((conference != null) && conference.isJitsiVideobridge())
- {
- for (Call aCall : conference.getCalls())
- {
- Iterator<? extends CallPeer> callPeerIter
- = aCall.getCallPeers();
-
- while (callPeerIter.hasNext())
- {
- CallPeer aCallPeer = callPeerIter.next();
-
- if (aCallPeer instanceof CallPeerJabberImpl)
- {
- TransportManagerJabberImpl aTransportManager
- = ((CallPeerJabberImpl) aCallPeer)
- .getMediaHandler()
- .getTransportManager();
-
- if (aTransportManager
- .isEstablishingConnectivityWithJitsiVideobridge)
- {
- transportManager = aTransportManager;
- break;
- }
- }
- }
- }
- }
- }
- return transportManager;
- }
-
- /**
- * Gets the {@link ColibriConferenceIQ.Channel} which belongs to a content
- * associated with a specific <tt>MediaType</tt> and is to be either locally
- * or remotely used.
- * <p>
- * <b>Note</b>: Modifications to the <tt>ColibriConferenceIQ.Channel</tt>
- * instance returned by the method propagate to (the state of) this
- * instance.
- * </p>
- *
- * @param mediaType the <tt>MediaType</tt> associated with the content which
- * contains the <tt>ColibriConferenceIQ.Channel</tt> to get
- * @param local <tt>true</tt> if the <tt>ColibriConferenceIQ.Channel</tt>
- * which is to be used locally is to be returned or <tt>false</tt> for the
- * one which is to be used remotely
- * @return the <tt>ColibriConferenceIQ.Channel</tt> which belongs to a
- * content associated with the specified <tt>mediaType</tt> and which is to
- * be used in accord with the specified <tt>local</tt> indicator if such a
- * channel exists; otherwise, <tt>null</tt>
- */
- ColibriConferenceIQ.Channel getColibriChannel(
- MediaType mediaType,
- boolean local)
- {
- ColibriConferenceIQ.Channel channel = null;
-
- if (colibri != null)
- {
- ColibriConferenceIQ.Content content
- = colibri.getContent(mediaType.toString());
-
- if (content != null)
- {
- List<ColibriConferenceIQ.Channel> channels
- = content.getChannels();
-
- if (channels.size() == 2)
- channel = channels.get(local ? 0 : 1);
- }
- }
- return channel;
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.net.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.colibri.*;
+import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
+import net.java.sip.communicator.impl.protocol.jabber.jinglesdp.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.media.*;
+import net.java.sip.communicator.util.*;
+
+import org.jitsi.service.neomedia.*;
+import org.jivesoftware.smack.packet.*;
+
+/**
+ * <tt>TransportManager</tt>s gather local candidates for incoming and outgoing
+ * calls. Their work starts by calling a start method which, using the remote
+ * peer's session description, would start the harvest. Calling a second wrapup
+ * method would deliver the candidate harvest, possibly after blocking if it has
+ * not yet completed.
+ *
+ * @author Emil Ivov
+ * @author Lyubomir Marinov
+ */
+public abstract class TransportManagerJabberImpl
+ extends TransportManager<CallPeerJabberImpl>
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>TransportManagerJabberImpl</tt> class
+ * and its instances to print debug messages.
+ */
+ private static final Logger logger
+ = Logger.getLogger(TransportManagerJabberImpl.class);
+
+ /**
+ * The ID that we will be assigning to our next candidate. We use
+ * <tt>int</tt>s for interoperability reasons (Emil: I believe that GTalk
+ * uses <tt>int</tt>s. If that turns out not to be the case we can stop
+ * using <tt>int</tt>s here if that's an issue).
+ */
+ private static int nextID = 1;
+
+ /**
+ * The information pertaining to the Jisti Videobridge conference which the
+ * local peer represented by this instance is a focus of. It gives a view of
+ * the whole Jitsi Videobridge conference managed by the associated
+ * <tt>CallJabberImpl</tt> which provides information specific to this
+ * <tt>TransportManager</tt> only.
+ */
+ private ColibriConferenceIQ colibri;
+
+ /**
+ * The generation of the candidates we are currently generating
+ */
+ private int currentGeneration = 0;
+
+ /**
+ * The indicator which determines whether this <tt>TransportManager</tt>
+ * instance is responsible to establish the connectivity with the associated
+ * Jitsi Videobridge (in case it is being employed at all).
+ */
+ boolean isEstablishingConnectivityWithJitsiVideobridge = false;
+
+ /**
+ * The indicator which determines whether this <tt>TransportManager</tt>
+ * instance is yet to start establishing the connectivity with the
+ * associated Jitsi Videobridge (in case it is being employed at all).
+ */
+ boolean startConnectivityEstablishmentWithJitsiVideobridge = false;
+
+ /**
+ * Creates a new instance of this transport manager, binding it to the
+ * specified peer.
+ *
+ * @param callPeer the {@link CallPeer} whose traffic we will be taking
+ * care of.
+ */
+ protected TransportManagerJabberImpl(CallPeerJabberImpl callPeer)
+ {
+ super(callPeer);
+ }
+
+ /**
+ * Returns the <tt>InetAddress</tt> that is most likely to be to be used
+ * as a next hop when contacting the specified <tt>destination</tt>. This is
+ * an utility method that is used whenever we have to choose one of our
+ * local addresses to put in the Via, Contact or (in the case of no
+ * registrar accounts) From headers.
+ *
+ * @param peer the CallPeer that we would contact.
+ *
+ * @return the <tt>InetAddress</tt> that is most likely to be to be used
+ * as a next hop when contacting the specified <tt>destination</tt>.
+ *
+ * @throws IllegalArgumentException if <tt>destination</tt> is not a valid
+ * host/IP/FQDN
+ */
+ @Override
+ protected InetAddress getIntendedDestination(CallPeerJabberImpl peer)
+ {
+ return peer.getProtocolProvider().getNextHop();
+ }
+
+ /**
+ * Returns the ID that we will be assigning to the next candidate we create.
+ *
+ * @return the next ID to use with a candidate.
+ */
+ protected String getNextID()
+ {
+ int nextID;
+
+ synchronized (TransportManagerJabberImpl.class)
+ {
+ nextID = TransportManagerJabberImpl.nextID++;
+ }
+ return Integer.toString(nextID);
+ }
+
+ /**
+ * Gets the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt> of
+ * the <tt>MediaStream</tt> with a specific <tt>MediaType</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> of the <tt>MediaStream</tt> which
+ * is to have its <tt>target</tt> set to the returned
+ * <tt>MediaStreamTarget</tt>
+ * @return the <tt>MediaStreamTarget</tt> to be used as the <tt>target</tt>
+ * of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
+ */
+ public abstract MediaStreamTarget getStreamTarget(MediaType mediaType);
+
+ /**
+ * Gets the XML namespace of the Jingle transport implemented by this
+ * <tt>TransportManagerJabberImpl</tt>.
+ *
+ * @return the XML namespace of the Jingle transport implemented by this
+ * <tt>TransportManagerJabberImpl</tt>
+ */
+ public abstract String getXmlNamespace();
+
+ /**
+ * Returns the generation that our current candidates belong to.
+ *
+ * @return the generation that we should assign to candidates that we are
+ * currently advertising.
+ */
+ protected int getCurrentGeneration()
+ {
+ return currentGeneration;
+ }
+
+ /**
+ * Increments the generation that we are assigning candidates.
+ */
+ protected void incrementGeneration()
+ {
+ currentGeneration++;
+ }
+
+ /**
+ * Sends transport-related information received from the remote peer to the
+ * associated Jiitsi Videobridge in order to update the (remote)
+ * <tt>ColibriConferenceIQ.Channel</tt> associated with this
+ * <tt>TransportManager</tt> instance.
+ *
+ * @param map a <tt>Map</tt> of media-IceUdpTransportPacketExtension pairs
+ * which represents the transport-related information which has been
+ * received from the remote peer and which is to be sent to the associated
+ * Jitsi Videobridge
+ */
+ protected void sendTransportInfoToJitsiVideobridge(
+ Map<String,IceUdpTransportPacketExtension> map)
+ {
+ CallPeerJabberImpl peer = getCallPeer();
+ boolean initiator = !peer.isInitiator();
+ ColibriConferenceIQ conferenceRequest = null;
+
+ for (Map.Entry<String,IceUdpTransportPacketExtension> e
+ : map.entrySet())
+ {
+ String media = e.getKey();
+ MediaType mediaType = MediaType.parseString(media);
+ ColibriConferenceIQ.Channel channel
+ = getColibriChannel(mediaType, false /* remote */);
+
+ if (channel != null)
+ {
+ IceUdpTransportPacketExtension transport;
+
+ try
+ {
+ transport = cloneTransportAndCandidates(e.getValue());
+ }
+ catch (OperationFailedException ofe)
+ {
+ transport = null;
+ }
+ if (transport == null)
+ continue;
+
+ ColibriConferenceIQ.Channel channelRequest
+ = new ColibriConferenceIQ.Channel();
+
+ channelRequest.setID(channel.getID());
+ channelRequest.setInitiator(initiator);
+ channelRequest.setTransport(transport);
+
+ if (conferenceRequest == null)
+ {
+ if (colibri == null)
+ break;
+ else
+ {
+ String id = colibri.getID();
+
+ if ((id == null) || (id.length() == 0))
+ break;
+ else
+ {
+ conferenceRequest = new ColibriConferenceIQ();
+ conferenceRequest.setID(id);
+ conferenceRequest.setTo(colibri.getFrom());
+ conferenceRequest.setType(IQ.Type.SET);
+ }
+ }
+ }
+ conferenceRequest.getOrCreateContent(media).addChannel(
+ channelRequest);
+ }
+ }
+ if (conferenceRequest != null)
+ {
+ peer.getProtocolProvider().getConnection().sendPacket(
+ conferenceRequest);
+ }
+ }
+
+ /**
+ * Starts transport candidate harvest for a specific
+ * <tt>ContentPacketExtension</tt> that we are going to offer or answer
+ * with.
+ *
+ * @param theirContent the <tt>ContentPacketExtension</tt> offered by the
+ * remote peer to which we are going to answer with <tt>ourContent</tt> or
+ * <tt>null</tt> if <tt>ourContent</tt> will be an offer to the remote peer
+ * @param ourContent the <tt>ContentPacketExtension</tt> for which transport
+ * candidate harvest is to be started
+ * @param transportInfoSender a <tt>TransportInfoSender</tt> if the
+ * harvested transport candidates are to be sent in a
+ * <tt>transport-info</tt> rather than in <tt>ourContent</tt>; otherwise,
+ * <tt>null</tt>
+ * @param media the media of the <tt>RtpDescriptionPacketExtension</tt>
+ * child of <tt>ourContent</tt>
+ * @return a <tt>PacketExtension</tt> to be added as a child to
+ * <tt>ourContent</tt>; otherwise, <tt>null</tt>
+ * @throws OperationFailedException if anything goes wrong while starting
+ * transport candidate harvest for the specified <tt>ourContent</tt>
+ */
+ protected abstract PacketExtension startCandidateHarvest(
+ ContentPacketExtension theirContent,
+ ContentPacketExtension ourContent,
+ TransportInfoSender transportInfoSender,
+ String media)
+ throws OperationFailedException;
+
+ /**
+ * Starts transport candidate harvest. This method should complete rapidly
+ * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
+ * are necessary, they should be executed in a separate thread. Candidate
+ * harvest would then need to be concluded in the
+ * {@link #wrapupCandidateHarvest()} method which would be called once we
+ * absolutely need the candidates.
+ *
+ * @param theirOffer a media description offer that we've received from the
+ * remote party and that we should use in case we need to know what
+ * transports our peer is using.
+ * @param ourAnswer the content descriptions that we should be adding our
+ * transport lists to (although not necessarily in this very instance).
+ * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
+ * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
+ * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
+ * <tt>TransportManagerJabberImpl</tt> wishes to utilize
+ * <tt>transport-info</tt>. Local candidate addresses sent by this
+ * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
+ * expected to not be included in the result of
+ * {@link #wrapupCandidateHarvest()}.
+ *
+ * @throws OperationFailedException if we fail to allocate a port number.
+ */
+ public void startCandidateHarvest(
+ List<ContentPacketExtension> theirOffer,
+ List<ContentPacketExtension> ourAnswer,
+ TransportInfoSender transportInfoSender)
+ throws OperationFailedException
+ {
+ CallPeerJabberImpl peer = getCallPeer();
+ CallJabberImpl call = peer.getCall();
+ boolean isJitsiVideobridge = call.getConference().isJitsiVideobridge();
+ List<ContentPacketExtension> cpes
+ = (theirOffer == null) ? ourAnswer : theirOffer;
+
+ /*
+ * If Jitsi Videobridge is to be used, determine which channels are to
+ * be allocated and attempt to allocate them now.
+ */
+ if (isJitsiVideobridge)
+ {
+ Map<ContentPacketExtension,ContentPacketExtension> contentMap
+ = new LinkedHashMap
+ <ContentPacketExtension,ContentPacketExtension>();
+
+ for (ContentPacketExtension cpe : cpes)
+ {
+ MediaType mediaType = JingleUtils.getMediaType(cpe);
+
+ /*
+ * The existence of a content for the mediaType and regardless
+ * of the existence of channels in it signals that a channel
+ * allocation request has already been sent for that mediaType.
+ */
+ if ((colibri == null)
+ || (colibri.getContent(mediaType.toString()) == null))
+ {
+ ContentPacketExtension local, remote;
+
+ if (cpes == ourAnswer)
+ {
+ local = cpe;
+ remote
+ = (theirOffer == null)
+ ? null
+ : findContentByName(theirOffer, cpe.getName());
+ }
+ else
+ {
+ local = findContentByName(ourAnswer, cpe.getName());
+ remote = cpe;
+ }
+ contentMap.put(local, remote);
+ }
+ }
+ if (!contentMap.isEmpty())
+ {
+ /*
+ * We are about to request the channel allocations for the media
+ * types found in contentMap. Regardless of the response, we do
+ * not want to repeat these requests.
+ */
+ if (colibri == null)
+ colibri = new ColibriConferenceIQ();
+ for (Map.Entry<ContentPacketExtension,ContentPacketExtension> e
+ : contentMap.entrySet())
+ {
+ ContentPacketExtension cpe = e.getValue();
+
+ if (cpe == null)
+ cpe = e.getKey();
+
+ colibri.getOrCreateContent(
+ JingleUtils.getMediaType(cpe).toString());
+ }
+
+ ColibriConferenceIQ conferenceResult
+ = call.createColibriChannels(peer, contentMap);
+
+ if (conferenceResult != null)
+ {
+ String videobridgeID = colibri.getID();
+ String conferenceResultID = conferenceResult.getID();
+
+ if (videobridgeID == null)
+ colibri.setID(conferenceResultID);
+ else if (!videobridgeID.equals(conferenceResultID))
+ throw new IllegalStateException("conference.id");
+
+ String videobridgeFrom = conferenceResult.getFrom();
+
+ if ((videobridgeFrom != null)
+ && (videobridgeFrom.length() != 0))
+ {
+ colibri.setFrom(videobridgeFrom);
+ }
+
+ for (ColibriConferenceIQ.Content contentResult
+ : conferenceResult.getContents())
+ {
+ ColibriConferenceIQ.Content content
+ = colibri.getOrCreateContent(
+ contentResult.getName());
+
+ for (ColibriConferenceIQ.Channel channelResult
+ : contentResult.getChannels())
+ {
+ if (content.getChannel(channelResult.getID())
+ == null)
+ {
+ content.addChannel(channelResult);
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * The call fails if the createColibriChannels method fails
+ * which may happen if the conference packet times out or it
+ * can't be built.
+ */
+ ProtocolProviderServiceJabberImpl
+ .throwOperationFailedException(
+ "Failed to allocate colibri channel.",
+ OperationFailedException.GENERAL_ERROR,
+ null,
+ logger);
+ }
+ }
+ }
+
+ for (ContentPacketExtension cpe : cpes)
+ {
+ String contentName = cpe.getName();
+ ContentPacketExtension ourContent
+ = findContentByName(ourAnswer, contentName);
+
+ //it might be that we decided not to reply to this content
+ if (ourContent != null)
+ {
+ ContentPacketExtension theirContent
+ = (theirOffer == null)
+ ? null
+ : findContentByName(theirOffer, contentName);
+ RtpDescriptionPacketExtension rtpDesc
+ = ourContent.getFirstChildOfType(
+ RtpDescriptionPacketExtension.class);
+ String media = rtpDesc.getMedia();
+ PacketExtension pe
+ = startCandidateHarvest(
+ theirContent,
+ ourContent,
+ transportInfoSender,
+ media);
+
+ if (pe != null)
+ ourContent.addChildExtension(pe);
+ }
+ }
+ }
+
+ /**
+ * Starts transport candidate harvest. This method should complete rapidly
+ * and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
+ * are necessary, they should be executed in a separate thread. Candidate
+ * harvest would then need to be concluded in the
+ * {@link #wrapupCandidateHarvest()} method which would be called once we
+ * absolutely need the candidates.
+ *
+ * @param ourOffer the content descriptions that we should be adding our
+ * transport lists to (although not necessarily in this very instance).
+ * @param transportInfoSender the <tt>TransportInfoSender</tt> to be used by
+ * this <tt>TransportManagerJabberImpl</tt> to send <tt>transport-info</tt>
+ * <tt>JingleIQ</tt>s from the local peer to the remote peer if this
+ * <tt>TransportManagerJabberImpl</tt> wishes to utilize
+ * <tt>transport-info</tt>. Local candidate addresses sent by this
+ * <tt>TransportManagerJabberImpl</tt> in <tt>transport-info</tt> are
+ * expected to not be included in the result of
+ * {@link #wrapupCandidateHarvest()}.
+ * @throws OperationFailedException if we fail to allocate a port number.
+ */
+ public void startCandidateHarvest(
+ List<ContentPacketExtension> ourOffer,
+ TransportInfoSender transportInfoSender)
+ throws OperationFailedException
+ {
+ startCandidateHarvest(
+ /* theirOffer */ null,
+ ourOffer,
+ transportInfoSender);
+ }
+
+ /**
+ * Notifies the transport manager that it should conclude candidate
+ * harvesting as soon as possible and return the lists of candidates
+ * gathered so far.
+ *
+ * @return the content list that we received earlier (possibly cloned into
+ * a new instance) and that we have updated with transport lists.
+ */
+ public abstract List<ContentPacketExtension> wrapupCandidateHarvest();
+
+ /**
+ * Looks through the <tt>cpExtList</tt> and returns the {@link
+ * ContentPacketExtension} with the specified name.
+ *
+ * @param cpExtList the list that we will be searching for a specific
+ * content.
+ * @param name the name of the content element we are looking for.
+ * @return the {@link ContentPacketExtension} with the specified name or
+ * <tt>null</tt> if no such content element exists.
+ */
+ public static ContentPacketExtension findContentByName(
+ Iterable<ContentPacketExtension> cpExtList,
+ String name)
+ {
+ for(ContentPacketExtension cpExt : cpExtList)
+ {
+ if(cpExt.getName().equals(name))
+ return cpExt;
+ }
+ return null;
+ }
+
+ /**
+ * Starts the connectivity establishment of this
+ * <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between
+ * the local and the remote peers given the remote counterpart of the
+ * negotiation between them.
+ *
+ * @param remote the collection of <tt>ContentPacketExtension</tt>s which
+ * represents the remote counterpart of the negotiation between the local
+ * and the remote peer
+ * @return <tt>true</tt> if connectivity establishment has been started in
+ * response to the call; otherwise, <tt>false</tt>.
+ * <tt>TransportManagerJabberImpl</tt> implementations which do not perform
+ * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The
+ * default implementation does not perform connectivity checks and always
+ * returns <tt>true</tt>.
+ */
+ public boolean startConnectivityEstablishment(
+ Iterable<ContentPacketExtension> remote)
+ {
+ return true;
+ }
+
+ /**
+ * Starts the connectivity establishment of this
+ * <tt>TransportManagerJabberImpl</tt> i.e. checks the connectivity between
+ * the local and the remote peers given the remote counterpart of the
+ * negotiation between them.
+ *
+ * @param remote a <tt>Map</tt> of
+ * media-<tt>IceUdpTransportPacketExtension</tt> pairs which represents the
+ * remote counterpart of the negotiation between the local and the remote
+ * peers
+ * @return <tt>true</tt> if connectivity establishment has been started in
+ * response to the call; otherwise, <tt>false</tt>.
+ * <tt>TransportManagerJabberImpl</tt> implementations which do not perform
+ * connectivity checks (e.g. raw UDP) should return <tt>true</tt>. The
+ * default implementation does not perform connectivity checks and always
+ * returns <tt>true</tt>.
+ */
+ protected boolean startConnectivityEstablishment(
+ Map<String,IceUdpTransportPacketExtension> remote)
+ {
+ return true;
+ }
+
+ /**
+ * Notifies this <tt>TransportManagerJabberImpl</tt> that it should conclude
+ * any started connectivity establishment.
+ *
+ * @throws OperationFailedException if anything goes wrong with connectivity
+ * establishment (i.e. ICE failed, ...)
+ */
+ public void wrapupConnectivityEstablishment()
+ throws OperationFailedException
+ {
+ }
+
+ /**
+ * Removes a content with a specific name from the transport-related part of
+ * the session represented by this <tt>TransportManagerJabberImpl</tt> which
+ * may have been reported through previous calls to the
+ * <tt>startCandidateHarvest</tt> and
+ * <tt>startConnectivityEstablishment</tt> methods.
+ * <p>
+ * <b>Note</b>: Because <tt>TransportManager</tt> deals with
+ * <tt>MediaType</tt>s, not content names and
+ * <tt>TransportManagerJabberImpl</tt> does not implement translating from
+ * content name to <tt>MediaType</tt>, implementers are expected to call
+ * {@link TransportManager#closeStreamConnector(MediaType)}.
+ * </p>
+ *
+ * @param name the name of the content to be removed from the
+ * transport-related part of the session represented by this
+ * <tt>TransportManagerJabberImpl</tt>
+ */
+ public abstract void removeContent(String name);
+
+ /**
+ * Removes a content with a specific name from a specific collection of
+ * contents and closes any associated <tt>StreamConnector</tt>.
+ *
+ * @param contents the collection of contents to remove the content with the
+ * specified name from
+ * @param name the name of the content to remove
+ * @return the removed <tt>ContentPacketExtension</tt> if any; otherwise,
+ * <tt>null</tt>
+ */
+ protected ContentPacketExtension removeContent(
+ Iterable<ContentPacketExtension> contents,
+ String name)
+ {
+ for (Iterator<ContentPacketExtension> contentIter = contents.iterator();
+ contentIter.hasNext();)
+ {
+ ContentPacketExtension content = contentIter.next();
+
+ if (name.equals(content.getName()))
+ {
+ contentIter.remove();
+
+ // closeStreamConnector
+ MediaType mediaType = JingleUtils.getMediaType(content);
+ if (mediaType != null)
+ {
+ closeStreamConnector(mediaType);
+ }
+
+ return content;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Clones a specific <tt>IceUdpTransportPacketExtension</tt> and its
+ * candidates.
+ *
+ * @param src the <tt>IceUdpTransportPacketExtension</tt> to be cloned
+ * @return a new <tt>IceUdpTransportPacketExtension</tt> instance which has
+ * the same run-time type, attributes, namespace, text and candidates as the
+ * specified <tt>src</tt>
+ * @throws OperationFailedException if an error occurs during the cloing of
+ * the specified <tt>src</tt> and its candidates
+ */
+ static IceUdpTransportPacketExtension cloneTransportAndCandidates(
+ IceUdpTransportPacketExtension src)
+ throws OperationFailedException
+ {
+ try
+ {
+ return IceUdpTransportPacketExtension
+ .cloneTransportAndCandidates(src);
+ }
+ catch (Exception e)
+ {
+ ProtocolProviderServiceJabberImpl
+ .throwOperationFailedException(
+ "Failed to close transport and candidates.",
+ OperationFailedException.GENERAL_ERROR,
+ e,
+ logger);
+
+ }
+ return null;
+ }
+
+ /**
+ * Releases the resources acquired by this <tt>TransportManager</tt> and
+ * prepares it for garbage collection.
+ */
+ public void close()
+ {
+ for (MediaType mediaType : MediaType.values())
+ closeStreamConnector(mediaType);
+ }
+
+ /**
+ * Closes a specific <tt>StreamConnector</tt> associated with a specific
+ * <tt>MediaType</tt>. If this <tt>TransportManager</tt> has a reference to
+ * the specified <tt>streamConnector</tt>, it remains.
+ * Also expires the <tt>ColibriConferenceIQ.Channel</tt> associated with
+ * the closed <tt>StreamConnector</tt>.
+ *
+ * @param mediaType the <tt>MediaType</tt> associated with the specified
+ * <tt>streamConnector</tt>
+ * @param streamConnector the <tt>StreamConnector</tt> to be closed
+ */
+ @Override
+ protected void closeStreamConnector(
+ MediaType mediaType,
+ StreamConnector streamConnector)
+ {
+ try
+ {
+ boolean superCloseStreamConnector = true;
+
+ if (streamConnector instanceof ColibriStreamConnector)
+ {
+ CallPeerJabberImpl peer = getCallPeer();
+
+ if (peer != null)
+ {
+ CallJabberImpl call = peer.getCall();
+
+ if (call != null)
+ {
+ superCloseStreamConnector = false;
+ call.closeColibriStreamConnector(
+ peer,
+ mediaType,
+ (ColibriStreamConnector) streamConnector);
+ }
+ }
+ }
+ if (superCloseStreamConnector)
+ super.closeStreamConnector(mediaType, streamConnector);
+ }
+ finally
+ {
+ /*
+ * Expire the ColibriConferenceIQ.Channel associated with the closed
+ * StreamConnector.
+ */
+ if (colibri != null)
+ {
+ ColibriConferenceIQ.Content content
+ = colibri.getContent(mediaType.toString());
+
+ if (content != null)
+ {
+ List<ColibriConferenceIQ.Channel> channels
+ = content.getChannels();
+
+ if (channels.size() == 2)
+ {
+ ColibriConferenceIQ requestConferenceIQ
+ = new ColibriConferenceIQ();
+
+ requestConferenceIQ.setID(colibri.getID());
+
+ ColibriConferenceIQ.Content requestContent
+ = requestConferenceIQ.getOrCreateContent(
+ content.getName());
+
+ requestContent.addChannel(channels.get(1 /* remote */));
+
+ /*
+ * Regardless of whether the request to expire the
+ * Channel associated with mediaType succeeds, consider
+ * the Channel in question expired. Since
+ * RawUdpTransportManager allocates a single channel per
+ * MediaType, consider the whole Content expired.
+ */
+ colibri.removeContent(content);
+
+ CallPeerJabberImpl peer = getCallPeer();
+
+ if (peer != null)
+ {
+ CallJabberImpl call = peer.getCall();
+
+ if (call != null)
+ {
+ call.expireColibriChannels(
+ peer,
+ requestConferenceIQ);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Adds support for telephony conferences utilizing the Jitsi Videobridge
+ * server-side technology.
+ *
+ * @see #doCreateStreamConnector(MediaType)
+ */
+ @Override
+ protected StreamConnector createStreamConnector(final MediaType mediaType)
+ throws OperationFailedException
+ {
+ ColibriConferenceIQ.Channel channel
+ = getColibriChannel(mediaType, true /* local */);
+
+ if (channel != null)
+ {
+ CallPeerJabberImpl peer = getCallPeer();
+ CallJabberImpl call = peer.getCall();
+ StreamConnector streamConnector
+ = call.createColibriStreamConnector(
+ peer,
+ mediaType,
+ channel,
+ new StreamConnectorFactory()
+ {
+ public StreamConnector createStreamConnector()
+ {
+ try
+ {
+ return doCreateStreamConnector(mediaType);
+ }
+ catch (OperationFailedException ofe)
+ {
+ return null;
+ }
+ }
+ });
+
+ if (streamConnector != null)
+ return streamConnector;
+ }
+
+ return doCreateStreamConnector(mediaType);
+ }
+
+ protected abstract PacketExtension createTransport(String media)
+ throws OperationFailedException;
+
+ protected PacketExtension createTransportForStartCandidateHarvest(
+ String media)
+ throws OperationFailedException
+ {
+ PacketExtension pe = null;
+
+ if (getCallPeer().isJitsiVideobridge())
+ {
+ MediaType mediaType = MediaType.parseString(media);
+ ColibriConferenceIQ.Channel channel
+ = getColibriChannel(mediaType, false /* remote */);
+
+ if (channel != null)
+ pe = cloneTransportAndCandidates(channel.getTransport());
+ }
+ else
+ pe = createTransport(media);
+ return pe;
+ }
+
+ /**
+ * Initializes a new <tt>PacketExtension</tt> instance appropriate to the
+ * type of Jingle transport represented by this <tt>TransportManager</tt>.
+ * The new instance is not initialized with any attributes or child
+ * extensions.
+ *
+ * @return a new <tt>PacketExtension</tt> instance appropriate to the type
+ * of Jingle transport represented by this <tt>TransportManager</tt>
+ */
+ protected abstract PacketExtension createTransportPacketExtension();
+
+ /**
+ * Creates a media <tt>StreamConnector</tt> for a stream of a specific
+ * <tt>MediaType</tt>. The minimum and maximum of the media port boundaries
+ * are taken into account.
+ *
+ * @param mediaType the <tt>MediaType</tt> of the stream for which a
+ * <tt>StreamConnector</tt> is to be created
+ * @return a <tt>StreamConnector</tt> for the stream of the specified
+ * <tt>mediaType</tt>
+ * @throws OperationFailedException if the binding of the sockets fails
+ */
+ protected StreamConnector doCreateStreamConnector(MediaType mediaType)
+ throws OperationFailedException
+ {
+ return super.createStreamConnector(mediaType);
+ }
+
+ /**
+ * Finds a <tt>TransportManagerJabberImpl</tt> participating in a telephony
+ * conference utilizing the Jitsi Videobridge server-side technology that
+ * this instance is participating in which is establishing the connectivity
+ * with the Jitsi Videobridge server (as opposed to a <tt>CallPeer</tt>).
+ *
+ * @return a <tt>TransportManagerJabberImpl</tt> which is participating in
+ * a telephony conference utilizing the Jitsi Videobridge server-side
+ * technology that this instance is participating in which is establishing
+ * the connectivity with the Jitsi Videobridge server (as opposed to a
+ * <tt>CallPeer</tt>).
+ */
+ TransportManagerJabberImpl
+ findTransportManagerEstablishingConnectivityWithJitsiVideobridge()
+ {
+ Call call = getCallPeer().getCall();
+ TransportManagerJabberImpl transportManager = null;
+
+ if (call != null)
+ {
+ CallConference conference = call.getConference();
+
+ if ((conference != null) && conference.isJitsiVideobridge())
+ {
+ for (Call aCall : conference.getCalls())
+ {
+ Iterator<? extends CallPeer> callPeerIter
+ = aCall.getCallPeers();
+
+ while (callPeerIter.hasNext())
+ {
+ CallPeer aCallPeer = callPeerIter.next();
+
+ if (aCallPeer instanceof CallPeerJabberImpl)
+ {
+ TransportManagerJabberImpl aTransportManager
+ = ((CallPeerJabberImpl) aCallPeer)
+ .getMediaHandler()
+ .getTransportManager();
+
+ if (aTransportManager
+ .isEstablishingConnectivityWithJitsiVideobridge)
+ {
+ transportManager = aTransportManager;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return transportManager;
+ }
+
+ /**
+ * Gets the {@link ColibriConferenceIQ.Channel} which belongs to a content
+ * associated with a specific <tt>MediaType</tt> and is to be either locally
+ * or remotely used.
+ * <p>
+ * <b>Note</b>: Modifications to the <tt>ColibriConferenceIQ.Channel</tt>
+ * instance returned by the method propagate to (the state of) this
+ * instance.
+ * </p>
+ *
+ * @param mediaType the <tt>MediaType</tt> associated with the content which
+ * contains the <tt>ColibriConferenceIQ.Channel</tt> to get
+ * @param local <tt>true</tt> if the <tt>ColibriConferenceIQ.Channel</tt>
+ * which is to be used locally is to be returned or <tt>false</tt> for the
+ * one which is to be used remotely
+ * @return the <tt>ColibriConferenceIQ.Channel</tt> which belongs to a
+ * content associated with the specified <tt>mediaType</tt> and which is to
+ * be used in accord with the specified <tt>local</tt> indicator if such a
+ * channel exists; otherwise, <tt>null</tt>
+ */
+ ColibriConferenceIQ.Channel getColibriChannel(
+ MediaType mediaType,
+ boolean local)
+ {
+ ColibriConferenceIQ.Channel channel = null;
+
+ if (colibri != null)
+ {
+ ColibriConferenceIQ.Content content
+ = colibri.getContent(mediaType.toString());
+
+ if (content != null)
+ {
+ List<ColibriConferenceIQ.Channel> channels
+ = content.getChannels();
+
+ if (channels.size() == 2)
+ channel = channels.get(local ? 0 : 1);
+ }
+ }
+ return channel;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java
index 1285581..778d086 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/DefaultPacketExtensionProvider.java
@@ -19,6 +19,7 @@ package net.java.sip.communicator.impl.protocol.jabber.extensions;
import java.util.logging.*;
+import net.java.sip.communicator.service.protocol.jabber.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.*;
@@ -41,6 +42,13 @@ public class DefaultPacketExtensionProvider<C extends AbstractPacketExtension>
.getLogger(DefaultPacketExtensionProvider.class.getName());
/**
+ * The <tt>AbstractSmackInteroperabilityLayer</tt> instance implementing
+ * necessary methods
+ */
+ private AbstractSmackInteroperabilityLayer smackInteroperabilityLayer =
+ AbstractSmackInteroperabilityLayer.getInstance();
+
+ /**
* The {@link Class} that the packets we will be parsing here belong to.
*/
private final Class<C> packetClass;
@@ -100,8 +108,7 @@ public class DefaultPacketExtensionProvider<C extends AbstractPacketExtension>
if (eventType == XmlPullParser.START_TAG)
{
- PacketExtensionProvider provider
- = (PacketExtensionProvider)ProviderManager.getInstance()
+ PacketExtensionProvider provider = smackInteroperabilityLayer
.getExtensionProvider( elementName, namespace );
if(provider == null)
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java
index ebdcbb0..97d9ff8 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/EntityCapsManager.java
@@ -243,6 +243,7 @@ public class EntityCapsManager
if ((user != null) && (node != null) && (hash != null) && (ver != null))
{
Caps caps = userCaps.get(user);
+ String bareJid=StringUtils.parseBareAddress(user);
if ((caps == null)
|| !caps.node.equals(node)
@@ -270,7 +271,9 @@ public class EntityCapsManager
String nodeVer = caps.getNodeVer();
for (UserCapsNodeListener listener : listeners)
- listener.userCapsNodeAdded(user, nodeVer, online);
+ listener.userCapsNodeAdded(user,
+ getFullJidsByBareJid(bareJid),
+ nodeVer, online);
}
}
}
@@ -305,6 +308,8 @@ public class EntityCapsManager
{
Caps caps = null;
String lastRemovedJid = null;
+ String bareJid=StringUtils.parseBareAddress(
+ contact.getAddress());
Iterator<String> iter = userCaps.keySet().iterator();
while(iter.hasNext())
@@ -337,7 +342,9 @@ public class EntityCapsManager
for (UserCapsNodeListener listener : listeners)
listener.userCapsNodeRemoved(
- lastRemovedJid, nodeVer, false);
+ lastRemovedJid,
+ getFullJidsByBareJid(bareJid),
+ nodeVer, false);
}
}
}
@@ -350,6 +357,7 @@ public class EntityCapsManager
public void removeUserCapsNode(String user)
{
Caps caps = userCaps.remove(user);
+ String bareJid=StringUtils.parseBareAddress(user);
// Fire userCapsNodeRemoved.
if (caps != null)
@@ -367,7 +375,9 @@ public class EntityCapsManager
String nodeVer = caps.getNodeVer();
for (UserCapsNodeListener listener : listeners)
- listener.userCapsNodeRemoved(user, nodeVer, false);
+ listener.userCapsNodeRemoved(user,
+ getFullJidsByBareJid(bareJid),
+ nodeVer, false);
}
}
}
@@ -404,6 +414,24 @@ public class EntityCapsManager
{
return userCaps.get(user);
}
+
+ /**
+ * Gets the full Jids (with resources) as Strings.
+ *
+ * @param the bare Jid
+ * @return the full Jids as an ArrayList <tt>user</tt>
+ */
+ public ArrayList<String> getFullJidsByBareJid(String bareJid)
+ {
+ ArrayList<String> jids = new ArrayList<String>();
+ for(String jid: userCaps.keySet())
+ {
+ if(bareJid.equals(StringUtils.parseBareAddress(jid))){
+ jids.add(jid);
+ }
+ }
+ return jids;
+ }
/**
* Get the discover info given a user name. The discover info is returned if
@@ -605,14 +633,9 @@ public class EntityCapsManager
* @param connection the connection that we'd like this manager to register
* with.
*/
- public void addPacketListener(XMPPConnection connection)
+ public void addPacketListener(Connection connection)
{
- PacketFilter filter
- = new AndFilter(
- new PacketTypeFilter(Presence.class),
- new PacketExtensionFilter(
- CapsPacketExtension.ELEMENT_NAME,
- CapsPacketExtension.NAMESPACE));
+ PacketFilter filter = new PacketTypeFilter(Presence.class);
connection.addPacketListener(new CapsPacketListener(), filter);
}
@@ -913,48 +936,48 @@ public class EntityCapsManager
*/
public void processPacket(Packet packet)
{
+ // Check it the packet indicates that the user is online. We
+ // will use this information to decide if we're going to send
+ // the discover info request.
+ boolean online
+ = (packet instanceof Presence)
+ && ((Presence) packet).isAvailable();
+
CapsPacketExtension ext
= (CapsPacketExtension)
packet.getExtension(
CapsPacketExtension.ELEMENT_NAME,
CapsPacketExtension.NAMESPACE);
- /*
- * Before Version 1.4 of XEP-0115: Entity Capabilities, the 'ver'
- * attribute was generated differently and the 'hash' attribute was
- * absent. The 'ver' attribute in Version 1.3 represents the
- * specific version of the client and thus does not provide a way to
- * validate the DiscoverInfo sent by the client. If
- * EntityCapsManager receives no 'hash' attribute, it will assume
- * the legacy format and will not cache it because the DiscoverInfo
- * to be received from the client later on will not be trustworthy.
- */
- String hash = ext.getHash();
-
- /* Google Talk web does not set hash but we need it to be cached */
- if(hash == null)
- hash = "";
-
- if (hash != null)
+ if(ext != null && online)
{
- // Check it the packet indicates that the user is online. We
- // will use this information to decide if we're going to send
- // the discover info request.
- boolean online
- = (packet instanceof Presence)
- && ((Presence) packet).isAvailable();
-
- if(online)
- {
- addUserCapsNode(
- packet.getFrom(),
- ext.getNode(), hash, ext.getVersion(),
- ext.getExtensions(), online);
- }
- else
- {
- removeUserCapsNode(packet.getFrom());
- }
+ /*
+ * Before Version 1.4 of XEP-0115: Entity Capabilities,
+ * the 'ver' attribute was generated differently and the 'hash'
+ * attribute was absent. The 'ver' attribute in Version 1.3
+ * represents the specific version of the client and thus does
+ * not provide a way to validate the DiscoverInfo sent by
+ * the client. If EntityCapsManager receives no 'hash'
+ * attribute, it will assume the legacy format and will not
+ * cache it because the DiscoverInfo to be received from
+ * the client later on will not be trustworthy.
+ */
+ String hash = ext.getHash();
+
+ /* Google Talk web does not set hash, but we need it to
+ * be cached
+ */
+ if (hash == null)
+ hash = "";
+
+ addUserCapsNode(
+ packet.getFrom(),
+ ext.getNode(), hash, ext.getVersion(),
+ ext.getExtensions(), online);
+ }
+ else if (!online)
+ {
+ removeUserCapsNode(packet.getFrom());
}
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java
index 5ee38b0..eda921f 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/caps/UserCapsNodeListener.java
@@ -17,6 +17,8 @@
*/
package net.java.sip.communicator.impl.protocol.jabber.extensions.caps;
+import java.util.ArrayList;
+
/**
* Represents a listener of events notifying about changes in the list of user
* caps nodes of <tt>EntityCapsManager</tt>.
@@ -30,18 +32,22 @@ public interface UserCapsNodeListener
* record for a specific user about the caps node the user has.
*
* @param user the user (full JID)
+ * @param fullJids a list of all resources of the user (full JIDs)
* @param node the entity caps node#ver
* @param online indicates if the user for which we're notified is online
*/
- public void userCapsNodeAdded(String user, String node, boolean online);
+ public void userCapsNodeAdded(String user, ArrayList<String> fullJids,
+ String node, boolean online);
/**
* Notifies this listener that an <tt>EntityCapsManager</tt> has removed a
* record for a specific user about the caps node the user has.
*
* @param user the user (full JID)
+ * @param fullJids a list of all resources of the user (full JIDs)
* @param node the entity caps node#ver
* @param online indicates if the user for which we're notified is online
*/
- public void userCapsNodeRemoved(String user, String node, boolean online);
+ public void userCapsNodeRemoved(String user, ArrayList<String> fullJids,
+ String node, boolean online);
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java
index 547ebe8..48207c2 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriBuilder.java
@@ -37,9 +37,9 @@ import java.util.*;
* Add one or multiple requests of the same type by calling
* {@link #addAllocateChannelsReq(boolean, String, boolean, java.util.List)}}
* or {@link #addExpireChannelsReq(ColibriConferenceIQ)}
- * or {@link #addTransportUpdateReq(boolean, java.util.Map, ColibriConferenceIQ)}
- * or {@link #addBundleTransportUpdateReq(
- * boolean, IceUdpTransportPacketExtension, ColibriConferenceIQ)}.
+ * or {@link #addRtpDescription(Map, ColibriConferenceIQ)}
+ * and {@link #addSSSRCGroupsInfo(Map, ColibriConferenceIQ)}
+ * and {@link #addSSSRCInfo(Map, ColibriConferenceIQ)}.
* </li>
* <li>
* Compile the request by calling {@link #getRequest(String)}. Then send it to
@@ -119,6 +119,20 @@ public class ColibriBuilder
private SimulcastMode simulcastMode;
/**
+ * Specifies the audio packet delay that will be set on all created audio
+ * channels. When set to <tt>null</tt> the builder will clear the attribute
+ * which stands for 'undefined'.
+ **/
+ private Integer audioPacketDelay;
+
+ /**
+ * Channel 'rtp-level-relay-type' option that will be used with all created
+ * audio channel. Possible values: mixer or translator (default).
+ *
+ */
+ private RTPLevelRelayType rtpLevelRelayType;
+
+ /**
* Creates new instance of {@link ColibriBuilder} for given
* <tt>conferenceState</tt>.
*
@@ -161,18 +175,26 @@ public class ColibriBuilder
* @param contents the list of {@link ContentPacketExtension} describing
* channels media.
*
- * @return this instance fo calls chaining purpose.
+ * @return <tt>true</tt> if the request yields any changes in Colibri
+ * channels state on the bridge or <tt>false</tt> otherwise.
+ * In general when <tt>false</tt> is returned for all
+ * combined requests it makes no sense to send it.
*/
- public ColibriBuilder addAllocateChannelsReq(
- boolean useBundle,
- String endpointName,
- boolean peerIsInitiator,
+ public boolean addAllocateChannelsReq(
+ boolean useBundle,
+ String endpointName,
+ boolean peerIsInitiator,
List<ContentPacketExtension> contents)
{
+ Objects.requireNonNull(endpointName, "endpointName");
+ Objects.requireNonNull(contents, "contents");
+
assertRequestType(RequestType.ALLOCATE_CHANNELS);
request.setType(IQ.Type.GET);
+ boolean hasAnyChanges = false;
+
for (ContentPacketExtension cpe : contents)
{
MediaType mediaType = JingleUtils.getMediaType(cpe);
@@ -193,7 +215,7 @@ public class ColibriBuilder
remoteChannelRequest.setChannelBundleId(endpointName);
}
- if (mediaType != MediaType.DATA)
+ if (remoteChannelRequest instanceof ColibriConferenceIQ.Channel)
{
RtpDescriptionPacketExtension rdpe
= cpe.getFirstChildOfType(
@@ -213,6 +235,13 @@ public class ColibriBuilder
remoteRtpChannelRequest.setAdaptiveLastN(adaptiveLastN);
remoteRtpChannelRequest.setAdaptiveSimulcast(adaptiveSimulcast);
remoteRtpChannelRequest.setSimulcastMode(simulcastMode);
+ if (MediaType.AUDIO.equals(mediaType))
+ {
+ // When audioPacketDelay is null it will clear the attribute
+ remoteRtpChannelRequest.setPacketDelay(audioPacketDelay);
+ // Set rtp packet relay type for this channel
+ remoteRtpChannelRequest.setRTPLevelRelayType(rtpLevelRelayType);
+ }
}
// Copy transport
@@ -221,13 +250,17 @@ public class ColibriBuilder
copyTransportOnChannel(cpe, remoteChannelRequest);
}
- if (mediaType != MediaType.DATA)
+ if (remoteChannelRequest instanceof ColibriConferenceIQ.Channel)
{
+ hasAnyChanges = true;
+
contentRequest.addChannel(
(ColibriConferenceIQ.Channel) remoteChannelRequest);
}
else
{
+ hasAnyChanges = true;
+
contentRequest.addSctpConnection(
(ColibriConferenceIQ.SctpConnection) remoteChannelRequest);
}
@@ -246,6 +279,8 @@ public class ColibriBuilder
IceUdpTransportPacketExtension.class);
if (transport != null)
{
+ hasAnyChanges = true;
+
bundle.setTransport(
IceUdpTransportPacketExtension
.cloneTransportAndCandidates(transport, true));
@@ -254,106 +289,38 @@ public class ColibriBuilder
request.addChannelBundle(bundle);
}
- return this;
- }
-
- /**
- * Adds next ICE transport update request to
- * {@link RequestType#TRANSPORT_UPDATE} query currently being built.
- *
- * @param initiator the value that will be set in 'initiator'
- * attribute({@link ColibriConferenceIQ.Channel#initiator}).
- * @param map the map of content name to transport extensions. Maps
- * transport to media types.
- * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about
- * Colibri channels to be updated.
- *
- * @return this instance fo calls chaining purpose.
- */
- public ColibriBuilder addTransportUpdateReq(
- boolean initiator,
- Map<String, IceUdpTransportPacketExtension> map,
- ColibriConferenceIQ localChannelsInfo)
- {
- if (conferenceState == null
- || StringUtils.isNullOrEmpty(conferenceState.getID()))
- {
- // We are not initialized yet
- return null;
- }
-
- assertRequestType(RequestType.TRANSPORT_UPDATE);
-
- request.setType(IQ.Type.SET);
-
- for (Map.Entry<String,IceUdpTransportPacketExtension> e
- : map.entrySet())
- {
- String contentName = e.getKey();
- ColibriConferenceIQ.ChannelCommon channel
- = getColibriChannel(localChannelsInfo, contentName);
-
- if (channel != null)
- {
- IceUdpTransportPacketExtension transport
- = IceUdpTransportPacketExtension
- .cloneTransportAndCandidates(e.getValue(), true);
-
- ColibriConferenceIQ.ChannelCommon channelRequest
- = channel instanceof ColibriConferenceIQ.Channel
- ? new ColibriConferenceIQ.Channel()
- : new ColibriConferenceIQ.SctpConnection();
-
- channelRequest.setID(channel.getID());
- channelRequest.setEndpoint(channel.getEndpoint());
- channelRequest.setInitiator(initiator);
- channelRequest.setTransport(transport);
-
- if (channelRequest instanceof ColibriConferenceIQ.Channel)
- {
- request.getOrCreateContent(contentName)
- .addChannel(
- (ColibriConferenceIQ.Channel) channelRequest);
- }
- else
- {
- request.getOrCreateContent(contentName)
- .addSctpConnection(
- (ColibriConferenceIQ.SctpConnection) channelRequest);
- }
- }
- }
- return this;
+ return hasAnyChanges;
}
/**
- * Adds next request to {@link RequestType#BUNDLE_TRANSPORT_UPDATE} query.
- * @param initiator the value that will be set in 'initiator'
- * attribute({@link ColibriConferenceIQ.Channel#initiator}).
+ * Adds next request to {@link RequestType#CHANNEL_INFO_UPDATE} query.
* @param localChannelsInfo the {@link ColibriConferenceIQ} instance that
* describes the channel for which bundle transport will be updated.
* It should contain the description of only one "channel bundle".
* If it contains more than one then the first one will be used.
- * @return this instance for calls chaining purpose.
+ * @return <tt>true</tt> if the request yields any changes in Colibri
+ * channels state on the bridge or <tt>false</tt> otherwise.
+ * In general when <tt>false</tt> is returned for all
+ * combined requests it makes no sense to send it.
* @throws IllegalArgumentException if <tt>localChannelsInfo</tt> does not
* describe any channel bundles.
*/
- public ColibriBuilder addBundleTransportUpdateReq(
- boolean initiator,
- IceUdpTransportPacketExtension transport,
- ColibriConferenceIQ localChannelsInfo)
+ public boolean addBundleTransportUpdateReq(
+ IceUdpTransportPacketExtension transport,
+ ColibriConferenceIQ localChannelsInfo)
throws IllegalArgumentException
{
- // FIXME:'initiator' not used on bundle transport update ?
+ Objects.requireNonNull(transport, "transport");
+ Objects.requireNonNull(localChannelsInfo, "localChannelsInfo");
if (conferenceState == null
|| StringUtils.isNullOrEmpty(conferenceState.getID()))
{
// We are not initialized yet
- return null;
+ return false;
}
- assertRequestType(RequestType.BUNDLE_TRANSPORT_UPDATE);
+ assertRequestType(RequestType.CHANNEL_INFO_UPDATE);
request.setType(IQ.Type.SET);
@@ -371,7 +338,7 @@ public class ColibriBuilder
else
{
throw new IllegalArgumentException(
- "Expected ChannelBundle as not found");
+ "Expected ChannelBundle as not found");
}
ColibriConferenceIQ.ChannelBundle bundleUpdate
@@ -387,7 +354,7 @@ public class ColibriBuilder
request.addChannelBundle(bundleUpdate);
- return this;
+ return true;
}
/**
@@ -395,15 +362,20 @@ public class ColibriBuilder
* {@link RequestType#EXPIRE_CHANNELS} query currently being built.
* @param channelInfo the {@link ColibriConferenceIQ} instance that contains
* info about the channels to be expired.
- * @return this instance for the purpose of calls chaining.
+ * @return <tt>true</tt> if the request yields any changes in Colibri
+ * channels state on the bridge or <tt>false</tt> otherwise.
+ * In general when <tt>false</tt> is returned for all
+ * combined requests it makes no sense to send it.
*/
- public ColibriBuilder addExpireChannelsReq(ColibriConferenceIQ channelInfo)
+ public boolean addExpireChannelsReq(ColibriConferenceIQ channelInfo)
{
+ Objects.requireNonNull(channelInfo, "channelInfo");
+
// Formulate the ColibriConferenceIQ request which is to be sent.
if (conferenceState == null
|| StringUtils.isNullOrEmpty(conferenceState.getID()))
{
- return null;
+ return false;
}
assertRequestType(RequestType.EXPIRE_CHANNELS);
@@ -411,7 +383,7 @@ public class ColibriBuilder
request.setType(IQ.Type.SET);
for (ColibriConferenceIQ.Content expiredContent
- : channelInfo.getContents())
+ : channelInfo.getContents())
{
ColibriConferenceIQ.Content stateContent
= conferenceState.getContent(expiredContent.getName());
@@ -420,7 +392,7 @@ public class ColibriBuilder
{
ColibriConferenceIQ.Content requestContent
= request.getOrCreateContent(
- stateContent.getName());
+ stateContent.getName());
for (ColibriConferenceIQ.Channel expiredChannel
: expiredContent.getChannels())
@@ -490,7 +462,7 @@ public class ColibriBuilder
*/
/*if (stateContent.getChannelCount() == 1)
{
- stateChannel = stateContent.getChannel(0);
+ stateChannel = stateContent.getRtpChannel(0);
ColibriConferenceIQ.Channel channelRequest
= new ColibriConferenceIQ.Channel();
@@ -537,7 +509,294 @@ public class ColibriBuilder
}
}
- return this;
+ return hasAnyChannelsToExpire;
+ }
+
+ /**
+ * Adds next payload type information update request to
+ * {@link RequestType#CHANNEL_INFO_UPDATE} query currently being built.
+ *
+ * @param map the map of content name to RTP description packet extension.
+ * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about
+ * Colibri channels to be updated.
+ *
+ * @return <tt>true</tt> if the request yields any changes in Colibri
+ * channels state on the bridge or <tt>false</tt> otherwise.
+ * In general when <tt>false</tt> is returned for all
+ * combined requests it makes no sense to send it.
+ */
+ public boolean addRtpDescription(
+ Map<String, RtpDescriptionPacketExtension> map,
+ ColibriConferenceIQ localChannelsInfo)
+ {
+ Objects.requireNonNull(map, "map");
+ Objects.requireNonNull(localChannelsInfo, "localChannelsInfo");
+
+ if (conferenceState == null
+ || StringUtils.isNullOrEmpty(conferenceState.getID()))
+ {
+ // We are not initialized yet
+ return false;
+ }
+
+ assertRequestType(RequestType.CHANNEL_INFO_UPDATE);
+
+ request.setType(IQ.Type.SET);
+
+ boolean anyUpdates = false;
+
+ for (Map.Entry<String, RtpDescriptionPacketExtension> e
+ : map.entrySet())
+ {
+ String contentName = e.getKey();
+ ColibriConferenceIQ.ChannelCommon channel
+ = getColibriChannel(localChannelsInfo, contentName);
+
+ if (channel != null
+ && channel instanceof ColibriConferenceIQ.Channel)
+ {
+ RtpDescriptionPacketExtension rtpPE = e.getValue();
+ if (rtpPE == null)
+ {
+ continue;
+ }
+
+ List<PayloadTypePacketExtension> pts = rtpPE.getPayloadTypes();
+ if (pts == null || pts.isEmpty())
+ {
+ continue;
+ }
+
+ anyUpdates = true;
+
+ ColibriConferenceIQ.Channel channelRequest
+ = (ColibriConferenceIQ.Channel) getRequestChannel(
+ request.getOrCreateContent(contentName),
+ channel);
+ if (channelRequest == null)
+ {
+ channelRequest = new ColibriConferenceIQ.Channel();
+ channelRequest.setID(channel.getID());
+ }
+
+ for (PayloadTypePacketExtension ptPE : pts)
+ {
+ channelRequest.addPayloadType(ptPE);
+ }
+ }
+ }
+
+ return anyUpdates;
+ }
+
+ /**
+ * Adds next SSRC information update request to
+ * {@link RequestType#CHANNEL_INFO_UPDATE} query currently being built.
+ *
+ * @param ssrcMap the map of content name to the list of
+ * <tt>SourcePacketExtension</tt>.
+ * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about
+ * Colibri channels to be updated.
+ *
+ * @return <tt>true</tt> if the request yields any changes in Colibri
+ * channels state on the bridge or <tt>false</tt> otherwise.
+ * In general when <tt>false</tt> is returned for all
+ * combined requests it makes no sense to send it.
+ */
+ public boolean addSSSRCInfo(
+ Map<String, List<SourcePacketExtension>> ssrcMap,
+ ColibriConferenceIQ localChannelsInfo)
+ {
+ Objects.requireNonNull(ssrcMap, "ssrcMap");
+ Objects.requireNonNull(localChannelsInfo, "localChannelsInfo");
+
+ if (conferenceState == null
+ || StringUtils.isNullOrEmpty(conferenceState.getID()))
+ {
+ // We are not initialized yet
+ return false;
+ }
+
+ assertRequestType(RequestType.CHANNEL_INFO_UPDATE);
+
+ request.setType(IQ.Type.SET);
+
+ boolean anyUpdates = false;
+
+ // Go over SSRCs
+ for (String contentName : ssrcMap.keySet())
+ {
+ // Get channel from local channel info
+ ColibriConferenceIQ.ChannelCommon rtpChanel
+ = getRtpChannel(localChannelsInfo, contentName);
+ if (rtpChanel == null)
+ {
+ // There's no channel for this content name in localChannelsInfo
+ continue;
+ }
+
+ anyUpdates = true;
+
+ // Ok we have channel for this content, let's add SSRCs
+ ColibriConferenceIQ.Channel reqChannel
+ = (ColibriConferenceIQ.Channel) getRequestChannel(
+ request.getOrCreateContent(contentName), rtpChanel);
+
+ for (SourcePacketExtension ssrc : ssrcMap.get(contentName))
+ {
+ reqChannel.addSource(ssrc.copy());
+ }
+
+ if (reqChannel.getSources() == null
+ || reqChannel.getSources().isEmpty())
+ {
+ // Put an empty source to remove all sources
+ SourcePacketExtension emptySource = new SourcePacketExtension();
+ emptySource.setSSRC(-1L);
+ reqChannel.addSource(emptySource);
+ }
+ }
+
+ return anyUpdates;
+ }
+
+ /**
+ * Adds next SSRC group information update request to
+ * {@link RequestType#CHANNEL_INFO_UPDATE} query currently being built.
+ *
+ * @param ssrcGroupMap the map of content name to the list of
+ * <tt>SourceGroupPacketExtension</tt>.
+ * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about
+ * Colibri channels to be updated.
+ *
+ * @return <tt>true</tt> if the request yields any changes in Colibri
+ * channels state on the bridge or <tt>false</tt> otherwise.
+ * In general when <tt>false</tt> is returned for all
+ * combined requests it makes no sense to send it.
+ */
+ public boolean addSSSRCGroupsInfo(
+ Map<String, List<SourceGroupPacketExtension>> ssrcGroupMap,
+ ColibriConferenceIQ localChannelsInfo)
+ {
+ Objects.requireNonNull(ssrcGroupMap, "ssrcGroupMap");
+ Objects.requireNonNull(localChannelsInfo, "localChannelsInfo");
+
+ if (conferenceState == null
+ || StringUtils.isNullOrEmpty(conferenceState.getID()))
+ {
+ // We are not initialized yet
+ return false;
+ }
+
+ assertRequestType(RequestType.CHANNEL_INFO_UPDATE);
+
+ request.setType(IQ.Type.SET);
+
+ boolean anyUpdates = false;
+
+ // Go over SSRC groups
+ for (String contentName : ssrcGroupMap.keySet())
+ {
+ // Get channel from local channel info
+ ColibriConferenceIQ.Channel rtpChannel
+ = getRtpChannel(localChannelsInfo, contentName);
+ if (rtpChannel == null)
+ {
+ // There's no channel for this content name in localChannelsInfo
+ continue;
+ }
+
+ List<SourceGroupPacketExtension> groups
+ = ssrcGroupMap.get(contentName);
+
+ // Ok we have channel for this content, let's add SSRCs
+ ColibriConferenceIQ.Channel reqChannel
+ = (ColibriConferenceIQ.Channel) getRequestChannel(
+ request.getOrCreateContent(contentName), rtpChannel);
+
+ if (groups.isEmpty() && "video".equalsIgnoreCase(contentName))
+ {
+ anyUpdates = true;
+
+ // Put empty source group to turn off simulcast layers
+ reqChannel.addSourceGroup(
+ SourceGroupPacketExtension.createSimulcastGroup());
+ }
+
+ for (SourceGroupPacketExtension group : groups)
+ {
+ anyUpdates = true;
+
+ reqChannel.addSourceGroup(group);
+ }
+ }
+
+ return anyUpdates;
+ }
+
+ /**
+ * Adds next ICE transport update request to
+ * {@link RequestType#CHANNEL_INFO_UPDATE} query currently being built.
+ *
+ * @param map the map of content name to transport extensions. Maps
+ * transport to media types.
+ * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about
+ * Colibri channels to be updated.
+ *
+ * @return <tt>true</tt> if the request yields any changes in Colibri
+ * channels state on the bridge or <tt>false</tt> otherwise.
+ * In general when <tt>false</tt> is returned for all
+ * combined requests it makes no sense to send it.
+ */
+ public boolean addTransportUpdateReq(
+ Map<String, IceUdpTransportPacketExtension> map,
+ ColibriConferenceIQ localChannelsInfo)
+ {
+ Objects.requireNonNull(map, "map");
+ Objects.requireNonNull(localChannelsInfo, "localChannelsInfo");
+
+ if (conferenceState == null
+ || StringUtils.isNullOrEmpty(conferenceState.getID()))
+ {
+ // We are not initialized yet
+ return false;
+ }
+
+ boolean hasAnyChanges = false;
+
+ assertRequestType(RequestType.CHANNEL_INFO_UPDATE);
+
+ request.setType(IQ.Type.SET);
+
+ for (Map.Entry<String,IceUdpTransportPacketExtension> e
+ : map.entrySet())
+ {
+ String contentName = e.getKey();
+ ColibriConferenceIQ.ChannelCommon channel
+ = getColibriChannel(localChannelsInfo, contentName);
+
+ if (channel != null)
+ {
+ IceUdpTransportPacketExtension transport
+ = IceUdpTransportPacketExtension
+ .cloneTransportAndCandidates(e.getValue(), true);
+
+ ColibriConferenceIQ.ChannelCommon channelRequest
+ = channel instanceof ColibriConferenceIQ.Channel
+ ? new ColibriConferenceIQ.Channel()
+ : new ColibriConferenceIQ.SctpConnection();
+
+ channelRequest.setID(channel.getID());
+ channelRequest.setEndpoint(channel.getEndpoint());
+ channelRequest.setTransport(transport);
+
+ request.getOrCreateContent(contentName)
+ .addChannelCommon(channelRequest);
+
+ hasAnyChanges = true;
+ }
+ }
+ return hasAnyChanges;
}
/**
@@ -583,10 +842,12 @@ public class ColibriBuilder
request.setTo(videobridge);
- if (requestType == RequestType.EXPIRE_CHANNELS
- && !hasAnyChannelsToExpire)
+ if (requestType == RequestType.EXPIRE_CHANNELS)
{
- return null;
+ if (!hasAnyChannelsToExpire)
+ return null;
+
+ hasAnyChannelsToExpire = false;
}
return request;
@@ -720,6 +981,28 @@ public class ColibriBuilder
}
/**
+ * Returns an <tt>Integer</tt> which stands for the audio packet delay
+ * that will be set on all created audio channels or <tt>null</tt> if
+ * the builder should leave not include the XML attribute at all.
+ */
+ public Integer getAudioPacketDelay()
+ {
+ return audioPacketDelay;
+ }
+
+ /**
+ * Configures audio channels packet delay.
+ * @param audioPacketDelay an <tt>Integer</tt> value which stands for
+ * the audio packet delay that will be set on all created audio channels or
+ * <tt>null</tt> if the builder should not set that channel property to any
+ * value.
+ */
+ public void setAudioPacketDelay(Integer audioPacketDelay)
+ {
+ this.audioPacketDelay = audioPacketDelay;
+ }
+
+ /**
* Sets channel 'simulcast-mode' option that will be added to the
* request when channels are created.
* @param simulcastMode a <tt>SimulcastMode</tt> value to specify
@@ -732,69 +1015,64 @@ public class ColibriBuilder
}
/**
- * Adds next payload type information update request to
- * {@link RequestType#RTP_DESCRIPTION_UPDATE} query currently being built.
+ * Creates a new instance of <tt>localChannelInfo</tt> and initializes only
+ * the fields required to identify particular Colibri channel on the bridge.
+ * This instance is meant to be used in Colibri
+ * {@link RequestType#CHANNEL_INFO_UPDATE} requests. This instance is also
+ * added to given <tt>requestContent</tt> which used to construct current
+ * request.
*
- * @param map the map of content name to RTP description packet extension.
- * @param localChannelsInfo {@link ColibriConferenceIQ} holding info about
- * Colibri channels to be updated.
+ * @param requestContent <tt>Content</tt> of Colibri update request to which
+ * new instance wil be automatically added after has been created.
+ * @param localChannelInfo the original channel for which "update request"
+ * equivalent is to be created with this call.
*
- * @return this instance for calls chaining purpose.
+ * @return new instance of <tt>localChannelInfo</tt> and initialized with
+ * only those fields required to identify particular Colibri channel on
+ * the bridge.
*/
- public ColibriBuilder addRtpDescription(
- Map<String, RtpDescriptionPacketExtension> map,
- ColibriConferenceIQ localChannelsInfo) {
-
- if (conferenceState == null
- || StringUtils.isNullOrEmpty(conferenceState.getID()))
- {
- // We are not initialized yet
- return null;
- }
-
- assertRequestType(RequestType.RTP_DESCRIPTION_UPDATE);
-
- request.setType(IQ.Type.SET);
-
- for (Map.Entry<String, RtpDescriptionPacketExtension> e
- : map.entrySet())
+ private ColibriConferenceIQ.ChannelCommon getRequestChannel(
+ ColibriConferenceIQ.Content requestContent,
+ ColibriConferenceIQ.ChannelCommon localChannelInfo)
+ {
+ ColibriConferenceIQ.ChannelCommon reqChannel
+ = requestContent.getChannel(localChannelInfo.getID());
+ if (reqChannel == null)
{
- String contentName = e.getKey();
- ColibriConferenceIQ.ChannelCommon channel
- = getColibriChannel(localChannelsInfo, contentName);
-
- if (channel != null
- && channel instanceof ColibriConferenceIQ.Channel)
+ if (localChannelInfo instanceof ColibriConferenceIQ.Channel)
{
- RtpDescriptionPacketExtension rtpPE = e.getValue();
- if (rtpPE == null)
- {
- continue;
- }
-
- List<PayloadTypePacketExtension> pts = rtpPE.getPayloadTypes();
- if (pts == null || pts.isEmpty())
- {
- continue;
- }
-
- ColibriConferenceIQ.Channel channelRequest
- = new ColibriConferenceIQ.Channel();
+ reqChannel = new ColibriConferenceIQ.Channel();
+ }
+ else if (
+ localChannelInfo instanceof ColibriConferenceIQ.SctpConnection)
+ {
+ reqChannel = new ColibriConferenceIQ.SctpConnection();
+ }
+ else
+ {
+ throw new RuntimeException(
+ "Unsupported ChannelCommon class: "
+ + localChannelInfo.getClass());
+ }
- channelRequest.setID(channel.getID());
+ reqChannel.setID(localChannelInfo.getID());
- for (PayloadTypePacketExtension ptPE : rtpPE.getPayloadTypes())
- {
- channelRequest.addPayloadType(ptPE);
- }
+ requestContent.addChannelCommon(reqChannel);
+ }
+ return reqChannel;
+ }
- request.getOrCreateContent(contentName)
- .addChannel(channelRequest);
- }
+ private ColibriConferenceIQ.Channel getRtpChannel(
+ ColibriConferenceIQ localChannelsInfo,
+ String contentName)
+ {
+ ColibriConferenceIQ.Content content
+ = localChannelsInfo.getContent(contentName);
- }
+ if (content == null)
+ return null;
- return this;
+ return content.getChannelCount() > 0 ? content.getChannel(0) : null;
}
/**
@@ -808,19 +1086,10 @@ public class ColibriBuilder
ALLOCATE_CHANNELS,
/**
- * Updates transport information for channels that use RTP bundle.
- */
- BUNDLE_TRANSPORT_UPDATE,
-
- /**
- * Updates channel transport information(ICE transport candidates).
+ * An update request which is meant to modify some values of existing
+ * Colibri channels on the bridge.
*/
- TRANSPORT_UPDATE,
-
- /**
- * Updates the RTP description of a channel (payload types).
- */
- RTP_DESCRIPTION_UPDATE,
+ CHANNEL_INFO_UPDATE,
/**
* Expires specified Colibri channels.
@@ -833,4 +1102,28 @@ public class ColibriBuilder
*/
UNDEFINED;
}
+
+ /**
+ * Configures RTP-level relay (RFC 3550, section 2.3).
+ * @param rtpLevelRelayType an <tt>RTPLevelRelayType</tt> value which
+ * stands for the rtp level relay type that will be set on all created
+ * audio channels.
+ */
+ public void setRTPLevelRelayType(RTPLevelRelayType rtpLevelRelayType)
+ {
+ this.rtpLevelRelayType = rtpLevelRelayType;
+ }
+
+ /**
+ * Configures RTP-level relay (RFC 3550, section 2.3).
+ * @param rtpLevelRelayType a <tt>String</tt> value which
+ * stands for the rtp level relay type that will be set on all created
+ * audio channels.
+ */
+ public void setRTPLevelRelayType(String rtpLevelRelayType)
+ {
+ setRTPLevelRelayType
+ (RTPLevelRelayType.parseRTPLevelRelayType(rtpLevelRelayType));
+ }
+
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java
index 52368bf..d5cf175 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriConferenceIQ.java
@@ -161,7 +161,7 @@ public class ColibriConferenceIQ
throw new NullPointerException("channelBundle");
return
- channelBundles.contains(channelBundles)
+ channelBundles.contains(channelBundle)
? false
: channelBundles.add(channelBundle);
}
@@ -543,6 +543,14 @@ public class ColibriConferenceIQ
= "receive-simulcast-layer";
/**
+ * The XML name of the <tt>packet-delay</tt> attribute of
+ * a <tt>channel</tt> of a <tt>content</tt> of a <tt>conference</tt> IQ
+ * which represents the value of the {@link #packetDelay} property of
+ * <tt>ColibriConferenceIQ.Channel</tt>.
+ */
+ public static final String PACKET_DELAY_ATTR_NAME = "packet-delay";
+
+ /**
* The XML name of the <tt>rtcpport</tt> attribute of a <tt>channel</tt>
* of a <tt>content</tt> of a <tt>conference</tt> IQ which represents
* the value of the <tt>rtcpPort</tt> property of
@@ -613,6 +621,11 @@ public class ColibriConferenceIQ
private SimulcastMode simulcastMode;
/**
+ * The amount of delay added to the RTP stream in a number of packets.
+ */
+ private Integer packetDelay;
+
+ /**
* The <tt>payload-type</tt> elements defined by XEP-0167: Jingle RTP
* Sessions associated with this <tt>channel</tt>.
*/
@@ -893,6 +906,18 @@ public class ColibriConferenceIQ
}
/**
+ * Returns an <tt>Integer</tt> which stands for the amount of delay
+ * added to the RTP stream in a number of packets.
+ *
+ * @return <tt>Integer</tt> with the value or <tt>null</tt> if
+ * unspecified.
+ */
+ public Integer getPacketDelay()
+ {
+ return packetDelay;
+ }
+
+ /**
* Gets a list of <tt>payload-type</tt> elements defined by XEP-0167:
* Jingle RTP Sessions added to this <tt>channel</tt>.
*
@@ -1077,8 +1102,16 @@ public class ColibriConferenceIQ
if (adaptiveSimulcast != null)
{
- xml.append(' ').append(adaptiveSimulcast).append("='")
- .append(adaptiveSimulcast).append('\'');
+ xml.append(' ').append(ADAPTIVE_SIMULCAST_ATTR_NAME)
+ .append("='").append(adaptiveSimulcast).append('\'');
+ }
+
+ // packet-delay
+ Integer packetDelay = getPacketDelay();
+ if (packetDelay != null)
+ {
+ xml.append(' ').append(PACKET_DELAY_ATTR_NAME).append("='")
+ .append(packetDelay).append('\'');
}
// simulcastMode
@@ -1314,6 +1347,17 @@ public class ColibriConferenceIQ
}
/**
+ * Configures channel's packet delay which tells by how many packets
+ * the RTP streams will be delayed.
+ * @param packetDelay an <tt>Integer</tt> value which stands for
+ * the packet delay that will be set or <tt>null</tt> to leave undefined
+ */
+ public void setPacketDelay(Integer packetDelay)
+ {
+ this.packetDelay = packetDelay;
+ }
+
+ /**
* Sets the value of the 'simulcast-mode' flag.
* @param simulcastMode the value to set.
*/
@@ -1925,6 +1969,25 @@ public class ColibriConferenceIQ
}
/**
+ * Adds <tt>ChannelCommon</tt> to this <tt>Content</tt>.
+ * @param channelCommon {@link ChannelCommon} instance to be added to
+ * this content.
+ * @return <tt>true</tt> if given <tt>channelCommon</tt> has been
+ * actually added to this <tt>Content</tt> instance.
+ */
+ public boolean addChannelCommon(ChannelCommon channelCommon)
+ {
+ if (channelCommon instanceof Channel)
+ {
+ return addChannel((Channel) channelCommon);
+ }
+ else
+ {
+ return addSctpConnection((SctpConnection) channelCommon);
+ }
+ }
+
+ /**
* Adds a specific <tt>SctpConnection</tt> to the list of
* <tt>SctpConnection</tt>s included into this <tt>Content</tt>.
*
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java
index 39e299f..ce676ef 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriIQProvider.java
@@ -20,6 +20,7 @@ package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
+import net.java.sip.communicator.service.protocol.jabber.*;
import org.jitsi.service.neomedia.*;
import org.jitsi.util.*;
import org.jivesoftware.smack.packet.*;
@@ -36,89 +37,101 @@ import org.xmlpull.v1.*;
public class ColibriIQProvider
implements IQProvider
{
+
+ /**
+ * Smack interoperation layer
+ */
+ private AbstractSmackInteroperabilityLayer smackInteroperabilityLayer =
+ AbstractSmackInteroperabilityLayer.getInstance();
+
/** Initializes a new <tt>ColibriIQProvider</tt> instance. */
public ColibriIQProvider()
{
- ProviderManager providerManager = ProviderManager.getInstance();
-
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
PayloadTypePacketExtension.ELEMENT_NAME,
ColibriConferenceIQ.NAMESPACE,
new DefaultPacketExtensionProvider<PayloadTypePacketExtension>(
PayloadTypePacketExtension.class));
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
RtcpFbPacketExtension.ELEMENT_NAME,
RtcpFbPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<RtcpFbPacketExtension>(
RtcpFbPacketExtension.class));
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
RTPHdrExtPacketExtension.ELEMENT_NAME,
ColibriConferenceIQ.NAMESPACE,
new DefaultPacketExtensionProvider<RTPHdrExtPacketExtension>(
RTPHdrExtPacketExtension.class));
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
SourcePacketExtension.ELEMENT_NAME,
SourcePacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<SourcePacketExtension>(
SourcePacketExtension.class));
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
SourceGroupPacketExtension.ELEMENT_NAME,
SourceGroupPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<SourceGroupPacketExtension>(
SourceGroupPacketExtension.class));
PacketExtensionProvider parameterProvider
- = new DefaultPacketExtensionProvider<ParameterPacketExtension>(
- ParameterPacketExtension.class);
+ = new DefaultPacketExtensionProvider<ParameterPacketExtension>(
+ ParameterPacketExtension.class);
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
ParameterPacketExtension.ELEMENT_NAME,
ColibriConferenceIQ.NAMESPACE,
parameterProvider);
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
ParameterPacketExtension.ELEMENT_NAME,
SourcePacketExtension.NAMESPACE,
parameterProvider);
// Shutdown IQ
- providerManager.addIQProvider(
- GracefulShutdownIQ.ELEMENT_NAME,
- GracefulShutdownIQ.NAMESPACE,
+ smackInteroperabilityLayer.addIQProvider(
+ ShutdownIQ.GRACEFUL_ELEMENT_NAME,
+ ShutdownIQ.NAMESPACE,
+ this);
+ smackInteroperabilityLayer.addIQProvider(
+ ShutdownIQ.FORCE_ELEMENT_NAME,
+ ShutdownIQ.NAMESPACE,
this);
// Shutdown extension
PacketExtensionProvider shutdownProvider
- = new DefaultPacketExtensionProvider
- <ColibriConferenceIQ.GracefulShutdown>(
- ColibriConferenceIQ.GracefulShutdown.class);
+ = new DefaultPacketExtensionProvider
+ <ColibriConferenceIQ.GracefulShutdown>(
+ ColibriConferenceIQ.GracefulShutdown.class);
- providerManager.addExtensionProvider(
- ColibriConferenceIQ.GracefulShutdown.ELEMENT_NAME,
- ColibriConferenceIQ.GracefulShutdown.NAMESPACE,
- shutdownProvider);
+ smackInteroperabilityLayer.addExtensionProvider(
+ ColibriConferenceIQ.GracefulShutdown.ELEMENT_NAME,
+ ColibriConferenceIQ.GracefulShutdown.NAMESPACE,
+ shutdownProvider);
// ColibriStatsIQ
- providerManager.addIQProvider(
- ColibriStatsIQ.ELEMENT_NAME,
- ColibriStatsIQ.NAMESPACE,
- this);
+ smackInteroperabilityLayer.addIQProvider(
+ ColibriStatsIQ.ELEMENT_NAME,
+ ColibriStatsIQ.NAMESPACE,
+ this);
// ColibriStatsExtension
PacketExtensionProvider statsProvider
- = new DefaultPacketExtensionProvider<ColibriStatsExtension>(
- ColibriStatsExtension.class);
+ = new DefaultPacketExtensionProvider<ColibriStatsExtension>(
+ ColibriStatsExtension.class);
- providerManager.addExtensionProvider(
- ColibriStatsExtension.ELEMENT_NAME,
- ColibriStatsExtension.NAMESPACE,
- statsProvider);
+ smackInteroperabilityLayer.addExtensionProvider(
+ ColibriStatsExtension.ELEMENT_NAME,
+ ColibriStatsExtension.NAMESPACE,
+ statsProvider);
// ColibriStatsExtension.Stat
PacketExtensionProvider statProvider
- = new DefaultPacketExtensionProvider<ColibriStatsExtension.Stat>(
- ColibriStatsExtension.Stat.class);
-
- providerManager.addExtensionProvider(
- ColibriStatsExtension.Stat.ELEMENT_NAME,
- ColibriStatsExtension.NAMESPACE,
- statProvider);
+ = new DefaultPacketExtensionProvider
+ <ColibriStatsExtension.Stat>(
+ ColibriStatsExtension.Stat.class);
+
+ smackInteroperabilityLayer.addExtensionProvider(
+ ColibriStatsExtension.Stat.ELEMENT_NAME,
+ ColibriStatsExtension.NAMESPACE,
+ statProvider);
+
+
}
private void addChildExtension(
@@ -199,8 +212,7 @@ public class ColibriIQProvider
throws Exception
{
PacketExtensionProvider extensionProvider
- = (PacketExtensionProvider)
- ProviderManager.getInstance().getExtensionProvider(
+ = smackInteroperabilityLayer.getExtensionProvider(
name,
namespace);
PacketExtension extension;
@@ -416,6 +428,15 @@ public class ColibriIQProvider
if ((expire != null) && (expire.length() != 0))
channel.setExpire(Integer.parseInt(expire));
+ String packetDelay
+ = parser.getAttributeValue(
+ "",
+ ColibriConferenceIQ.Channel
+ .PACKET_DELAY_ATTR_NAME);
+ if (!StringUtils.isNullOrEmpty(packetDelay))
+ channel.setPacketDelay(
+ Integer.parseInt(packetDelay));
+
// host
String host
= parser.getAttributeValue(
@@ -464,6 +485,18 @@ public class ColibriIQProvider
channel.setAdaptiveLastN(
Boolean.parseBoolean(adaptiveLastN));
+ String adaptiveSimulcast
+ = parser.getAttributeValue(
+ "",
+ ColibriConferenceIQ.Channel
+ .ADAPTIVE_SIMULCAST_ATTR_NAME);
+
+ if (!StringUtils.isNullOrEmpty(adaptiveSimulcast))
+ {
+ channel.setAdaptiveSimulcast(
+ Boolean.parseBoolean(adaptiveSimulcast));
+ }
+
// simulcastMode
String simulcastMode
= parser.getAttributeValue(
@@ -802,12 +835,12 @@ public class ColibriIQProvider
iq = conference;
}
- else if (GracefulShutdownIQ.ELEMENT_NAME.equals(parser.getName())
- && GracefulShutdownIQ.NAMESPACE.equals(namespace))
+ else if (ShutdownIQ.NAMESPACE.equals(namespace) &&
+ ShutdownIQ.isValidElementName(parser.getName()))
{
String rootElement = parser.getName();
- iq = new GracefulShutdownIQ();
+ iq = ShutdownIQ.createShutdownIQ(rootElement);
boolean done = false;
@@ -825,12 +858,6 @@ public class ColibriIQProvider
}
break;
}
-
- case XmlPullParser.TEXT:
- {
- // Parse some text here
- break;
- }
}
}
}
@@ -891,12 +918,6 @@ public class ColibriIQProvider
}
break;
}
-
- case XmlPullParser.TEXT:
- {
- // Parse some text here
- break;
- }
}
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java
index d5a6ce1..ef80392 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ColibriStreamConnector.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,74 +15,74 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri;
-
-import org.jitsi.service.neomedia.*;
-
-/**
- * Implements a <tt>StreamConnector</tt> which allows sharing a specific
- * <tt>StreamConnector</tt> instance among multiple <tt>TransportManager</tt>s
- * for the purposes of the Jitsi Videobridge.
- *
- * @author Lyubomir Marinov
- */
-public class ColibriStreamConnector
- extends StreamConnectorDelegate<StreamConnector>
-{
- /**
- * Initializes a new <tt>ColibriStreamConnector</tt> instance which is to
- * share a specific <tt>StreamConnector</tt> instance among multiple
- * <tt>TransportManager</tt>s for the purposes of the Jitsi Videobridge.
- *
- * @param streamConnector the <tt>StreamConnector</tt> instance to be shared
- * by the new instance among multiple <tt>TransportManager</tt>s for the
- * purposes of the Jitsi Videobridge
- */
- public ColibriStreamConnector(StreamConnector streamConnector)
- {
- super(streamConnector);
- }
-
- /**
- * {@inheritDoc}
- *
- * Overrides {@link StreamConnectorDelegate#close()} in order to prevent the
- * closing of the <tt>StreamConnector</tt> wrapped by this instance because
- * the latter is shared and it is not clear whether no
- * <tt>TransportManager</tt> is using it.
- */
- @Override
- public void close()
- {
- /*
- * Do not close the shared StreamConnector because it is not clear
- * whether no TransportManager is using it.
- */
- }
-
- /**
- * {@inheritDoc}
- *
- * Invokes {@link #close()} on this instance when it is clear that no
- * <tt>TransportManager</tt> is using it in order to release the resources
- * allocated by this instance throughout its life time (that need explicit
- * disposal).
- */
- @Override
- protected void finalize()
- throws Throwable
- {
- try
- {
- /*
- * Close the shared StreamConnector because it is clear that no
- * TrasportManager is using it.
- */
- super.close();
- }
- finally
- {
- super.finalize();
- }
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri;
+
+import org.jitsi.service.neomedia.*;
+
+/**
+ * Implements a <tt>StreamConnector</tt> which allows sharing a specific
+ * <tt>StreamConnector</tt> instance among multiple <tt>TransportManager</tt>s
+ * for the purposes of the Jitsi Videobridge.
+ *
+ * @author Lyubomir Marinov
+ */
+public class ColibriStreamConnector
+ extends StreamConnectorDelegate<StreamConnector>
+{
+ /**
+ * Initializes a new <tt>ColibriStreamConnector</tt> instance which is to
+ * share a specific <tt>StreamConnector</tt> instance among multiple
+ * <tt>TransportManager</tt>s for the purposes of the Jitsi Videobridge.
+ *
+ * @param streamConnector the <tt>StreamConnector</tt> instance to be shared
+ * by the new instance among multiple <tt>TransportManager</tt>s for the
+ * purposes of the Jitsi Videobridge
+ */
+ public ColibriStreamConnector(StreamConnector streamConnector)
+ {
+ super(streamConnector);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Overrides {@link StreamConnectorDelegate#close()} in order to prevent the
+ * closing of the <tt>StreamConnector</tt> wrapped by this instance because
+ * the latter is shared and it is not clear whether no
+ * <tt>TransportManager</tt> is using it.
+ */
+ @Override
+ public void close()
+ {
+ /*
+ * Do not close the shared StreamConnector because it is not clear
+ * whether no TransportManager is using it.
+ */
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Invokes {@link #close()} on this instance when it is clear that no
+ * <tt>TransportManager</tt> is using it in order to release the resources
+ * allocated by this instance throughout its life time (that need explicit
+ * disposal).
+ */
+ @Override
+ protected void finalize()
+ throws Throwable
+ {
+ try
+ {
+ /*
+ * Close the shared StreamConnector because it is clear that no
+ * TrasportManager is using it.
+ */
+ super.close();
+ }
+ finally
+ {
+ super.finalize();
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ShutdownIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ShutdownIQ.java
new file mode 100644
index 0000000..4df251b
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/ShutdownIQ.java
@@ -0,0 +1,134 @@
+/*
+ * 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.jabber.extensions.colibri;
+
+import org.jivesoftware.smack.packet.*;
+
+/**
+ * The IQ used to trigger the graceful shutdown mode of the videobridge or force
+ * shutdown the one which receives the stanza(given that source JID is
+ * authorized to do so).
+ *
+ * @author Pawel Domas
+ */
+public class ShutdownIQ
+ extends IQ
+{
+ /**
+ * XML namespace name for shutdown IQs.
+ */
+ final static public String NAMESPACE = ColibriConferenceIQ.NAMESPACE;
+
+ /**
+ * Force shutdown IQ element name.
+ */
+ final static public String FORCE_ELEMENT_NAME = "force-shutdown";
+
+ /**
+ * Graceful shutdown IQ element name.
+ */
+ final static public String GRACEFUL_ELEMENT_NAME = "graceful-shutdown";
+
+ /**
+ * The element name of this IQ. Either {@link #FORCE_ELEMENT_NAME} or
+ * {@link #GRACEFUL_ELEMENT_NAME}.
+ */
+ private final String elementName;
+
+ /**
+ * Checks if given element is a valid one for <tt>ShutdownIQ</tt>.
+ *
+ * @param elementName the name if XML element name inside of the IQ.
+ *
+ * @return <tt>true</tt> if given <tt>elementName</tt> is correct for
+ * <tt>ShutdownIQ</tt>.
+ */
+ public static boolean isValidElementName(String elementName)
+ {
+ return GRACEFUL_ELEMENT_NAME.equals(elementName)
+ || FORCE_ELEMENT_NAME.equals(elementName);
+ }
+
+ /**
+ * Creates shutdown IQ for given element name.
+ *
+ * @param elementName can be {@link #FORCE_ELEMENT_NAME} or
+ * {@link #GRACEFUL_ELEMENT_NAME}
+ *
+ * @return new <tt>ShutdownIQ</tt> instance for given element name.
+ *
+ * @throws IllegalArgumentException if given element name is neither
+ * {@link #FORCE_ELEMENT_NAME} nor {@link #GRACEFUL_ELEMENT_NAME}.
+ */
+ public static ShutdownIQ createShutdownIQ(String elementName)
+ {
+ if (!isValidElementName(elementName))
+ {
+ throw new IllegalArgumentException(
+ "Invalid element name: " + elementName);
+ }
+
+ if (GRACEFUL_ELEMENT_NAME.equals(elementName))
+ {
+ return createGracefulShutdownIQ();
+ }
+ else
+ {
+ return createForceShutdownIQ();
+ }
+ }
+
+ /**
+ * Creates and returns new instance of graceful shutdown IQ.
+ */
+ public static ShutdownIQ createGracefulShutdownIQ()
+ {
+ return new ShutdownIQ(GRACEFUL_ELEMENT_NAME);
+ }
+
+ /**
+ * Creates and returns new instance of force shutdown IQ.
+ */
+ public static ShutdownIQ createForceShutdownIQ()
+ {
+ return new ShutdownIQ(FORCE_ELEMENT_NAME);
+ }
+
+ private ShutdownIQ(String elementName)
+ {
+ this.elementName = elementName;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this IQ instance is a "graceful shutdown" one.
+ * Otherwise it is a force shutdown IQ.
+ */
+ public boolean isGracefulShutdown()
+ {
+ return elementName.equals(GRACEFUL_ELEMENT_NAME);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getChildElementXML()
+ {
+ return "<" + elementName + " xmlns='" + NAMESPACE + "' />";
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/GracefulShutdownIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQ.java
index 9808b11..661bb1b 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/colibri/GracefulShutdownIQ.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQ.java
@@ -15,23 +15,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.jabber.extensions.colibri;
+package net.java.sip.communicator.impl.protocol.jabber.extensions.health;
import org.jivesoftware.smack.packet.*;
/**
- * The IQ used to trigger the graceful shutdown mode of the videobridge which
- * receives the stanza(given that source JID is authorized to start it).
+ * The health check IQ used to trigger health checks on the Jitsi Videobridge.
*
* @author Pawel Domas
*/
-public class GracefulShutdownIQ
+public class HealthCheckIQ
extends IQ
{
- public static final String NAMESPACE = ColibriConferenceIQ.NAMESPACE;
+ /**
+ * Health check IQ element name.
+ */
+ final static public String ELEMENT_NAME = "healthcheck";
- public static final String ELEMENT_NAME = "graceful-shutdown";
+ /**
+ * XML namespace name for health check IQs.
+ */
+ final static public String NAMESPACE
+ = "http://jitsi.org/protocol/healthcheck";
+ /**
+ * {@inheritDoc}
+ */
@Override
public String getChildElementXML()
{
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQProvider.java
new file mode 100644
index 0000000..9c2903d
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/health/HealthCheckIQProvider.java
@@ -0,0 +1,94 @@
+/*
+ * 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.jabber.extensions.health;
+
+import net.java.sip.communicator.service.protocol.jabber.*;
+
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.provider.*;
+
+import org.xmlpull.v1.*;
+
+/**
+ * The <tt>IQProvider</tt> for {@link HealthCheckIQ}.
+ *
+ * @author Pawel Domas
+ */
+public class HealthCheckIQProvider
+ implements IQProvider
+{
+ /**
+ * Registers <tt>HealthCheckIQProvider</tt> as an <tt>IQProvider</tt>
+ * in {@link AbstractSmackInteroperabilityLayer}.
+ */
+ public static void registerIQProvider()
+ {
+ AbstractSmackInteroperabilityLayer smackInteropLayer =
+ AbstractSmackInteroperabilityLayer.getInstance();
+
+ // ColibriStatsIQ
+ smackInteropLayer.addIQProvider(
+ HealthCheckIQ.ELEMENT_NAME,
+ HealthCheckIQ.NAMESPACE,
+ new HealthCheckIQProvider());
+ }
+
+ /**
+ * Parses <tt>HealthCheckIQ</tt>.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public IQ parseIQ(XmlPullParser parser)
+ throws Exception
+ {
+ String namespace = parser.getNamespace();
+ IQ iq;
+
+ if (HealthCheckIQ.ELEMENT_NAME.equals(parser.getName())
+ && HealthCheckIQ.NAMESPACE.equals(namespace))
+ {
+ String rootElement = parser.getName();
+
+ iq = new HealthCheckIQ();
+
+ boolean done = false;
+
+ while (!done)
+ {
+ switch (parser.next())
+ {
+ case XmlPullParser.END_TAG:
+ {
+ String name = parser.getName();
+
+ if (rootElement.equals(name))
+ {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+ else
+ iq = null;
+
+ return iq;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIq.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIq.java
new file mode 100644
index 0000000..8b964af
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIq.java
@@ -0,0 +1,413 @@
+/*
+ * 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.jabber.extensions.jibri;
+
+import org.jitsi.util.*;
+
+import org.jivesoftware.smack.packet.*;
+
+import java.util.*;
+
+/**
+ * The IQ used to control conference recording with Jibri component.
+ *
+ * Start the recording:
+ *
+ * 1. Send Jibri IQ with {@link Action#START} to Jibri.
+ * 2. Jibri replies with RESULT and status {@link Status#PENDING}.
+ * 3. Jibri sends SET IQ with status {@link Status#ON} once recording actually
+ * starts.
+ *
+ * Stop the recording:
+ *
+ * 1. Send Jibri IQ with {@link Action#STOP} to Jibri.
+ * 2. Jibri replies with {@link Status#OFF} immediately if the recording has
+ * been stopped already or sends separate Jibri SET IQ later on if it takes
+ * more time.
+ *
+ * @author lishunyang
+ * @author Pawel Domas
+ */
+public class JibriIq
+ extends IQ
+{
+ /**
+ * Attribute name of "action".
+ */
+ public static final String ACTION_ATTR_NAME = "action";
+
+ /**
+ * XML element name of the Jibri IQ.
+ */
+ public static final String ELEMENT_NAME = "jibri";
+
+ /**
+ * XML namespace of the Jibri IQ.
+ */
+ public static final String NAMESPACE = "http://jitsi.org/protocol/jibri";
+
+ /**
+ * The name of XML attribute which stores the recording status.
+ */
+ static final String STATUS_ATTR_NAME = "status";
+
+ /**
+ * The name of XML attribute which stores the stream id.
+ */
+ static final String STREAM_ID_ATTR_NAME = "streamid";
+
+ /**
+ * The name of XML attribute which stores the name of the conference room to
+ * be recorded.
+ */
+ static final String ROOM_ATTR_NAME = "room";
+
+ /**
+ * Holds the action.
+ */
+ private Action action = Action.UNDEFINED;
+
+ /**
+ * XMPPError stores error details for {@link Status#FAILED}.
+ */
+ private XMPPError error;
+
+ /**
+ * Holds recording status.
+ */
+ private Status status = Status.UNDEFINED;
+
+ /**
+ * The ID of the stream which will be used to record the conference. The
+ * value depends on recording service provider.
+ */
+ private String streamId = null;
+
+ /**
+ * The name of the conference room to be recorded.
+ */
+ private String room = null;
+
+ /**
+ * Returns the value of {@link #STREAM_ID_ATTR_NAME} attribute.
+ * @return a <tt>String</tt> which contains the value of "stream id"
+ * attribute or <tt>null</tt> if empty.
+ */
+ public String getStreamId()
+ {
+ return streamId;
+ }
+
+ /**
+ * Sets the value for {@link #STREAM_ID_ATTR_NAME} attribute.
+ * @param streamId a <tt>String</tt> for the stream id attribute or
+ * <tt>null</tt> to remove it from XML element.
+ */
+ public void setStreamId(String streamId)
+ {
+ this.streamId = streamId;
+ }
+
+ /**
+ * Returns the value of {@link #ROOM_ATTR_NAME} attribute.
+ * @return a <tt>String</tt> which contains the value of the room attribute
+ * or <tt>null</tt> if empty.
+ * @see #room
+ */
+ public String getRoom()
+ {
+ return room;
+ }
+
+ /**
+ * Sets the value for {@link #ROOM_ATTR_NAME} attribute.
+ * @param room a <tt>String</tt> for the room attribute or <tt>null</tt> to
+ * remove it from XML element.
+ * @see #room
+ */
+ public void setRoom(String room)
+ {
+ this.room = room;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getChildElementXML()
+ {
+ StringBuilder xml = new StringBuilder();
+
+ xml.append('<').append(ELEMENT_NAME);
+ xml.append(" xmlns='").append(NAMESPACE).append("' ");
+
+ if (action != Action.UNDEFINED)
+ {
+ printStringAttribute(xml, ACTION_ATTR_NAME, action.toString());
+ }
+
+ if (status != Status.UNDEFINED)
+ {
+ printStringAttribute(xml, STATUS_ATTR_NAME, status.toString());
+ }
+
+ if (room != null)
+ {
+ printStringAttribute(xml, ROOM_ATTR_NAME, room);
+ }
+
+ if (streamId != null)
+ {
+ printStringAttribute(xml, STREAM_ID_ATTR_NAME, streamId);
+ }
+
+ Collection<PacketExtension> extensions = getExtensions();
+ if (extensions.size() > 0)
+ {
+ xml.append(">");
+ for (PacketExtension extension : extensions)
+ {
+ xml.append(extension.toXML());
+ }
+ xml.append("</").append(ELEMENT_NAME).append(">");
+ }
+ else
+ {
+ xml.append("/>");
+ }
+
+ return xml.toString();
+ }
+
+ private void printStringAttribute(
+ StringBuilder xml, String attrName, String attr)
+ {
+ if (!StringUtils.isNullOrEmpty(attr))
+ {
+ attr = org.jivesoftware.smack.util.StringUtils.escapeForXML(attr);
+ xml.append(attrName).append("='")
+ .append(attr).append("' ");
+ }
+ }
+
+ /**
+ * Sets the value of 'action' attribute.
+ *
+ * @param action the value to be set as 'action' attribute of this IQ.
+ */
+ public void setAction(Action action)
+ {
+ this.action = action;
+ }
+
+ /**
+ * Returns the value of 'action' attribute.
+ */
+ public Action getAction()
+ {
+ return action;
+ }
+
+ /**
+ * Sets the value of 'status' attribute.
+ */
+ public void setStatus(Status status)
+ {
+ this.status = status;
+ }
+
+ /**
+ * Returns the value of 'status' attribute.
+ */
+ public Status getStatus()
+ {
+ return status;
+ }
+
+ /**
+ * Sets the <tt>XMPPError</tt> which will provide details about Jibri
+ * failure. It is expected to be set when this IQ's status value is
+ * {@link Status#FAILED}.
+ *
+ * @param error <tt>XMPPError</tt> to be set on this <tt>JibriIq</tt>
+ * instance.
+ */
+ public void setXMPPError(XMPPError error)
+ {
+ this.error = error;
+ }
+
+ /**
+ * Returns {@link XMPPError} with Jibri error details when the status is
+ * {@link Status#FAILED}.
+ */
+ public XMPPError getError()
+ {
+ return error;
+ }
+
+ /**
+ * Enumerative value of attribute "action" in recording extension.
+ *
+ * @author lishunyang
+ * @author Pawel Domas
+ *
+ */
+ public enum Action
+ {
+ /**
+ * Start the recording.
+ */
+ START("start"),
+ /**
+ * Stop the recording.
+ */
+ STOP("stop"),
+ /**
+ * Unknown/uninitialized
+ */
+ UNDEFINED("undefined");
+
+ private String name;
+
+ Action(String name)
+ {
+ this.name = name;
+ }
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Parses <tt>Action</tt> from given string.
+ *
+ * @param action the string representation of <tt>Action</tt>.
+ *
+ * @return <tt>Action</tt> value for given string or
+ * {@link #UNDEFINED} if given string does not
+ * reflect any of valid values.
+ */
+ public static Action parse(String action)
+ {
+ if (StringUtils.isNullOrEmpty(action))
+ return UNDEFINED;
+
+ try
+ {
+ return Action.valueOf(action.toUpperCase());
+ }
+ catch(IllegalArgumentException e)
+ {
+ return UNDEFINED;
+ }
+ }
+ }
+
+ /**
+ * The enumeration of recording status values.
+ */
+ public enum Status
+ {
+ /**
+ * Recording is in progress.
+ */
+ ON("on"),
+
+ /**
+ * Recording stopped.
+ */
+ OFF("off"),
+
+ /**
+ * Starting the recording process.
+ */
+ PENDING("pending"),
+
+ /**
+ * The recorder has failed and the service is retrying on another
+ * instance.
+ */
+ RETRYING("retrying"),
+
+ /**
+ * An error occurred any point during startup, recording or shutdown.
+ */
+ FAILED("failed"),
+
+ /**
+ * There are Jibri instances connected to the system, but all of them
+ * are currently busy.
+ */
+ BUSY("busy"),
+
+ /**
+ * Unknown/uninitialized.
+ */
+ UNDEFINED("undefined");
+
+ /**
+ * Status name holder.
+ */
+ private String name;
+
+ /**
+ * Creates new {@link Status} instance.
+ * @param name a string corresponding to one of {@link Status} values.
+ */
+ Status(String name)
+ {
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Parses <tt>Status</tt> from given string.
+ *
+ * @param status the string representation of <tt>Status</tt>.
+ *
+ * @return <tt>Status</tt> value for given string or
+ * {@link #UNDEFINED} if given string does not
+ * reflect any of valid values.
+ */
+ public static Status parse(String status)
+ {
+ if (StringUtils.isNullOrEmpty(status))
+ return UNDEFINED;
+
+ try
+ {
+ return Status.valueOf(status.toUpperCase());
+ }
+ catch(IllegalArgumentException e)
+ {
+ return UNDEFINED;
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIqProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIqProvider.java
new file mode 100644
index 0000000..155853c
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriIqProvider.java
@@ -0,0 +1,112 @@
+/*
+ * 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.jabber.extensions.jibri;
+
+import org.jitsi.util.*;
+
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.provider.*;
+import org.jivesoftware.smack.util.PacketParserUtils;
+
+import org.xmlpull.v1.*;
+
+/**
+ * Parses {@link JibriIq}.
+ */
+public class JibriIqProvider
+ implements IQProvider
+{
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IQ parseIQ(XmlPullParser parser)
+ throws Exception
+ {
+ String namespace = parser.getNamespace();
+
+ // Check the namespace
+ if (!JibriIq.NAMESPACE.equals(namespace))
+ {
+ return null;
+ }
+
+ String rootElement = parser.getName();
+
+ JibriIq iq;
+
+ if (JibriIq.ELEMENT_NAME.equals(rootElement))
+ {
+ iq = new JibriIq();
+
+ String action
+ = parser.getAttributeValue("", JibriIq.ACTION_ATTR_NAME);
+ iq.setAction(JibriIq.Action.parse(action));
+
+ String status
+ = parser.getAttributeValue("", JibriIq.STATUS_ATTR_NAME);
+ iq.setStatus(JibriIq.Status.parse(status));
+
+ String room
+ = parser.getAttributeValue("", JibriIq.ROOM_ATTR_NAME);
+ if (!StringUtils.isNullOrEmpty(room))
+ iq.setRoom(room);
+
+ String streamId
+ = parser.getAttributeValue("", JibriIq.STREAM_ID_ATTR_NAME);
+ if (!StringUtils.isNullOrEmpty(streamId))
+ iq.setStreamId(streamId);
+ }
+ else
+ {
+ return null;
+ }
+
+ boolean done = false;
+
+ while (!done)
+ {
+ switch (parser.next())
+ {
+ case XmlPullParser.START_TAG:
+ {
+ String name = parser.getName();
+
+ if ("error".equals(name))
+ {
+ XMPPError error = PacketParserUtils.parseError(parser);
+ iq.setXMPPError(error);
+ }
+ break;
+ }
+ case XmlPullParser.END_TAG:
+ {
+ String name = parser.getName();
+
+ if (rootElement.equals(name))
+ {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+
+ return iq;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriStatusPacketExt.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriStatusPacketExt.java
new file mode 100644
index 0000000..e046b68
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/JibriStatusPacketExt.java
@@ -0,0 +1,121 @@
+/*
+ * 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.jabber.extensions.jibri;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
+
+import org.jitsi.util.*;
+
+import org.jivesoftware.smack.provider.*;
+
+/**
+ * Status extension included in MUC presence by Jibri to indicate it's status.
+ * One of:
+ * <li>idle</li> - the instance is idle and can be used for recording
+ * <li>busy</li> - the instance is currently recording or doing something very
+ * important and should not be disturbed
+ *
+ *
+ */
+public class JibriStatusPacketExt
+ extends AbstractPacketExtension
+{
+ /**
+ * The namespace of this packet extension.
+ */
+ public static final String NAMESPACE = JibriIq.NAMESPACE;
+
+ /**
+ * XML element name of this packet extension.
+ */
+ public static final String ELEMENT_NAME = "jibri-status";
+
+ private static final String STATUS_ATTRIBUTE = "status";
+
+ /**
+ * Creates new instance of <tt>VideoMutedExtension</tt>.
+ */
+ public JibriStatusPacketExt()
+ {
+ super(NAMESPACE, ELEMENT_NAME);
+ }
+
+ static public void registerExtensionProvider()
+ {
+ ProviderManager.getInstance().addExtensionProvider(
+ ELEMENT_NAME,
+ NAMESPACE,
+ new DefaultPacketExtensionProvider<JibriStatusPacketExt>(
+ JibriStatusPacketExt.class)
+ );
+ }
+
+ public Status getStatus()
+ {
+ return Status.parse(getAttributeAsString(STATUS_ATTRIBUTE));
+ }
+
+ public void setStatus(Status status)
+ {
+ setAttribute(STATUS_ATTRIBUTE, String.valueOf(status));
+ }
+
+ public enum Status
+ {
+ IDLE("idle"),
+ BUSY("busy"),
+ UNDEFINED("undefined");
+
+ private String name;
+
+ Status(String name)
+ {
+ this.name = name;
+ }
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Parses <tt>Status</tt> from given string.
+ *
+ * @param status the string representation of <tt>Status</tt>.
+ *
+ * @return <tt>Status</tt> value for given string or
+ * {@link #UNDEFINED} if given string does not
+ * reflect any of valid values.
+ */
+ public static Status parse(String status)
+ {
+ if (StringUtils.isNullOrEmpty(status))
+ return UNDEFINED;
+
+ try
+ {
+ return Status.valueOf(status.toUpperCase());
+ }
+ catch(IllegalArgumentException e)
+ {
+ return UNDEFINED;
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/RecordingStatus.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/RecordingStatus.java
new file mode 100644
index 0000000..13177cf
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/RecordingStatus.java
@@ -0,0 +1,126 @@
+/*
+ * 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.jabber.extensions.jibri;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
+
+import org.jivesoftware.smack.packet.*;
+
+import java.util.*;
+
+/**
+ * The packet extension added to Jicofo MUC presence to broadcast current
+ * recording status to all conference participants.
+ *
+ * Status meaning:
+ * <tt>{@link JibriIq.Status#UNDEFINED}</tt> - recording not available
+ * <tt>{@link JibriIq.Status#OFF}</tt> - recording stopped(available to start)
+ * <tt>{@link JibriIq.Status#PENDING}</tt> - starting recording
+ * <tt>{@link JibriIq.Status#ON}</tt> - recording in progress
+ */
+public class RecordingStatus
+ extends AbstractPacketExtension
+{
+ /**
+ * The namespace of this packet extension.
+ */
+ public static final String NAMESPACE = JibriIq.NAMESPACE;
+
+ /**
+ * XML element name of this packet extension.
+ */
+ public static final String ELEMENT_NAME = "jibri-recording-status";
+
+ /**
+ * The name of XML attribute which holds the recording status.
+ */
+ private static final String STATUS_ATTRIBUTE = "status";
+
+ public RecordingStatus()
+ {
+ super(NAMESPACE, ELEMENT_NAME);
+ }
+
+ /**
+ * Returns the value of current recording status stored in it's attribute.
+ * @return one of {@link JibriIq.Status}
+ */
+ public JibriIq.Status getStatus()
+ {
+ String statusAttr = getAttributeAsString(STATUS_ATTRIBUTE);
+
+ return JibriIq.Status.parse(statusAttr);
+ }
+
+ /**
+ * Sets new value for the recording status.
+ * @param status one of {@link JibriIq.Status}
+ */
+ public void setStatus(JibriIq.Status status)
+ {
+ setAttribute(STATUS_ATTRIBUTE, String.valueOf(status));
+ }
+
+ /**
+ * Returns <tt>XMPPError</tt> associated with current
+ * {@link RecordingStatus}.
+ */
+ public XMPPError getError()
+ {
+ XMPPErrorPE errorPe = getErrorPE();
+ return errorPe != null ? errorPe.getError() : null;
+ }
+
+ /**
+ * Gets <tt>{@link XMPPErrorPE}</tt> from the list of child packet
+ * extensions.
+ * @return {@link XMPPErrorPE} or <tt>null</tt> if not found.
+ */
+ private XMPPErrorPE getErrorPE()
+ {
+ List<? extends PacketExtension> errorPe
+ = getChildExtensionsOfType(XMPPErrorPE.class);
+
+ return (XMPPErrorPE) (!errorPe.isEmpty() ? errorPe.get(0) : null);
+ }
+
+ /**
+ * Sets <tt>XMPPError</tt> on this <tt>RecordingStatus</tt>.
+ * @param error <tt>XMPPError</tt> to add error details to this
+ * <tt>RecordingStatus</tt> instance or <tt>null</tt> to have it removed.
+ */
+ public void setError(XMPPError error)
+ {
+ if (error != null)
+ {
+ // Wrap and add XMPPError as packet extension
+ XMPPErrorPE errorPe = getErrorPE();
+ if (errorPe == null)
+ {
+ errorPe = new XMPPErrorPE(error);
+ addChildExtension(errorPe);
+ }
+ errorPe.setError(error);
+ }
+ else
+ {
+ // Remove error PE
+ getChildExtensions().remove(getErrorPE());
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/XMPPErrorPE.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/XMPPErrorPE.java
new file mode 100644
index 0000000..a72f310
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jibri/XMPPErrorPE.java
@@ -0,0 +1,93 @@
+/*
+ * 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.jabber.extensions.jibri;
+
+import org.jivesoftware.smack.packet.*;
+
+import java.util.*;
+
+/**
+ * Wraps Smack's <tt>XMPPError</tt> into <tt>PacketExtension</tt>, so that it
+ * can be easily inserted into {@link RecordingStatus}.
+ */
+public class XMPPErrorPE
+ implements PacketExtension
+{
+ /**
+ * <tt>XMPPError</tt> wrapped into this <tt>XMPPErrorPE</tt>.
+ */
+ private XMPPError error;
+
+ /**
+ * Creates new instance of <tt>XMPPErrorPE</tt>.
+ * @param xmppError the instance of <tt>XMPPError</tt> that will be wrapped
+ * by the newly created <tt>XMPPErrorPE</tt>.
+ */
+ public XMPPErrorPE(XMPPError xmppError)
+ {
+ setError(xmppError);
+ }
+
+ /**
+ * Returns the underlying instance of <tt>XMPPError</tt>.
+ */
+ public XMPPError getError()
+ {
+ return error;
+ }
+
+ /**
+ * Sets new instance of <tt>XMPPError</tt> to be wrapped by this
+ * <tt>XMPPErrorPE</tt>.
+ * @param error <tt>XMPPError</tt> that will be wrapped by this
+ * <TT>XMPPErrorPE</TT>.
+ */
+ public void setError(XMPPError error)
+ {
+ Objects.requireNonNull(error, "error");
+
+ this.error = error;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getElementName()
+ {
+ return "error";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getNamespace()
+ {
+ return "";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toXML()
+ {
+ return error.toXML();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java
index 0c5b190..46b1e77 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CandidatePacketExtension.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,441 +15,441 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle;
-
-import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
-import org.ice4j.ice.*;
-
-/**
- * @author Emil Ivov
- */
-public class CandidatePacketExtension extends AbstractPacketExtension
- implements Comparable<CandidatePacketExtension>
-{
- /**
- * The name of the "candidate" element.
- */
- public static final String ELEMENT_NAME = "candidate";
-
- /**
- * The name of the "component" element.
- */
- public static final String COMPONENT_ATTR_NAME = "component";
-
- /**
- * The "component" ID for RTP components.
- */
- public static final int RTP_COMPONENT_ID = 1;
-
- /**
- * The "component" ID for RTCP components.
- */
- public static final int RTCP_COMPONENT_ID = 2;
-
- /**
- * The name of the "foundation" element.
- */
- public static final String FOUNDATION_ATTR_NAME = "foundation";
-
- /**
- * The name of the "generation" element.
- */
- public static final String GENERATION_ATTR_NAME = "generation";
-
- /**
- * The name of the "id" element.
- */
- public static final String ID_ATTR_NAME = "id";
-
- /**
- * The name of the "ip" element.
- */
- public static final String IP_ATTR_NAME = "ip";
-
- /**
- * The name of the "network" element.
- */
- public static final String NETWORK_ATTR_NAME = "network";
-
- /**
- * The name of the "port" element.
- */
- public static final String PORT_ATTR_NAME = "port";
-
- /**
- * The name of the "priority" element.
- */
- public static final String PRIORITY_ATTR_NAME = "priority";
-
- /**
- * The name of the "protocol" element.
- */
- public static final String PROTOCOL_ATTR_NAME = "protocol";
-
- /**
- * The name of the "rel-addr" element.
- */
- public static final String REL_ADDR_ATTR_NAME = "rel-addr";
-
- /**
- * The name of the "rel-port" element.
- */
- public static final String REL_PORT_ATTR_NAME = "rel-port";
-
- /**
- * The name of the "type" element.
- */
- public static final String TYPE_ATTR_NAME = "type";
-
- /**
- * The name of the "tcptype" element.
- */
- public static final String TCPTYPE_ATTR_NAME = "tcptype";
-
- /**
- * Creates a new {@link CandidatePacketExtension}
- */
- public CandidatePacketExtension()
- {
- super(null, ELEMENT_NAME);
- }
-
- /**
- * Creates a new {@link CandidatePacketExtension} with the specified
- * <tt>elementName</tt> so that this class would be usable as a
- * <tt>RemoteCandidatePacketExtension</tt> parent.
- *
- * @param elementName the element name that this instance should be using.
- */
- protected CandidatePacketExtension(String elementName)
- {
- super(null, elementName);
- }
-
- /**
- * Sets a component ID as defined in ICE-CORE.
- *
- * @param component a component ID as defined in ICE-CORE.
- */
- public void setComponent(int component)
- {
- super.setAttribute(COMPONENT_ATTR_NAME, component);
- }
-
- /**
- * Returns a component ID as defined in ICE-CORE.
- *
- * @return a component ID as defined in ICE-CORE.
- */
- public int getComponent()
- {
- return super.getAttributeAsInt(COMPONENT_ATTR_NAME);
- }
-
- /**
- * Sets the candidate foundation as defined in ICE-CORE.
- *
- * @param foundation the candidate foundation as defined in ICE-CORE.
- */
- public void setFoundation(String foundation)
- {
- super.setAttribute(FOUNDATION_ATTR_NAME, foundation);
- }
-
- /**
- * Returns the candidate foundation as defined in ICE-CORE.
- *
- * @return the candidate foundation as defined in ICE-CORE.
- */
- public String getFoundation()
- {
- return super.getAttributeAsString(FOUNDATION_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's generation index. A generation is an index,
- * starting at 0, that enables the parties to keep track of updates to the
- * candidate throughout the life of the session. For details, see the ICE
- * Restarts section of XEP-0176.
- *
- * @param generation this candidate's generation index.
- */
- public void setGeneration(int generation)
- {
- super.setAttribute(GENERATION_ATTR_NAME, generation);
- }
-
- /**
- * Returns this candidate's generation. A generation is an index, starting at
- * 0, that enables the parties to keep track of updates to the candidate
- * throughout the life of the session. For details, see the ICE Restarts
- * section of XEP-0176.
- *
- * @return this candidate's generation index.
- */
- public int getGeneration()
- {
- return super.getAttributeAsInt(GENERATION_ATTR_NAME);
- }
-
- /**
- * Sets this candidates's unique identifier <tt>String</tt>.
- *
- * @param id this candidates's unique identifier <tt>String</tt>
- */
- public void setID(String id)
- {
- super.setAttribute(ID_ATTR_NAME, id);
- }
-
- /**
- * Returns this candidates's unique identifier <tt>String</tt>.
- *
- * @return this candidates's unique identifier <tt>String</tt>
- */
- public String getID()
- {
- return super.getAttributeAsString(ID_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's Internet Protocol (IP) address; this can be either
- * an IPv4 address or an IPv6 address.
- *
- * @param ip this candidate's IPv4 or IPv6 address.
- */
- public void setIP(String ip)
- {
- super.setAttribute(IP_ATTR_NAME, ip);
- }
-
- /**
- * Returns this candidate's Internet Protocol (IP) address; this can be
- * either an IPv4 address or an IPv6 address.
- *
- * @return this candidate's IPv4 or IPv6 address.
- */
- public String getIP()
- {
- return super.getAttributeAsString(IP_ATTR_NAME);
- }
-
- /**
- * The network index indicating the interface that the candidate belongs to.
- * The network ID is used for diagnostic purposes only in cases where the
- * calling hardware has more than one Network Interface Card.
- *
- * @param network the network index indicating the interface that the
- * candidate belongs to.
- */
- public void setNetwork(int network)
- {
- super.setAttribute(NETWORK_ATTR_NAME, network);
- }
-
- /**
- * Returns the network index indicating the interface that the candidate
- * belongs to. The network ID is used for diagnostic purposes only in cases
- * where the calling hardware has more than one Network Interface Card.
- *
- * @return the network index indicating the interface that the candidate
- * belongs to.
- */
- public int getNetwork()
- {
- return super.getAttributeAsInt(NETWORK_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's port number.
- *
- * @param port this candidate's port number.
- */
- public void setPort(int port)
- {
- super.setAttribute(PORT_ATTR_NAME, port);
- }
-
- /**
- * Returns this candidate's port number.
- *
- * @return this candidate's port number.
- */
- public int getPort()
- {
- return super.getAttributeAsInt(PORT_ATTR_NAME);
- }
-
- /**
- * This candidate's priority as defined in ICE's RFC 5245
- *
- * @param priority this candidate's priority
- */
- public void setPriority(long priority)
- {
- super.setAttribute(PRIORITY_ATTR_NAME, priority);
- }
-
- /**
- * This candidate's priority as defined in ICE's RFC 5245
- *
- * @return this candidate's priority
- */
- public int getPriority()
- {
- return super.getAttributeAsInt(PRIORITY_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's transport protocol.
- *
- * @param protocol this candidate's transport protocol.
- */
- public void setProtocol(String protocol)
- {
- super.setAttribute(PROTOCOL_ATTR_NAME, protocol);
- }
-
- /**
- * Sets this candidate's transport protocol.
- *
- * @return this candidate's transport protocol.
- */
- public String getProtocol()
- {
- return super.getAttributeAsString(PROTOCOL_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's related address as described by ICE's RFC 5245.
- *
- * @param relAddr this candidate's related address as described by ICE's
- * RFC 5245.
- */
- public void setRelAddr(String relAddr)
- {
- super.setAttribute(REL_ADDR_ATTR_NAME, relAddr);
- }
-
- /**
- * Returns this candidate's related address as described by ICE's RFC 5245.
- *
- * @return this candidate's related address as described by ICE's RFC 5245.
- */
- public String getRelAddr()
- {
- return super.getAttributeAsString(REL_ADDR_ATTR_NAME);
- }
-
- /**
- * Sets this candidate's related port as described by ICE's RFC 5245.
- *
- * @param relPort this candidate's related port as described by ICE's
- * RFC 5245.
- */
- public void setRelPort(int relPort)
- {
- super.setAttribute(REL_PORT_ATTR_NAME, relPort);
- }
-
- /**
- * Returns this candidate's related port as described by ICE's RFC 5245.
- *
- * @return this candidate's related port as described by ICE's RFC 5245.
- */
- public int getRelPort()
- {
- return super.getAttributeAsInt(REL_PORT_ATTR_NAME);
- }
-
- /**
- * Sets a Candidate Type as defined in ICE-CORE. The allowable values are
- * "host" for host candidates, "prflx" for peer reflexive candidates,
- * "relay" for relayed candidates, and "srflx" for server reflexive
- * candidates. All allowable values are enumerated in the {@link
- * CandidateType} enum.
- *
- * @param type this candidates' type as per ICE's RFC 5245.
- */
- public void setType(CandidateType type)
- {
- super.setAttribute(TYPE_ATTR_NAME, type);
- }
-
- /**
- * Returns a Candidate Type as defined in ICE-CORE. The allowable values are
- * "host" for host candidates, "prflx" for peer reflexive candidates,
- * "relay" for relayed candidates, and "srflx" for server reflexive
- * candidates. All allowable values are enumerated in the {@link
- * CandidateType} enum.
- *
- * @return this candidates' type as per ICE's RFC 5245.
- */
- public CandidateType getType()
- {
- return CandidateType.valueOf(getAttributeAsString(TYPE_ATTR_NAME));
- }
-
- /**
- * Compares this instance with another CandidatePacketExtension by
- * preference of type: host < local < prflx < srflx < stun < relay.
- *
- * @return 0 if the type are equal. -1 if this instance type is preferred.
- * Otherwise 1.
- */
- public int compareTo(CandidatePacketExtension candidatePacketExtension)
- {
- // If the types are different.
- if(this.getType() != candidatePacketExtension.getType())
- {
- CandidateType[] types = {
- CandidateType.host,
- CandidateType.local,
- CandidateType.prflx,
- CandidateType.srflx,
- CandidateType.stun,
- CandidateType.relay
- };
- for(int i = 0; i < types.length; ++i)
- {
- // this object is preferred.
- if(types[i] == this.getType())
- {
- return -1;
- }
- // the candidatePacketExtension is preferred.
- else if(types[i] == candidatePacketExtension.getType())
- {
- return 1;
- }
- }
- }
- // If the types are equal.
- return 0;
- }
-
- /**
- * Gets the TCP type for this <tt>CandidatePacketExtension</tt>.
- */
- public CandidateTcpType getTcpType()
- {
- String tcpTypeString = getAttributeAsString(TCPTYPE_ATTR_NAME);
- try
- {
- return CandidateTcpType.parse(tcpTypeString);
- }
- catch (IllegalArgumentException iae)
- {
- return null;
- }
- }
-
- /**
- * Sets the TCP type for this <tt>CandidatePacketExtension</tt>.
- * @param tcpType
- */
- public void setTcpType(CandidateTcpType tcpType)
- {
- setAttribute(TCPTYPE_ATTR_NAME, tcpType.toString());
- }
-}
+package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
+import org.ice4j.ice.*;
+
+/**
+ * @author Emil Ivov
+ */
+public class CandidatePacketExtension extends AbstractPacketExtension
+ implements Comparable<CandidatePacketExtension>
+{
+ /**
+ * The name of the "candidate" element.
+ */
+ public static final String ELEMENT_NAME = "candidate";
+
+ /**
+ * The name of the "component" element.
+ */
+ public static final String COMPONENT_ATTR_NAME = "component";
+
+ /**
+ * The "component" ID for RTP components.
+ */
+ public static final int RTP_COMPONENT_ID = 1;
+
+ /**
+ * The "component" ID for RTCP components.
+ */
+ public static final int RTCP_COMPONENT_ID = 2;
+
+ /**
+ * The name of the "foundation" element.
+ */
+ public static final String FOUNDATION_ATTR_NAME = "foundation";
+
+ /**
+ * The name of the "generation" element.
+ */
+ public static final String GENERATION_ATTR_NAME = "generation";
+
+ /**
+ * The name of the "id" element.
+ */
+ public static final String ID_ATTR_NAME = "id";
+
+ /**
+ * The name of the "ip" element.
+ */
+ public static final String IP_ATTR_NAME = "ip";
+
+ /**
+ * The name of the "network" element.
+ */
+ public static final String NETWORK_ATTR_NAME = "network";
+
+ /**
+ * The name of the "port" element.
+ */
+ public static final String PORT_ATTR_NAME = "port";
+
+ /**
+ * The name of the "priority" element.
+ */
+ public static final String PRIORITY_ATTR_NAME = "priority";
+
+ /**
+ * The name of the "protocol" element.
+ */
+ public static final String PROTOCOL_ATTR_NAME = "protocol";
+
+ /**
+ * The name of the "rel-addr" element.
+ */
+ public static final String REL_ADDR_ATTR_NAME = "rel-addr";
+
+ /**
+ * The name of the "rel-port" element.
+ */
+ public static final String REL_PORT_ATTR_NAME = "rel-port";
+
+ /**
+ * The name of the "type" element.
+ */
+ public static final String TYPE_ATTR_NAME = "type";
+
+ /**
+ * The name of the "tcptype" element.
+ */
+ public static final String TCPTYPE_ATTR_NAME = "tcptype";
+
+ /**
+ * Creates a new {@link CandidatePacketExtension}
+ */
+ public CandidatePacketExtension()
+ {
+ super(null, ELEMENT_NAME);
+ }
+
+ /**
+ * Creates a new {@link CandidatePacketExtension} with the specified
+ * <tt>elementName</tt> so that this class would be usable as a
+ * <tt>RemoteCandidatePacketExtension</tt> parent.
+ *
+ * @param elementName the element name that this instance should be using.
+ */
+ protected CandidatePacketExtension(String elementName)
+ {
+ super(null, elementName);
+ }
+
+ /**
+ * Sets a component ID as defined in ICE-CORE.
+ *
+ * @param component a component ID as defined in ICE-CORE.
+ */
+ public void setComponent(int component)
+ {
+ super.setAttribute(COMPONENT_ATTR_NAME, component);
+ }
+
+ /**
+ * Returns a component ID as defined in ICE-CORE.
+ *
+ * @return a component ID as defined in ICE-CORE.
+ */
+ public int getComponent()
+ {
+ return super.getAttributeAsInt(COMPONENT_ATTR_NAME);
+ }
+
+ /**
+ * Sets the candidate foundation as defined in ICE-CORE.
+ *
+ * @param foundation the candidate foundation as defined in ICE-CORE.
+ */
+ public void setFoundation(String foundation)
+ {
+ super.setAttribute(FOUNDATION_ATTR_NAME, foundation);
+ }
+
+ /**
+ * Returns the candidate foundation as defined in ICE-CORE.
+ *
+ * @return the candidate foundation as defined in ICE-CORE.
+ */
+ public String getFoundation()
+ {
+ return super.getAttributeAsString(FOUNDATION_ATTR_NAME);
+ }
+
+ /**
+ * Sets this candidate's generation index. A generation is an index,
+ * starting at 0, that enables the parties to keep track of updates to the
+ * candidate throughout the life of the session. For details, see the ICE
+ * Restarts section of XEP-0176.
+ *
+ * @param generation this candidate's generation index.
+ */
+ public void setGeneration(int generation)
+ {
+ super.setAttribute(GENERATION_ATTR_NAME, generation);
+ }
+
+ /**
+ * Returns this candidate's generation. A generation is an index, starting at
+ * 0, that enables the parties to keep track of updates to the candidate
+ * throughout the life of the session. For details, see the ICE Restarts
+ * section of XEP-0176.
+ *
+ * @return this candidate's generation index.
+ */
+ public int getGeneration()
+ {
+ return super.getAttributeAsInt(GENERATION_ATTR_NAME);
+ }
+
+ /**
+ * Sets this candidates's unique identifier <tt>String</tt>.
+ *
+ * @param id this candidates's unique identifier <tt>String</tt>
+ */
+ public void setID(String id)
+ {
+ super.setAttribute(ID_ATTR_NAME, id);
+ }
+
+ /**
+ * Returns this candidates's unique identifier <tt>String</tt>.
+ *
+ * @return this candidates's unique identifier <tt>String</tt>
+ */
+ public String getID()
+ {
+ return super.getAttributeAsString(ID_ATTR_NAME);
+ }
+
+ /**
+ * Sets this candidate's Internet Protocol (IP) address; this can be either
+ * an IPv4 address or an IPv6 address.
+ *
+ * @param ip this candidate's IPv4 or IPv6 address.
+ */
+ public void setIP(String ip)
+ {
+ super.setAttribute(IP_ATTR_NAME, ip);
+ }
+
+ /**
+ * Returns this candidate's Internet Protocol (IP) address; this can be
+ * either an IPv4 address or an IPv6 address.
+ *
+ * @return this candidate's IPv4 or IPv6 address.
+ */
+ public String getIP()
+ {
+ return super.getAttributeAsString(IP_ATTR_NAME);
+ }
+
+ /**
+ * The network index indicating the interface that the candidate belongs to.
+ * The network ID is used for diagnostic purposes only in cases where the
+ * calling hardware has more than one Network Interface Card.
+ *
+ * @param network the network index indicating the interface that the
+ * candidate belongs to.
+ */
+ public void setNetwork(int network)
+ {
+ super.setAttribute(NETWORK_ATTR_NAME, network);
+ }
+
+ /**
+ * Returns the network index indicating the interface that the candidate
+ * belongs to. The network ID is used for diagnostic purposes only in cases
+ * where the calling hardware has more than one Network Interface Card.
+ *
+ * @return the network index indicating the interface that the candidate
+ * belongs to.
+ */
+ public int getNetwork()
+ {
+ return super.getAttributeAsInt(NETWORK_ATTR_NAME);
+ }
+
+ /**
+ * Sets this candidate's port number.
+ *
+ * @param port this candidate's port number.
+ */
+ public void setPort(int port)
+ {
+ super.setAttribute(PORT_ATTR_NAME, port);
+ }
+
+ /**
+ * Returns this candidate's port number.
+ *
+ * @return this candidate's port number.
+ */
+ public int getPort()
+ {
+ return super.getAttributeAsInt(PORT_ATTR_NAME);
+ }
+
+ /**
+ * This candidate's priority as defined in ICE's RFC 5245
+ *
+ * @param priority this candidate's priority
+ */
+ public void setPriority(long priority)
+ {
+ super.setAttribute(PRIORITY_ATTR_NAME, priority);
+ }
+
+ /**
+ * This candidate's priority as defined in ICE's RFC 5245
+ *
+ * @return this candidate's priority
+ */
+ public int getPriority()
+ {
+ return super.getAttributeAsInt(PRIORITY_ATTR_NAME);
+ }
+
+ /**
+ * Sets this candidate's transport protocol.
+ *
+ * @param protocol this candidate's transport protocol.
+ */
+ public void setProtocol(String protocol)
+ {
+ super.setAttribute(PROTOCOL_ATTR_NAME, protocol);
+ }
+
+ /**
+ * Sets this candidate's transport protocol.
+ *
+ * @return this candidate's transport protocol.
+ */
+ public String getProtocol()
+ {
+ return super.getAttributeAsString(PROTOCOL_ATTR_NAME);
+ }
+
+ /**
+ * Sets this candidate's related address as described by ICE's RFC 5245.
+ *
+ * @param relAddr this candidate's related address as described by ICE's
+ * RFC 5245.
+ */
+ public void setRelAddr(String relAddr)
+ {
+ super.setAttribute(REL_ADDR_ATTR_NAME, relAddr);
+ }
+
+ /**
+ * Returns this candidate's related address as described by ICE's RFC 5245.
+ *
+ * @return this candidate's related address as described by ICE's RFC 5245.
+ */
+ public String getRelAddr()
+ {
+ return super.getAttributeAsString(REL_ADDR_ATTR_NAME);
+ }
+
+ /**
+ * Sets this candidate's related port as described by ICE's RFC 5245.
+ *
+ * @param relPort this candidate's related port as described by ICE's
+ * RFC 5245.
+ */
+ public void setRelPort(int relPort)
+ {
+ super.setAttribute(REL_PORT_ATTR_NAME, relPort);
+ }
+
+ /**
+ * Returns this candidate's related port as described by ICE's RFC 5245.
+ *
+ * @return this candidate's related port as described by ICE's RFC 5245.
+ */
+ public int getRelPort()
+ {
+ return super.getAttributeAsInt(REL_PORT_ATTR_NAME);
+ }
+
+ /**
+ * Sets a Candidate Type as defined in ICE-CORE. The allowable values are
+ * "host" for host candidates, "prflx" for peer reflexive candidates,
+ * "relay" for relayed candidates, and "srflx" for server reflexive
+ * candidates. All allowable values are enumerated in the {@link
+ * CandidateType} enum.
+ *
+ * @param type this candidates' type as per ICE's RFC 5245.
+ */
+ public void setType(CandidateType type)
+ {
+ super.setAttribute(TYPE_ATTR_NAME, type);
+ }
+
+ /**
+ * Returns a Candidate Type as defined in ICE-CORE. The allowable values are
+ * "host" for host candidates, "prflx" for peer reflexive candidates,
+ * "relay" for relayed candidates, and "srflx" for server reflexive
+ * candidates. All allowable values are enumerated in the {@link
+ * CandidateType} enum.
+ *
+ * @return this candidates' type as per ICE's RFC 5245.
+ */
+ public CandidateType getType()
+ {
+ return CandidateType.valueOf(getAttributeAsString(TYPE_ATTR_NAME));
+ }
+
+ /**
+ * Compares this instance with another CandidatePacketExtension by
+ * preference of type: host < local < prflx < srflx < stun < relay.
+ *
+ * @return 0 if the type are equal. -1 if this instance type is preferred.
+ * Otherwise 1.
+ */
+ public int compareTo(CandidatePacketExtension candidatePacketExtension)
+ {
+ // If the types are different.
+ if(this.getType() != candidatePacketExtension.getType())
+ {
+ CandidateType[] types = {
+ CandidateType.host,
+ CandidateType.local,
+ CandidateType.prflx,
+ CandidateType.srflx,
+ CandidateType.stun,
+ CandidateType.relay
+ };
+ for(int i = 0; i < types.length; ++i)
+ {
+ // this object is preferred.
+ if(types[i] == this.getType())
+ {
+ return -1;
+ }
+ // the candidatePacketExtension is preferred.
+ else if(types[i] == candidatePacketExtension.getType())
+ {
+ return 1;
+ }
+ }
+ }
+ // If the types are equal.
+ return 0;
+ }
+
+ /**
+ * Gets the TCP type for this <tt>CandidatePacketExtension</tt>.
+ */
+ public CandidateTcpType getTcpType()
+ {
+ String tcpTypeString = getAttributeAsString(TCPTYPE_ATTR_NAME);
+ try
+ {
+ return CandidateTcpType.parse(tcpTypeString);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the TCP type for this <tt>CandidatePacketExtension</tt>.
+ * @param tcpType
+ */
+ public void setTcpType(CandidateTcpType tcpType)
+ {
+ setAttribute(TCPTYPE_ATTR_NAME, tcpType.toString());
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java
index 69b2e47..3ccbd72 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/CryptoPacketExtension.java
@@ -17,6 +17,8 @@
*/
package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle;
+import java.util.Objects;
+
import ch.imvs.sdes4j.srtp.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
@@ -332,4 +334,14 @@ public class CryptoPacketExtension
}
return false;
}
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(
+ getCryptoSuite(),
+ getKeyParams(),
+ getSessionParams(),
+ getTag());
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java
index 1112c8c..173701f 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/IceUdpTransportPacketExtension.java
@@ -191,6 +191,27 @@ public class IceUdpTransportPacketExtension
}
/**
+ * Removes given <tt>PacketExtension</tt> from the list of child packet
+ * extensions. <tt>CandidatePacketExtension</tt> are not taken into account
+ * in this method and {@link #removeCandidate(CandidatePacketExtension)}
+ * should be used instead.
+ *
+ * @param childExtension <tt>PacketExtension</tt> instance to be removed
+ * from child packet extensions list.
+ *
+ * @return <tt>true</tt> if given <tt>childExtension</tt> has been in the
+ * list and was removed or <tt>false</tt> otherwise.
+ */
+ public boolean removeChildExtension(PacketExtension childExtension)
+ {
+ List<? extends PacketExtension> childExtensions
+ = super.getChildExtensions();
+
+ return childExtensions != null
+ && childExtensions.remove(childExtension);
+ }
+
+ /**
* Returns the list of {@link CandidatePacketExtension}s currently
* registered with this transport.
*
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java
index 9183fa0..65b0849 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQ.java
@@ -21,6 +21,7 @@ import java.math.*;
import java.security.*;
import java.util.*;
+import net.java.sip.communicator.service.protocol.jabber.*;
import org.jivesoftware.smack.packet.*;
/**
@@ -62,7 +63,8 @@ public class JingleIQ extends IQ
* The name of the argument that contains the session id.
*/
public static final String SID_ATTR_NAME = "sid";
-
+
+
/**
* The <tt>JingleAction</tt> that describes the purpose of this
* <tt>jingle</tt> element.
@@ -104,8 +106,8 @@ public class JingleIQ extends IQ
* The list of "content" elements included in this IQ.
*/
private final List<ContentPacketExtension> contentList
- = new ArrayList<ContentPacketExtension>();
-
+ = new ArrayList<ContentPacketExtension>();
+
/**
* Returns the XML string of this Jingle IQ's "section" sub-element.
*
@@ -124,17 +126,18 @@ public class JingleIQ extends IQ
if( initiator != null)
bldr.append(" " + INITIATOR_ATTR_NAME
- + "='" + getInitiator() + "'");
+ + "='" + getInitiator() + "'");
if( responder != null)
bldr.append(" " + RESPONDER_ATTR_NAME
- + "='" + getResponder() + "'");
+ + "='" + getResponder() + "'");
bldr.append(" " + SID_ATTR_NAME
- + "='" + getSID() + "'");
-
- String extensionsXML = getExtensionsXML();
+ + "='" + getSID() + "'");
+ CharSequence extensionsXMLSeq = getExtensionsXML();
+ String extensionsXML = extensionsXMLSeq.toString();
+
if ((contentList.size() == 0)
&& (reason == null)
&& (sessionInfo == null)
@@ -348,7 +351,7 @@ public class JingleIQ extends IQ
* otherwise.
*/
public boolean containsContentChildOfType(
- Class<? extends PacketExtension> contentType)
+ Class<? extends PacketExtension> contentType)
{
if(getContentForType(contentType) != null)
return true;
@@ -369,14 +372,14 @@ public class JingleIQ extends IQ
* found.
*/
public ContentPacketExtension getContentForType(
- Class<? extends PacketExtension> contentType)
+ Class<? extends PacketExtension> contentType)
{
synchronized(contentList)
{
for(ContentPacketExtension content : contentList)
{
PacketExtension child
- = content.getFirstChildOfType(contentType);
+ = content.getFirstChildOfType(contentType);
if(child != null)
return content;
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java
index 11f909a..49934e5 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java
@@ -20,6 +20,7 @@ package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jitsimeet.*;
+import net.java.sip.communicator.service.protocol.jabber.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.*;
@@ -37,174 +38,187 @@ public class JingleIQProvider implements IQProvider
*/
public JingleIQProvider()
{
- ProviderManager providerManager = ProviderManager.getInstance();
+
+ AbstractSmackInteroperabilityLayer smackInteroperabilityLayer =
+ AbstractSmackInteroperabilityLayer.getInstance();
//<description/> provider
- providerManager.addExtensionProvider(
- RtpDescriptionPacketExtension.ELEMENT_NAME,
- RtpDescriptionPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <RtpDescriptionPacketExtension>(
- RtpDescriptionPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ RtpDescriptionPacketExtension.ELEMENT_NAME,
+ RtpDescriptionPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <RtpDescriptionPacketExtension>(
+ RtpDescriptionPacketExtension.class));
//<payload-type/> provider
- providerManager.addExtensionProvider(
- PayloadTypePacketExtension.ELEMENT_NAME,
- RtpDescriptionPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <PayloadTypePacketExtension>(
- PayloadTypePacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ PayloadTypePacketExtension.ELEMENT_NAME,
+ RtpDescriptionPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <PayloadTypePacketExtension>(
+ PayloadTypePacketExtension.class));
//<parameter/> provider
- providerManager.addExtensionProvider(
- ParameterPacketExtension.ELEMENT_NAME,
- RtpDescriptionPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <ParameterPacketExtension>(ParameterPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ ParameterPacketExtension.ELEMENT_NAME,
+ RtpDescriptionPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <ParameterPacketExtension>
+ (ParameterPacketExtension.class));
//<rtp-hdrext/> provider
- providerManager.addExtensionProvider(
- RTPHdrExtPacketExtension.ELEMENT_NAME,
- RTPHdrExtPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <RTPHdrExtPacketExtension>(RTPHdrExtPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ RTPHdrExtPacketExtension.ELEMENT_NAME,
+ RTPHdrExtPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <RTPHdrExtPacketExtension>
+ (RTPHdrExtPacketExtension.class));
// <sctpmap/> provider
- providerManager.addExtensionProvider(
- SctpMapExtension.ELEMENT_NAME,
- SctpMapExtension.NAMESPACE,
- new SctpMapExtensionProvider());
+ smackInteroperabilityLayer.addExtensionProvider(
+ SctpMapExtension.ELEMENT_NAME,
+ SctpMapExtension.NAMESPACE,
+ new SctpMapExtensionProvider());
//<encryption/> provider
- providerManager.addExtensionProvider(
- EncryptionPacketExtension.ELEMENT_NAME,
- RtpDescriptionPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <EncryptionPacketExtension>(EncryptionPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ EncryptionPacketExtension.ELEMENT_NAME,
+ RtpDescriptionPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <EncryptionPacketExtension>
+ (EncryptionPacketExtension.class));
//<zrtp-hash/> provider
- providerManager.addExtensionProvider(
- ZrtpHashPacketExtension.ELEMENT_NAME,
- ZrtpHashPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <ZrtpHashPacketExtension>(ZrtpHashPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ ZrtpHashPacketExtension.ELEMENT_NAME,
+ ZrtpHashPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <ZrtpHashPacketExtension>
+ (ZrtpHashPacketExtension.class));
//<crypto/> provider
- providerManager.addExtensionProvider(
- CryptoPacketExtension.ELEMENT_NAME,
- RtpDescriptionPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <CryptoPacketExtension>(CryptoPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ CryptoPacketExtension.ELEMENT_NAME,
+ RtpDescriptionPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <CryptoPacketExtension>
+ (CryptoPacketExtension.class));
// <bundle/> provider
- providerManager.addExtensionProvider(
- BundlePacketExtension.ELEMENT_NAME,
- BundlePacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <BundlePacketExtension>(BundlePacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ BundlePacketExtension.ELEMENT_NAME,
+ BundlePacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <BundlePacketExtension>
+ (BundlePacketExtension.class));
// <group/> provider
- providerManager.addExtensionProvider(
- GroupPacketExtension.ELEMENT_NAME,
- GroupPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider
- <GroupPacketExtension>(GroupPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ GroupPacketExtension.ELEMENT_NAME,
+ GroupPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <GroupPacketExtension>(GroupPacketExtension.class));
//ice-udp transport
- providerManager.addExtensionProvider(
- IceUdpTransportPacketExtension.ELEMENT_NAME,
- IceUdpTransportPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider<IceUdpTransportPacketExtension>(
- IceUdpTransportPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ IceUdpTransportPacketExtension.ELEMENT_NAME,
+ IceUdpTransportPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <IceUdpTransportPacketExtension>(
+ IceUdpTransportPacketExtension.class));
//<raw-udp/> provider
- providerManager.addExtensionProvider(
- RawUdpTransportPacketExtension.ELEMENT_NAME,
- RawUdpTransportPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider<RawUdpTransportPacketExtension>(
- RawUdpTransportPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ RawUdpTransportPacketExtension.ELEMENT_NAME,
+ RawUdpTransportPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <RawUdpTransportPacketExtension>(
+ RawUdpTransportPacketExtension.class));
//ice-udp <candidate/> provider
- providerManager.addExtensionProvider(
- CandidatePacketExtension.ELEMENT_NAME,
- IceUdpTransportPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider<CandidatePacketExtension>(
- CandidatePacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ CandidatePacketExtension.ELEMENT_NAME,
+ IceUdpTransportPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <CandidatePacketExtension>(
+ CandidatePacketExtension.class));
//raw-udp <candidate/> provider
- providerManager.addExtensionProvider(
- CandidatePacketExtension.ELEMENT_NAME,
- RawUdpTransportPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider<CandidatePacketExtension>(
- CandidatePacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ CandidatePacketExtension.ELEMENT_NAME,
+ RawUdpTransportPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <CandidatePacketExtension>(
+ CandidatePacketExtension.class));
//ice-udp <remote-candidate/> provider
- providerManager.addExtensionProvider(
- RemoteCandidatePacketExtension.ELEMENT_NAME,
- IceUdpTransportPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider<RemoteCandidatePacketExtension>(
- RemoteCandidatePacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ RemoteCandidatePacketExtension.ELEMENT_NAME,
+ IceUdpTransportPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider
+ <RemoteCandidatePacketExtension>(
+ RemoteCandidatePacketExtension.class));
//inputevt <inputevt/> provider
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
InputEvtPacketExtension.ELEMENT_NAME,
InputEvtPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<InputEvtPacketExtension>(
InputEvtPacketExtension.class));
//coin <conference-info/> provider
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
CoinPacketExtension.ELEMENT_NAME,
CoinPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<CoinPacketExtension>(
CoinPacketExtension.class));
// DTLS-SRTP
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
DtlsFingerprintPacketExtension.ELEMENT_NAME,
DtlsFingerprintPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider
- <DtlsFingerprintPacketExtension>(
+ <DtlsFingerprintPacketExtension>(
DtlsFingerprintPacketExtension.class));
/*
* XEP-0251: Jingle Session Transfer <transfer/> and <transferred>
* providers
*/
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
TransferPacketExtension.ELEMENT_NAME,
TransferPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<TransferPacketExtension>(
TransferPacketExtension.class));
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
TransferredPacketExtension.ELEMENT_NAME,
TransferredPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<TransferredPacketExtension>(
TransferredPacketExtension.class));
//conference description <callid/> provider
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
ConferenceDescriptionPacketExtension.CALLID_ELEM_NAME,
ConferenceDescriptionPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<CallIdPacketExtension>(
CallIdPacketExtension.class));
//rtcp-fb
- providerManager.addExtensionProvider(
- RtcpFbPacketExtension.ELEMENT_NAME,
- RtcpFbPacketExtension.NAMESPACE,
- new DefaultPacketExtensionProvider<RtcpFbPacketExtension>(
- RtcpFbPacketExtension.class));
+ smackInteroperabilityLayer.addExtensionProvider(
+ RtcpFbPacketExtension.ELEMENT_NAME,
+ RtcpFbPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider<RtcpFbPacketExtension>(
+ RtcpFbPacketExtension.class));
//rtcp-mux
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
RtcpmuxPacketExtension.ELEMENT_NAME,
IceUdpTransportPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<RtcpmuxPacketExtension>(
RtcpmuxPacketExtension.class));
//ssrcInfo
- providerManager.addExtensionProvider(
+ smackInteroperabilityLayer.addExtensionProvider(
SSRCInfoPacketExtension.ELEMENT_NAME,
SSRCInfoPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<SSRCInfoPacketExtension>(
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/ComponentVersionsExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/ComponentVersionsExtension.java
new file mode 100644
index 0000000..bcc3397
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/ComponentVersionsExtension.java
@@ -0,0 +1,135 @@
+/*
+ * 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.jabber.extensions.jitsimeet;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
+
+/**
+ * The packet extension is used by Jicofo to broadcast versions of all video
+ * conferencing system components. This packets extension is added to jicofo's
+ * MUC presence. It will contain {@link Component} children which carry each
+ * component's name and version.
+ *
+ * @author Pawel Domas
+ */
+public class ComponentVersionsExtension
+ extends AbstractPacketExtension
+{
+ /**
+ * The XML element name of {@link ComponentVersionsExtension}.
+ */
+ public static final String ELEMENT_NAME = "versions";
+
+ /**
+ * The name of XML sub-elements which carry the info about particular
+ * component's version.
+ */
+ public static final String COMPONENT_ELEMENT_NAME = "component";
+
+ /**
+ * Constant for {@link Component} name used to signal the version of
+ * conference focus.
+ */
+ public static final String COMPONENT_FOCUS = "focus";
+
+ /**
+ * Constant for {@link Component} name used to signal the version of
+ * XMPP server.
+ */
+ public static final String COMPONENT_XMPP_SERVER = "xmpp";
+
+ /**
+ * Constant for {@link Component} name used to signal the version of
+ * the videobridge.
+ */
+ public static final String COMPONENT_VIDEOBRIDGE = "videobridge";
+
+ /**
+ * The XML element namespace of {@link ComponentVersionsExtension}.
+ */
+ public static final String NAMESPACE = "http://jitsi.org/jitmeet";
+
+ /**
+ * Creates an {@link AbstractPacketExtension} instance for the specified
+ * <tt>namespace</tt> and <tt>elementName</tt>.
+ */
+ public ComponentVersionsExtension()
+ {
+ super(NAMESPACE, ELEMENT_NAME);
+ }
+
+ /**
+ * Adds component's version to this extension.
+ *
+ * @param componentName the name of the component for which
+ * child {@link Component} extension will be added.
+ * @param versionStr human readable string that describes component's
+ * version.
+ */
+ public void addComponentVersion(String componentName, String versionStr)
+ {
+ Component v = new Component();
+
+ v.setName(componentName);
+ v.setText(versionStr);
+
+ addChildExtension(v);
+ }
+
+ /**
+ * Component child element of {@link ComponentVersionsExtension}. The name
+ * of the component is carried in name attribute and the version string is
+ * the text value.
+ */
+ public class Component
+ extends AbstractPacketExtension
+ {
+ /**
+ * The name of that attribute that carries component's name.
+ */
+ private final String NAME_ATTR_NAME = "name";
+
+ /**
+ * Creates new instance of {@link Component} packet extension.
+ */
+ public Component()
+ {
+ super(NAMESPACE, COMPONENT_ELEMENT_NAME);
+ }
+
+ /**
+ * Returns the value of the name attribute.
+ * @return <tt>String</tt> which describes the name of video
+ * conferencing system component.
+ */
+ public String getName()
+ {
+ return getAttributeAsString(NAME_ATTR_NAME);
+ }
+
+ /**
+ * Sets new value for the component's name attribute.
+ * @param name a <tt>String</tt> which describes the name of video
+ * conferencing system component.
+ */
+ public void setName(String name)
+ {
+ setAttribute(NAME_ATTR_NAME, name);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java
index 239c708..7384c9e 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/SSRCInfoPacketExtension.java
@@ -1,8 +1,19 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
+ * 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.jabber.extensions.jitsimeet;
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/VideoMutedExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/VideoMutedExtension.java
new file mode 100644
index 0000000..cf76d89
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jitsimeet/VideoMutedExtension.java
@@ -0,0 +1,70 @@
+/*
+ * 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.jabber.extensions.jitsimeet;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
+
+/**
+ * Video muted extension that is included in users presence in Jitsi-meet
+ * conferences. It does carry the info about user's video muted status.
+ *
+ * @author Pawel Domas
+ */
+public class VideoMutedExtension
+ extends AbstractPacketExtension
+{
+ /**
+ * The namespace of this packet extension.
+ */
+ public static final String NAMESPACE = "http://jitsi.org/jitmeet/video";
+
+ /**
+ * XML element name of this packet extension.
+ */
+ public static final String ELEMENT_NAME = "videomuted";
+
+ /**
+ * Creates new instance of <tt>VideoMutedExtension</tt>.
+ */
+ public VideoMutedExtension()
+ {
+ super(NAMESPACE, ELEMENT_NAME);
+ }
+
+ /**
+ * Check whether or not user's video is in muted status.
+ * @return <tt>true</tt> if muted, <tt>false</tt> if unmuted or
+ * <tt>null</tt> if no valid info found in the extension body.
+ */
+ public Boolean isVideoMuted()
+ {
+ return Boolean.valueOf(getText());
+ }
+
+ /**
+ * Sets user's video muted status.
+ *
+ * @param videoMuted <tt>true</tt> or <tt>false</tt> which indicates video
+ * muted status of the user.
+ */
+ public void setVideoMuted(Boolean videoMuted)
+ {
+ setText(
+ String.valueOf(videoMuted));
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java b/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java
index 93cdead..8094f68 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/jinglesdp/JingleUtils.java
@@ -179,12 +179,17 @@ public class JingleUtils
else
paramsMap.put(paramName, param.getValue());
}
-
- // video-related attributes in payload-type element
+
for(String attr : payloadType.getAttributeNames())
{
+ //video-related attributes in payload-type element
if(attr.equals("width") || attr.equals("height"))
paramsMap.put(attr, payloadType.getAttributeAsString(attr));
+
+ //update ptime with the actual value from the payload
+ if (attr.equals(PayloadTypePacketExtension.PTIME_ATTR_NAME))
+ advancedMap.put(PayloadTypePacketExtension.PTIME_ATTR_NAME,
+ Integer.toString(payloadType.getPtime()));
}
//now create the format.
diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java b/src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java
index fa4fff1..e6cdd36 100644
--- a/src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java
+++ b/src/net/java/sip/communicator/impl/protocol/mock/MockContactGroup.java
@@ -506,6 +506,31 @@ public class MockContactGroup
return true;
}
+ @Override
+ public int hashCode()
+ {
+ List<Object> objects = new ArrayList<Object>();
+ objects.add(getGroupName());
+ objects.add(getUID());
+ objects.add(countContacts());
+ objects.add(countSubgroups());
+
+ //traverse child contacts
+ for (Contact c : contacts)
+ {
+ objects.add(c.getAddress());
+ }
+
+
+ //traverse subgroups
+ for (ContactGroup g : subGroups)
+ {
+ objects.add(g.getGroupName());
+ }
+
+ return Objects.hash(objects.toArray());
+ }
+
public void setPersistent(boolean isPersistent)
{
this.isPersistent = isPersistent;
diff --git a/src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java b/src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java
index bcff799..a7b6e5e 100644
--- a/src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java
+++ b/src/net/java/sip/communicator/impl/protocol/mock/MockProvider.java
@@ -294,6 +294,15 @@ public class MockProvider
}
/**
+ * Always true.
+ */
+ @Override
+ public boolean validateContactAddress(String contactId, List<String> result)
+ {
+ return true;
+ }
+
+ /**
* Mock implementation of the corresponding ProtocolProviderService method.
* We have no icon corresponding to this protocol provider.
*/
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java
index eba0da6..4fd1b6f 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/AddressResolverImpl.java
@@ -70,18 +70,8 @@ public class AddressResolverImpl
// if it is a textual IP address, do no try to resolve it
if(NetworkUtils.isValidIPAddress(hostAddress))
{
- byte[] addr = null;
-
- addr = NetworkUtils.strToIPv4(hostAddress);
-
- // not an IPv4, try IPv6
- if (addr == null)
- {
- addr = NetworkUtils.strToIPv6(hostAddress);
- }
-
InetSocketAddress hostSocketAddress = new InetSocketAddress(
- InetAddress.getByAddress(hostAddress, addr),
+ NetworkUtils.getInetAddress(hostAddress),
inputAddress.getPort());
return new HopImpl(hostSocketAddress.getHostName(),
inputAddress.getPort(),
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java
index c4193a5..b626dbb 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java
@@ -87,7 +87,7 @@ public class CallPeerMediaHandlerSipImpl
* Whether other party is able to change video quality settings.
* Normally its whether we have detected existence of imageattr in sdp.
*/
- boolean supportQualityControls;
+ private boolean supportQualityControls;
/**
* The current quality controls for this peer media handler if any.
@@ -98,7 +98,7 @@ public class CallPeerMediaHandlerSipImpl
* The lock we use to make sure that we won't be processing a second
* offer/answer exchange while a .
*/
- private Object offerAnswerLock = new Object();
+ private final Object offerAnswerLock = new Object();
/**
* Creates a new handler that will be managing media streams for
@@ -164,7 +164,7 @@ public class CallPeerMediaHandlerSipImpl
throws OperationFailedException
{
//Audio Media Description
- Vector<MediaDescription> mediaDescs = createMediaDescriptions();
+ List<MediaDescription> mediaDescs = createMediaDescriptions();
//wrap everything up in a session description
String userName
@@ -197,7 +197,7 @@ public class CallPeerMediaHandlerSipImpl
* for reasons like - problems with device interaction, allocating ports,
* etc.
*/
- private Vector<MediaDescription> createMediaDescriptions()
+ private List<MediaDescription> createMediaDescriptions()
throws OperationFailedException
{
//Audio Media Description
@@ -215,12 +215,16 @@ public class CallPeerMediaHandlerSipImpl
receiveQualityPreset = qualityControls.getRemoteSendMaxPreset();
}
- for (MediaType mediaType : MediaType.values())
+ for (MediaType mediaType : new MediaType[] { MediaType.AUDIO , MediaType.VIDEO})
{
MediaDevice dev = getDefaultDevice(mediaType);
if (!isDeviceActive(dev, sendQualityPreset, receiveQualityPreset))
+ {
+ logger.warn("No active device for " + mediaType.toString()
+ + " was found!");
continue;
+ }
MediaDirection direction
= dev.getDirection().and(getDirectionUserPreference(mediaType));
@@ -339,7 +343,7 @@ public class CallPeerMediaHandlerSipImpl
* it is known that the other endpoint supports this
* profile" and "[o]ther profiles MAY also be used."
*/
- updateMediaDescriptionForZrtp(mediaType, md, null);
+ updateMediaDescriptionForZrtp(mediaType, md);
if (SrtpControl.RTP_SAVP.equals(proto)
|| SrtpControl.RTP_SAVPF.equals(proto))
{
@@ -400,7 +404,7 @@ public class CallPeerMediaHandlerSipImpl
{
//create the media descriptions reflecting our current state.
- Vector<MediaDescription> newMediaDescs = createMediaDescriptions();
+ List<MediaDescription> newMediaDescs = createMediaDescriptions();
SessionDescription newOffer = SdpUtils.createSessionUpdateDescription(
sdescToUpdate, getTransportManager().getLastUsedLocalHost(),
@@ -510,7 +514,7 @@ public class CallPeerMediaHandlerSipImpl
throws OperationFailedException,
IllegalArgumentException
{
- Vector<MediaDescription> answerDescriptions
+ List<MediaDescription> answerDescriptions
= createMediaDescriptionsForAnswer(newOffer);
// wrap everything up in a session description
SessionDescription newAnswer
@@ -555,10 +559,11 @@ public class CallPeerMediaHandlerSipImpl
boolean rejectedAvpOfferDueToSavpMandatory = false;
AccountID accountID = getPeer().getProtocolProvider().getAccountID();
- int savpOption
- = accountID.getAccountPropertyBoolean(
- ProtocolProviderFactory.DEFAULT_ENCRYPTION,
- true)
+ boolean useDefaultEncryption =
+ accountID.getAccountPropertyBoolean(
+ ProtocolProviderFactory.DEFAULT_ENCRYPTION , true);
+
+ int savpOption = useDefaultEncryption
? accountID.getAccountPropertyInt(
ProtocolProviderFactory.SAVP_OPTION,
ProtocolProviderFactory.SAVP_OFF)
@@ -638,7 +643,7 @@ public class CallPeerMediaHandlerSipImpl
{
mutuallySupportedFormats = null;
}
- else if(mediaType.equals(MediaType.VIDEO)
+ else if(MediaType.VIDEO.equals(mediaType)
&& (qualityControls != null))
{
/*
@@ -705,18 +710,18 @@ public class CallPeerMediaHandlerSipImpl
= getTransportManager().getStreamConnector(mediaType);
// check for options from remote party and set them locally
- if(mediaType.equals(MediaType.VIDEO))
+ if(MediaType.VIDEO.equals(mediaType))
{
// update stream
MediaStream stream = getStream(MediaType.VIDEO);
- if(stream != null && dev != null)
+ if(stream != null)
{
List<MediaFormat> fmts = intersectFormats(
getLocallySupportedFormats(dev),
remoteFormats);
- if(fmts.size() > 0)
+ if(!fmts.isEmpty())
{
MediaFormat fmt = fmts.get(0);
@@ -809,7 +814,7 @@ public class CallPeerMediaHandlerSipImpl
{
if(remoteDescriptions.size() > 1)
{
- if(mediaType.equals(MediaType.AUDIO))
+ if(MediaType.AUDIO.equals(mediaType))
{
masterStream = true;
masterStreamSet = true;
@@ -1192,8 +1197,7 @@ public class CallPeerMediaHandlerSipImpl
*/
private boolean updateMediaDescriptionForZrtp(
MediaType mediaType,
- MediaDescription localMd,
- MediaDescription remoteMd)
+ MediaDescription localMd)
{
MediaAwareCallPeer<?, ?, ?> peer = getPeer();
AccountID accountID = peer.getProtocolProvider().getAccountID();
@@ -1499,7 +1503,7 @@ public class CallPeerMediaHandlerSipImpl
// check for options from remote party and set
// is quality controls supported
- if(mediaType.equals(MediaType.VIDEO))
+ if(MediaType.VIDEO.equals(mediaType))
{
supportQualityControls
= SdpUtils.containsAttribute(mediaDescription, "imageattr");
@@ -1544,7 +1548,7 @@ public class CallPeerMediaHandlerSipImpl
{
if(remoteDescriptions.size() > 1)
{
- if(mediaType.equals(MediaType.AUDIO))
+ if(MediaType.AUDIO.equals(mediaType))
{
masterStream = true;
masterStreamSet = true;
@@ -1882,7 +1886,7 @@ public class CallPeerMediaHandlerSipImpl
// ZRTP
else if(srtpControlType == SrtpControlType.ZRTP)
{
- if(updateMediaDescriptionForZrtp(mediaType, localMd, remoteMd))
+ if(updateMediaDescriptionForZrtp(mediaType, localMd))
{
// Stop once an encryption advertisement has been chosen.
return;
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
index fa0e08e..62711c7 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
@@ -61,15 +61,27 @@ public class CallSipImpl
* Name of extra INVITE header which specifies name of MUC room that is
* hosting the Jitsi Meet conference.
*/
- public static final String JITSI_MEET_ROOM_HEADER
- = "Jitsi-Conference-Room";
+ public String JITSI_MEET_ROOM_HEADER = "Jitsi-Conference-Room";
+
+ /**
+ * Property name of extra INVITE header which specifies name of MUC room
+ * that is hosting the Jitsi Meet conference.
+ */
+ private static final String JITSI_MEET_ROOM_HEADER_PROPERTY
+ = "JITSI_MEET_ROOM_HEADER_NAME";
+
+ /**
+ * Property name of extra INVITE header which specifies password required
+ * to enter MUC room that is hosting the Jitsi Meet conference.
+ */
+ public String JITSI_MEET_ROOM_PASS_HEADER = "Jitsi-Conference-Room-Pass";
/**
* Name of extra INVITE header which specifies password required to enter
* MUC room that is hosting the Jitsi Meet conference.
*/
- public static final String JITSI_MEET_ROOM_PASS_HEADER
- = "Jitsi-Conference-Room-Pass";
+ private static final String JITSI_MEET_ROOM_PASS_HEADER_PROPERTY
+ = "JITSI_MEET_ROOM_PASS_HEADER_NAME";
/**
* Custom header included in initial desktop sharing call creation.
@@ -78,16 +90,27 @@ public class CallSipImpl
public static final String DS_SHARING_HEADER = "X-Desktop-Share";
/**
- * When starting call we may have quality preferences we must use
- * for the call.
+ * Custom header name prefix that can be added to the call instance.
+ * Several headers can be specified in the form of:
+ * EXTRA_HEADER_NAME.1=...
+ * EXTRA_HEADER_NAME.2=...
+ * Index starting from 1.
*/
- private QualityPreset initialQualityPreferences;
+ public static final String EXTRA_HEADER_NAME = "EXTRA_HEADER_NAME";
/**
- * A reference to the <tt>SipMessageFactory</tt> instance that we should
- * use when creating requests.
+ * Custom header value prefix that can be added to the call instance.
+ * Several headers can be specified in the form of:
+ * EXTRA_HEADER_VALUE.1=...
+ * EXTRA_HEADER_VALUE.2=...
+ * Index starting from 1.
*/
- private final SipMessageFactory messageFactory;
+ public static final String EXTRA_HEADER_VALUE = "EXTRA_HEADER_VALUE";
+
+ /**
+ * Maximum number of retransmissions that will be sent.
+ */
+ private static final int MAX_RETRANSMISSIONS = 3;
/**
* The name of the property under which the user may specify the number of
@@ -95,19 +118,27 @@ public class CallSipImpl
* 180.
*/
private static final String RETRANSMITS_RINGING_INTERVAL
- = "net.java.sip.communicator.impl.protocol.sip"
- + ".RETRANSMITS_RINGING_INTERVAL";
+ = "net.java.sip.communicator.impl.protocol.sip"
+ + ".RETRANSMITS_RINGING_INTERVAL";
/**
- * The default amount of time (in milliseconds) for the initial interval for
- * retransmissions of response 180.
- */
+ * The default amount of time (in milliseconds) for the initial interval for
+ * retransmissions of response 180.
+ */
private static final int DEFAULT_RETRANSMITS_RINGING_INTERVAL = 500;
/**
- * Maximum number of retransmissions that will be sent.
+ * When starting call we may have quality preferences we must use
+ * for the call.
*/
- private static final int MAX_RETRANSMISSIONS = 3;
+ private QualityPreset initialQualityPreferences;
+
+ /**
+ * A reference to the <tt>SipMessageFactory</tt> instance that we should
+ * use when creating requests.
+ */
+ private final SipMessageFactory messageFactory;
+
/**
* The amount of time (in milliseconds) for the initial interval for
@@ -140,6 +171,13 @@ public class CallSipImpl
}
this.retransmitsRingingInterval = retransmitsRingingInterval;
+ AccountID account = parentOpSet.getProtocolProvider().getAccountID();
+ // Specify custom header names
+ JITSI_MEET_ROOM_HEADER = account.getAccountPropertyString(
+ JITSI_MEET_ROOM_HEADER_PROPERTY, JITSI_MEET_ROOM_HEADER);
+ JITSI_MEET_ROOM_PASS_HEADER = account.getAccountPropertyString(
+ JITSI_MEET_ROOM_PASS_HEADER_PROPERTY, JITSI_MEET_ROOM_PASS_HEADER);
+
//let's add ourselves to the calls repo. we are doing it ourselves just
//to make sure that no one ever forgets.
parentOpSet.getActiveCallsRepository().addCall(this);
@@ -387,24 +425,21 @@ public class CallSipImpl
logger.trace("Looking for peer with dialog: " + dialog
+ "among " + getCallPeerCount() + " calls");
}
- while (callPeers.hasNext())
+ for (CallPeerSipImpl callPeer : getCallPeerList())
{
- CallPeerSipImpl cp = callPeers.next();
-
- if (cp.getDialog() == dialog)
+ if (callPeer.getDialog() == dialog)
{
if (logger.isTraceEnabled())
- logger.trace("Returning cp=" + cp);
- return cp;
+ logger.trace("Returning cp=" + callPeer);
+ return callPeer;
}
else
{
if (logger.isTraceEnabled())
- logger.trace("Ignoring cp=" + cp + " because cp.dialog="
- + cp.getDialog() + " while dialog=" + dialog);
+ logger.trace("Ignoring cp=" + callPeer + " because cp.dialog="
+ + callPeer.getDialog() + " while dialog=" + dialog);
}
}
-
return null;
}
@@ -449,7 +484,7 @@ public class CallSipImpl
// Transport preference
String forceTransport = null;
javax.sip.address.URI calleeURI = calleeAddress.getURI();
- if(calleeURI.getScheme().toLowerCase().equals("sips"))
+ if("sips".equals(calleeURI.getScheme().toLowerCase()))
{
// MUST use TLS
forceTransport = "TLS";
@@ -553,7 +588,7 @@ public class CallSipImpl
String alternativeIMPPAddress = null;
if (infoHeader != null
&& infoHeader.getParameter("purpose") != null
- && infoHeader.getParameter("purpose").equals("impp"))
+ && "impp".equals(infoHeader.getParameter("purpose")))
{
alternativeIMPPAddress = infoHeader.getInfo().toString();
}
@@ -585,7 +620,7 @@ public class CallSipImpl
{
if (logger.isTraceEnabled())
logger.trace("will send ringing response: ");
- if(peer.getState().equals(CallPeerState.INCOMING_CALL))
+ if( CallPeerState.INCOMING_CALL.equals(peer.getState()) )
{
response = messageFactory.createResponse(Response.RINGING, invite);
@@ -680,10 +715,10 @@ public class CallSipImpl
*/
public void reInvite() throws OperationFailedException
{
- Iterator<CallPeerSipImpl> peers = getCallPeers();
-
- while (peers.hasNext())
- peers.next().sendReInvite();
+ for(CallPeerSipImpl peer : getCallPeerList())
+ {
+ peer.sendReInvite();
+ }
}
/**
@@ -706,6 +741,22 @@ public class CallSipImpl
protected void processExtraHeaders(javax.sip.message.Message message)
throws ParseException
{
+ // If there are custom headers added to the call instance, add those
+ // headers
+ int extraHeaderIx = 1;
+
+ Object name = getData(EXTRA_HEADER_NAME + "." + extraHeaderIx);
+ while(name != null)
+ {
+ Object value = getData(EXTRA_HEADER_VALUE + "." + extraHeaderIx);
+
+ Header header = getProtocolProvider().getHeaderFactory()
+ .createHeader((String) name, (String) value);
+ message.setHeader(header);
+
+ extraHeaderIx++;
+ name = getData(EXTRA_HEADER_NAME + "." + extraHeaderIx);
+ }
}
/**
@@ -758,8 +809,8 @@ public class CallSipImpl
{
try
{
- if(!peer.getState().equals(
- CallPeerState.INCOMING_CALL))
+ if( !CallPeerState.INCOMING_CALL.equals(
+ peer.getState()) )
{
timer.cancel();
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java
index f5782f1..3c2aeb5 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ContactGroupSipImpl.java
@@ -641,5 +641,31 @@ public class ContactGroupSipImpl
return true;
}
+
+ @Override
+ public int hashCode()
+ {
+ List<Object> objects = new ArrayList<Object>();
+ objects.add(getGroupName());
+ objects.add(getUID());
+ objects.add(countContacts());
+ objects.add(countSubgroups());
+ objects.add(getProtocolProvider());
+
+ //traverse child contacts
+ for (Contact c : contacts)
+ {
+ objects.add(c.getAddress());
+ }
+
+
+ //traverse subgroups
+ for (ContactGroup g : subGroups)
+ {
+ objects.add(g.getGroupName());
+ }
+
+ return Objects.hash(objects.toArray());
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java
index 0d0b5e8..d4662dd 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ContactSipImpl.java
@@ -172,7 +172,7 @@ public class ContactSipImpl
public String getAddress()
{
SipURI sipURI = (SipURI) sipAddress.getURI();
- return sipURI.getUser() + "@" + sipURI.getHost();
+ return sipURI.toString().substring(sipURI.getScheme().length() + 1);
}
/**
@@ -535,9 +535,7 @@ public class ContactSipImpl
if(obj instanceof String)
{
String sobj = (String)obj;
-
- if(sobj.startsWith("sip:"))
- sobj = sobj.substring(4);
+ sobj = stripScheme(stripAddress(sobj));
if(getAddress().equalsIgnoreCase(sobj))
return true;
@@ -556,6 +554,43 @@ public class ContactSipImpl
}
/**
+ * Get rid of any parameters, ports etc. within a sip contact
+ * @param address the address to strip
+ * @return [sip[s]:]user@host without any params or port numbers.
+ */
+ static String stripAddress(String address)
+ {
+ if (address != null && address.length() > 0)
+ {
+ int idx = address.indexOf(':', 5);
+ if (idx > -1)
+ address = address.substring(0, idx);
+ idx = address.indexOf(';');
+ if (idx > -1)
+ address = address.substring(0, idx);
+ }
+ return address;
+ }
+
+ /**
+ * @param from address to strip
+ * @return the address, stripped from either "sip:" or "sips:"
+ */
+ public static String stripScheme(String from)
+ {
+ if (from.startsWith("sip:"))
+ {
+ return from.substring(4);
+ }
+ else if (from.startsWith("sips:"))
+ {
+ return from.substring(5);
+ }
+
+ return from;
+ }
+
+ /**
* Returns the presence operation set that this contact belongs
* to.
*
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
index 5ee32c9..fc99643 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
@@ -434,7 +434,10 @@ public class OperationSetBasicTelephonySipImpl
CSeqHeader cseq = ((CSeqHeader) response.getHeader(CSeqHeader.NAME));
if (cseq == null)
+ {
logger.error("An incoming response did not contain a CSeq header");
+ return false;
+ }
String method = cseq.getMethod();
@@ -1163,11 +1166,7 @@ public class OperationSetBasicTelephonySipImpl
protocolProvider.getAccountID().getService()
, 399, reasonText);
}
- catch(InvalidArgumentException e)
- {
- logger.error("Cannot create warning header", e);
- }
- catch(ParseException e)
+ catch(InvalidArgumentException | ParseException e)
{
logger.error("Cannot create warning header", e);
}
@@ -1458,11 +1457,7 @@ public class OperationSetBasicTelephonySipImpl
{
serverTransaction.sendResponse(accepted);
}
- catch (InvalidArgumentException ex)
- {
- failure = ex;
- }
- catch (SipException ex)
+ catch (InvalidArgumentException | SipException ex)
{
failure = ex;
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java
index 77a7fae..4997fc1 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetJitsiMeetToolsSipImpl.java
@@ -142,6 +142,16 @@ public class OperationSetJitsiMeetToolsSipImpl
* {@inheritDoc}
*/
@Override
+ public void removePresenceExtension(ChatRoom chatRoom,
+ PacketExtension extension)
+ {
+ throw new RuntimeException("Not implemented for SIP");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void setPresenceStatus(ChatRoom chatRoom, String statusMessage)
{
throw new RuntimeException("Not implemented for SIP");
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java
index 28fce28..7972ee8 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetMessageWaitingSipImpl.java
@@ -209,6 +209,10 @@ public class OperationSetMessageWaitingSipImpl
try
{
subscribeAddress = getSubscribeAddress();
+ if (subscribeAddress == null)
+ {
+ return;
+ }
}
catch (ParseException e)
{
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java
index 2fce57e..ef0ec4c 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetPresenceSipImpl.java
@@ -2240,6 +2240,40 @@ public class OperationSetPresenceSipImpl
updateContactIcon((ContactSipImpl) contact, personStatusIcon);
}
+ // search for a <note> that can define a more precise
+ // status this is not recommended by RFC3863 but some im
+ // clients use this.
+ NodeList presNoteList = getPidfChilds(presence, NOTE_ELEMENT);
+ if (presNoteList.getLength() >= 1)
+ {
+ Node noteNode = presNoteList.item(presNoteList.getLength() - 1);
+ if (noteNode.getNodeType() == Node.ELEMENT_NODE)
+ {
+ String state = getTextContent((Element)noteNode);
+ if (state != null)
+ {
+ switch (state.toLowerCase())
+ {
+ case "ready":
+ case "available":
+ personStatus = sipStatusEnum
+ .getStatus(SipStatusEnum.ONLINE);
+ break;
+ case "ringing":
+ case "on the phone":
+ case "on hold":
+ personStatus = sipStatusEnum
+ .getStatus(SipStatusEnum.ON_THE_PHONE);
+ break;
+ case "unavailable":
+ personStatus = sipStatusEnum
+ .getStatus(SipStatusEnum.OFFLINE);
+ break;
+ }
+ }
+ }
+ }
+
// Vector containing the list of status to set for each contact in
// the presence document ordered by priority (highest first).
// <SipContact, Float (priority), SipStatusEnum>
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java
index 644c85a..a9aefbe 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetTelephonyBLFSipImpl.java
@@ -72,6 +72,10 @@ public class OperationSetTelephonyBLFSipImpl
* Account property suffix to set/provision monitored line group.
*/
public static final String BLF_LINE_GROUP_ACC_PROP_PREFIX = "Group";
+ /**
+ * Account property suffix to set/provision monitored line pickup template.
+ */
+ public static final String BLF_LINE_PICKUP_ACC_PROP_PREFIX = "Pickup";
/**
* The name of the event package supported by
@@ -195,7 +199,7 @@ public class OperationSetTelephonyBLFSipImpl
String[] lineValues = lines.get(ix);
if(lineValues == null)
{
- lineValues = new String[3];
+ lineValues = new String[4];
lines.put(ix, lineValues);
}
@@ -211,13 +215,17 @@ public class OperationSetTelephonyBLFSipImpl
{
lineValues[2] = entryValue;
}
+ else if(pName.contains(BLF_LINE_PICKUP_ACC_PROP_PREFIX))
+ {
+ lineValues[3] = entryValue;
+ }
}
for(Map.Entry<String, String[]> en : lines.entrySet())
{
String[] vals = en.getValue();
- this.lines.add(new Line(vals[0], vals[1], vals[2], this.provider));
+ this.lines.add(new Line(vals[0], vals[1], vals[2], vals[3], this.provider));
}
}
@@ -263,16 +271,25 @@ public class OperationSetTelephonyBLFSipImpl
if(details == null)
return;
- if(StringUtils.isNullOrEmpty(details.callID)
- || StringUtils.isNullOrEmpty(details.localTag)
- || StringUtils.isNullOrEmpty(details.remoteTag))
- return;
-
// replaces
Address targetAddress = null;
try
{
- targetAddress = provider.parseAddressString(line.getAddress());
+ String address = line.getAddress();
+ if(asteriskMode(details))
+ {
+ // broken mode for Asterisk, doesn't provide us with
+ // the proper call-id, etc. attributes.
+ // send an unspecified pickup-call if a template is set
+ if (StringUtils.isNullOrEmpty(line.getPickupTemplate(), true))
+ {
+ return;
+ }
+
+ address = line.getPickupTemplate().replace("\\1", address);
+ }
+
+ targetAddress = provider.parseAddressString(address);
}
catch (ParseException ex)
{
@@ -281,6 +298,16 @@ public class OperationSetTelephonyBLFSipImpl
OperationFailedException.ILLEGAL_ARGUMENT, ex, logger);
}
+ OperationSetBasicTelephonySipImpl telOpSet
+ = (OperationSetBasicTelephonySipImpl)provider
+ .getOperationSet(OperationSetBasicTelephony.class);
+
+ if (asteriskMode(details))
+ {
+ telOpSet.createOutgoingCall(targetAddress, null, null);
+ return;
+ }
+
Replaces replacesHeader = null;
SipURI sipURI = (SipURI) targetAddress.getURI();
@@ -315,12 +342,14 @@ public class OperationSetTelephonyBLFSipImpl
OperationFailedException.INTERNAL_ERROR, ex, logger);
}
- OperationSetBasicTelephonySipImpl telOpSet
- = (OperationSetBasicTelephonySipImpl)provider
- .getOperationSet(OperationSetBasicTelephony.class);
-
- CallSipImpl call
- = telOpSet.createOutgoingCall(targetAddress, null, null);
+ telOpSet.createOutgoingCall(targetAddress, null, null);
+ }
+
+ private boolean asteriskMode(LineDetails details)
+ {
+ return StringUtils.isNullOrEmpty(details.callID)
+ || StringUtils.isNullOrEmpty(details.localTag)
+ || StringUtils.isNullOrEmpty(details.remoteTag);
}
/**
@@ -549,6 +578,8 @@ public class OperationSetTelephonyBLFSipImpl
Node dialogNode = dialogList.item(i);
Element dialogElem = (Element)dialogNode;
+ details.id = dialogElem.getAttribute("id");
+ details.direction = dialogElem.getAttribute("direction");
details.callID = dialogElem.getAttribute("call-id");
details.localTag = dialogElem.getAttribute("local-tag");
details.remoteTag = dialogElem.getAttribute("remote-tag");
@@ -698,12 +729,25 @@ public class OperationSetTelephonyBLFSipImpl
*/
private class LineDetails
{
+
/**
* The current status of the line, the last event fired for it.
*/
int lastStatusEvent = BLFStatusEvent.STATUS_OFFLINE;
/**
+ * id of the dialog. Mandatory.
+ */
+ String id = null;
+
+ /**
+ * either initiator or recipient, and indicates whether the observed
+ * user was the initiator of the dialog, or the recipient of the INVITE
+ * that created it.
+ */
+ String direction;
+
+ /**
* call-id of the dialog if any, used for remote pickup.
*/
String callID = null;
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
index 0fbcf23..927c0a3 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
@@ -246,6 +246,76 @@ public class ProtocolProviderServiceSipImpl
}
/**
+ * Validates the contact identifier and returns an error message if
+ * applicable and a suggested correction
+ *
+ * @param contactId the contact identifier to validate
+ * @param result Must be supplied as an empty a list. Implementors add
+ * items:
+ * <ol>
+ * <li>is the error message if applicable
+ * <li>a suggested correction. Index 1 is optional and can only
+ * be present if there was a validation failure.
+ * </ol>
+ * @return true if the contact id is valid, false otherwise
+ */
+ @Override
+ public boolean validateContactAddress(String contactId, List<String> result)
+ {
+ if (result == null)
+ {
+ throw new IllegalArgumentException("result must be an empty list");
+ }
+
+ result.clear();
+ try
+ {
+ Address address = parseAddressString(contactId);
+ if (address.toString().equals(contactId))
+ {
+ return true;
+ }
+ else if (((SipUri) address.getURI()).getUser().equals(contactId))
+ {
+ return true;
+ }
+ else if (address.toString().equals(address.getURI().getScheme() + ":" + contactId))
+ {
+ return true;
+ }
+ else
+ {
+ result.add(SipActivator.getResources().getI18NString(
+ "impl.protocol.sip.INVALID_ADDRESS", new String[]
+ { contactId }));
+ result.add(((SipUri) address.getURI()).getUser());
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.error("Validating SIP address failed for " + contactId, ex);
+ result.add(SipActivator.getResources()
+ .getI18NString("impl.protocol.sip.INVALID_ADDRESS", new String[]
+ { contactId }));
+
+ String user = contactId;
+ String remainder = "";
+ int at = contactId.indexOf('@');
+ if (at > -1)
+ {
+ user = contactId.substring(0, at);
+ remainder = contactId.substring(at);
+ }
+
+ // replace invalid characters in user part with hex encoding
+ String banned = "([^a-z0-9-_.!~*'()&=+$,;?/])+";
+ result.add(user.replaceAll(banned, "") + remainder);
+ }
+
+ return false;
+ }
+
+ /**
* Indicates whether or not this provider must registered
* when placing outgoing calls.
*
@@ -610,50 +680,50 @@ public class ProtocolProviderServiceSipImpl
addSupportedOperationSet(
OperationSetPresence.class,
opSetPersPresence);
+ }
- // Only init messaging and typing if enabled.
- boolean isMessagingDisabled
- = SipActivator.getConfigurationService()
- .getBoolean(IS_MESSAGING_DISABLED, false);
+ // Only init messaging and typing if enabled.
+ boolean isMessagingDisabled
+ = SipActivator.getConfigurationService()
+ .getBoolean(IS_MESSAGING_DISABLED, false);
- if (!isMessagingDisabled)
- {
- // init instant messaging
- this.opSetBasicIM =
- new OperationSetBasicInstantMessagingSipImpl(this);
+ if (!isMessagingDisabled)
+ {
+ // init instant messaging
+ this.opSetBasicIM =
+ new OperationSetBasicInstantMessagingSipImpl(this);
- addSupportedOperationSet(
- OperationSetBasicInstantMessaging.class,
- opSetBasicIM);
+ addSupportedOperationSet(
+ OperationSetBasicInstantMessaging.class,
+ opSetBasicIM);
- // init typing notifications
- this.opSetTypingNotif
- = new OperationSetTypingNotificationsSipImpl(
- this, opSetBasicIM);
- addSupportedOperationSet(
- OperationSetTypingNotifications.class,
- opSetTypingNotif);
+ // init typing notifications
+ this.opSetTypingNotif
+ = new OperationSetTypingNotificationsSipImpl(
+ this, opSetBasicIM);
+ addSupportedOperationSet(
+ OperationSetTypingNotifications.class,
+ opSetTypingNotif);
- addSupportedOperationSet(
- OperationSetInstantMessageTransform.class,
- new OperationSetInstantMessageTransformImpl());
- }
+ addSupportedOperationSet(
+ OperationSetInstantMessageTransform.class,
+ new OperationSetInstantMessageTransformImpl());
+ }
- this.opSetSSAccountInfo =
- new OperationSetServerStoredAccountInfoSipImpl(this);
+ this.opSetSSAccountInfo =
+ new OperationSetServerStoredAccountInfoSipImpl(this);
- // Set the display name.
- opSetSSAccountInfo.setOurDisplayName(ourDisplayName);
+ // Set the display name.
+ opSetSSAccountInfo.setOurDisplayName(ourDisplayName);
- // init avatar
- addSupportedOperationSet(
- OperationSetServerStoredAccountInfo.class,
- opSetSSAccountInfo);
+ // init avatar
+ addSupportedOperationSet(
+ OperationSetServerStoredAccountInfo.class,
+ opSetSSAccountInfo);
- addSupportedOperationSet(
- OperationSetAvatar.class,
- new OperationSetAvatarSipImpl(this, opSetSSAccountInfo));
- }
+ addSupportedOperationSet(
+ OperationSetAvatar.class,
+ new OperationSetAvatarSipImpl(this, opSetSSAccountInfo));
// MWI is enabled by default
if(accountID.getAccountPropertyBoolean(
@@ -1519,16 +1589,16 @@ public class ProtocolProviderServiceSipImpl
Set<ProtocolProviderServiceSipImpl> instances
= new HashSet<ProtocolProviderServiceSipImpl>();
BundleContext context = SipActivator.getBundleContext();
- ServiceReference[] references = context.getServiceReferences(
- ProtocolProviderService.class.getName(),
- null
- );
- for(ServiceReference reference : references)
+ Collection<ServiceReference<ProtocolProviderService>> references =
+ context.getServiceReferences(ProtocolProviderService.class,
+ null);
+ for(ServiceReference<ProtocolProviderService> ref : references)
{
- Object service = context.getService(reference);
+ ProtocolProviderService service = context.getService(ref);
if(service instanceof ProtocolProviderServiceSipImpl)
instances.add((ProtocolProviderServiceSipImpl) service);
}
+
return instances;
}
catch(InvalidSyntaxException ex)
@@ -2390,21 +2460,38 @@ public class ProtocolProviderServiceSipImpl
//we don't know how to handle the "tel:" and "callto:" schemes ... or
// rather we handle them same as sip so replace:
if(uriStr.toLowerCase().startsWith("tel:"))
- uriStr = "sip:" + uriStr.substring("tel:".length());
+ uriStr = uriStr.substring("tel:".length());
else if(uriStr.toLowerCase().startsWith("callto:"))
- uriStr = "sip:" + uriStr.substring("callto:".length());
+ uriStr = uriStr.substring("callto:".length());
+ else if(uriStr.toLowerCase().startsWith("sips:"))
+ uriStr = uriStr.substring("sips:".length());
+ else if(uriStr.toLowerCase().startsWith("sip:"))
+ uriStr = uriStr.substring("sip:".length());
+
+ String user = uriStr;
+ String remainder = "";
+ int at = uriStr.indexOf('@');
+ if (at > -1)
+ {
+ user = uriStr.substring(0, at);
+ remainder = uriStr.substring(at);
+ }
+
+ //replace invalid characters in user part with hex encoding
+ String banned = "([^a-z0-9-_.!~*'()&=+$,;?/])+";
+ user = user.replaceAll(banned, "") + remainder;
//Handle default domain name (i.e. transform 1234 -> 1234@sip.com)
//assuming that if no domain name is specified then it should be the
//same as ours.
- if (uriStr.indexOf('@') == -1)
+ if (at == -1)
{
//if we have a registrar, then we could append its domain name as
//default
SipRegistrarConnection src = sipRegistrarConnection;
if(src != null && !src.isRegistrarless() )
{
- uriStr = uriStr + "@"
+ uriStr = user + "@"
+ ((SipURI)src.getAddressOfRecord().getURI()).getHost();
}
@@ -2684,6 +2771,11 @@ public class ProtocolProviderServiceSipImpl
*/
protected void notifyConnectionFailed()
{
+ if (sipRegistrarConnection.isRegistrarless())
+ {
+ return;
+ }
+
if(getRegistrationState().equals(RegistrationState.REGISTERED)
&& sipRegistrarConnection != null)
sipRegistrarConnection.setRegistrationState(
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java b/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java
index 5d60612..b6bf997 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ProxyRouter.java
@@ -19,6 +19,7 @@ package net.java.sip.communicator.impl.protocol.sip;
import gov.nist.javax.sip.stack.*;
+import java.net.*;
import java.util.*;
import javax.sip.*;
@@ -26,6 +27,7 @@ import javax.sip.address.*;
import javax.sip.header.*;
import javax.sip.message.*;
+import net.java.sip.communicator.impl.protocol.sip.net.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
@@ -137,7 +139,9 @@ public class ProxyRouter
ProtocolProviderServiceSipImpl sipProvider
= ((ProtocolProviderServiceSipImpl) service);
- String proxy = sipProvider.getConnection().getOutboundProxyString();
+ final ProxyConnection connection = sipProvider.getConnection();
+ final String proxy = connection.getOutboundProxyString();
+ logger.trace("Router for proxy: " + proxy);
boolean forceLooseRouting
= sipProvider.getAccountID()
@@ -146,13 +150,30 @@ public class ProxyRouter
// P2P case
if (proxy == null || forceLooseRouting )
+ {
+ logger.info("Returning default SIP router, P2P/loose routing");
return this.getDefaultRouter();
+ }
// outbound proxy case
Router router = routerCache.get(proxy);
if (router == null)
{
- router = new DefaultRouter(stack, proxy);
+ router = new DefaultRouter(stack, proxy)
+ {
+ @Override
+ public Hop getNextHop(Request request) throws SipException
+ {
+ logger.info("Outbound proxy mode, using proxy " +
+ proxy + " as hop instead of an address resolved" +
+ " by the SIP router");
+ InetSocketAddress sa = connection.getAddress();
+ return new HopImpl(
+ sa.getAddress().getHostAddress(),
+ sa.getPort(),
+ connection.getTransport());
+ }
+ };
routerCache.put(proxy, router);
}
return router;
@@ -164,6 +185,7 @@ public class ProxyRouter
logger.error("unable to identify the service which created this "
+ "out-of-dialog request");
+ logger.info("Returning default router");
return this.getDefaultRouter();
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java
index 99cb0f8..2a3ed00 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/SipRegistrarConnection.java
@@ -1288,8 +1288,11 @@ public class SipRegistrarConnection
if(registrarPort != ListeningPoint.PORT_5060)
registrarURI.setPort(registrarPort);
- if(!registrationTransport.equals(ListeningPoint.UDP))
+ if(!registrationTransport.equals(ListeningPoint.UDP)
+ && !registrationTransport.equals(ListeningPoint.TLS))
+ {
registrarURI.setTransportParam(registrationTransport);
+ }
}
return registrarURI;
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java b/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java
index 2e714b5..c92782e 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/SipStackSharing.java
@@ -1172,6 +1172,9 @@ public class SipStackSharing
String transport)
throws IOException
{
+ logger.info("Gettting source address for " +
+ localAddress + " -> " + dst + ":" + dstPort +
+ "(" + transport + ")");
if(ListeningPoint.TLS.equalsIgnoreCase(transport))
return (java.net.InetSocketAddress)(((SipStackImpl)this.stack)
.getLocalAddressForTlsDst(dst, dstPort, localAddress));
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java
index 885455a..ecec5c5 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/UriHandlerSipImpl.java
@@ -351,7 +351,11 @@ public class UriHandlerSipImpl
// Even if not registered after the timeout, try the call
// anyway and the error popup will appear to ask the
// user if they want to register
- handleUri(uri, provider);
+ if(provider.getRegistrationState()
+ != RegistrationState.REGISTERED)
+ {
+ handleUri(uri, provider);
+ }
}
}, initialRegistrationTimeout);
}
@@ -401,9 +405,34 @@ public class UriHandlerSipImpl
OperationSetBasicTelephony<?> telephonyOpSet
= provider.getOperationSet(OperationSetBasicTelephony.class);
+ OperationSetVideoTelephony videoTelephonyOpSet
+ = provider.getOperationSet(OperationSetVideoTelephony.class);
+
+ boolean videoCall = false;
+ if(videoTelephonyOpSet != null
+ && uri.contains("?"))
+ {
+ String params = uri.substring(uri.indexOf('?') + 1);
+ uri = uri.substring(0, uri.indexOf('?'));
+
+ StringTokenizer paramTokens = new StringTokenizer(params, "&");
+ while(paramTokens.hasMoreTokens())
+ {
+ String tok = paramTokens.nextToken();
+ String[] keyValue = tok.split("\\=");
+ if (keyValue.length == 2
+ && keyValue[0].equalsIgnoreCase("video")
+ && keyValue[1].equalsIgnoreCase("true"))
+ videoCall = true;
+ }
+ }
+
try
{
- telephonyOpSet.createCall(uri);
+ if(videoCall)
+ videoTelephonyOpSet.createVideoCall(uri);
+ else
+ telephonyOpSet.createCall(uri);
}
catch (OperationFailedException exc)
{
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java
index 5439d99..ca7faa9 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ManualProxyConnection.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,115 +15,115 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.sip.net;
-
-import static javax.sip.ListeningPoint.PORT_5060;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PREFERRED_TRANSPORT;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_ADDRESS;
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_PORT;
-
-import java.net.*;
-import java.text.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.dns.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Implementation of the manually configured SIP proxy connection. IP Address
- * lookups are performed using the account's proxy address.
- *
- * @author Ingo Bauersachs
- */
-public class ManualProxyConnection
- extends ProxyConnection
-{
- private final static Logger logger
- = Logger.getLogger(ManualProxyConnection.class);
-
- private String address;
- private int port;
-
- private InetSocketAddress[] lookups;
- private int lookupIndex;
-
- /**
- * Creates a new instance of this class. Uses the server from the account.
- *
- * @param account the account of this SIP protocol instance
- */
- public ManualProxyConnection(SipAccountIDImpl account)
- {
- super(account);
- reset();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#
- * getNextAddress()
- */
- @Override
- public boolean getNextAddressFromDns()
- throws DnssecException
- {
- if(lookups == null)
- {
- try
- {
- lookupIndex = 0;
- lookups = NetworkUtils.getAandAAAARecords(address, port);
-
- //no result found, reset state and indicate "out of addresses"
- if(lookups.length == 0)
- {
- lookups = null;
- return false;
- }
- }
- catch (ParseException e)
- {
- logger.error("Invalid address <" + address + ">", e);
- return false;
- }
- }
-
- //check if the available addresses are exhausted
- if(lookupIndex >= lookups.length)
- {
- if(logger.isDebugEnabled())
- logger.debug("No more addresses for " + account);
- lookups = null;
- return false;
- }
-
- //assign the next address and return lookup success
- if(logger.isDebugEnabled())
- logger.debug("Returning <" + socketAddress
- + "> as next address for " + account);
- socketAddress = lookups[lookupIndex];
- lookupIndex++;
- return true;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#reset()
- */
- @Override
- public void reset()
- {
- super.reset();
- address = account.getAccountPropertyString(PROXY_ADDRESS);
- port = account.getAccountPropertyInt(PROXY_PORT, PORT_5060);
- transport = account.getAccountPropertyString(PREFERRED_TRANSPORT);
-
- //check property sanity
- if(!ProtocolProviderServiceSipImpl.isValidTransport(transport))
- throw new IllegalArgumentException(
- transport + " is not a valid SIP transport");
- }
-}
+package net.java.sip.communicator.impl.protocol.sip.net;
+
+import static javax.sip.ListeningPoint.PORT_5060;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PREFERRED_TRANSPORT;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_ADDRESS;
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_PORT;
+
+import java.net.*;
+import java.text.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import net.java.sip.communicator.service.dns.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implementation of the manually configured SIP proxy connection. IP Address
+ * lookups are performed using the account's proxy address.
+ *
+ * @author Ingo Bauersachs
+ */
+public class ManualProxyConnection
+ extends ProxyConnection
+{
+ private final static Logger logger
+ = Logger.getLogger(ManualProxyConnection.class);
+
+ private String address;
+ private int port;
+
+ private InetSocketAddress[] lookups;
+ private int lookupIndex;
+
+ /**
+ * Creates a new instance of this class. Uses the server from the account.
+ *
+ * @param account the account of this SIP protocol instance
+ */
+ public ManualProxyConnection(SipAccountIDImpl account)
+ {
+ super(account);
+ reset();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#
+ * getNextAddress()
+ */
+ @Override
+ public boolean getNextAddressFromDns()
+ throws DnssecException
+ {
+ if(lookups == null)
+ {
+ try
+ {
+ lookupIndex = 0;
+ lookups = NetworkUtils.getAandAAAARecords(address, port);
+
+ //no result found, reset state and indicate "out of addresses"
+ if(lookups.length == 0)
+ {
+ lookups = null;
+ return false;
+ }
+ }
+ catch (ParseException e)
+ {
+ logger.error("Invalid address <" + address + ">", e);
+ return false;
+ }
+ }
+
+ //check if the available addresses are exhausted
+ if(lookupIndex >= lookups.length)
+ {
+ if(logger.isDebugEnabled())
+ logger.debug("No more addresses for " + account);
+ lookups = null;
+ return false;
+ }
+
+ //assign the next address and return lookup success
+ if(logger.isDebugEnabled())
+ logger.debug("Returning <" + socketAddress
+ + "> as next address for " + account);
+ socketAddress = lookups[lookupIndex];
+ lookupIndex++;
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * net.java.sip.communicator.impl.protocol.sip.net.ProxyConnection#reset()
+ */
+ @Override
+ public void reset()
+ {
+ super.reset();
+ address = account.getAccountPropertyString(PROXY_ADDRESS);
+ port = account.getAccountPropertyInt(PROXY_PORT, PORT_5060);
+ transport = account.getAccountPropertyString(PREFERRED_TRANSPORT);
+
+ //check property sanity
+ if(!ProtocolProviderServiceSipImpl.isValidTransport(transport))
+ throw new IllegalArgumentException(
+ transport + " is not a valid SIP transport");
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java
index 99fcfb2..f7079f4 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java
@@ -1,180 +1,180 @@
-/*
- * 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.sip.net;
-
-import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_AUTO_CONFIG;
-
-import java.net.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.dns.*;
-
-/**
- * Abstract class for the determining the address for the SIP proxy.
- *
- * @author Ingo Bauersachs
- */
-public abstract class ProxyConnection
-{
- private List<String> returnedAddresses = new LinkedList<String>();
-
- protected String transport;
- protected InetSocketAddress socketAddress;
- protected final SipAccountIDImpl account;
-
- /**
- * Creates a new instance of this class.
- * @param account the account of this SIP protocol instance
- */
- protected ProxyConnection(SipAccountIDImpl account)
- {
- this.account = account;
- }
-
- /**
- * Gets the address to use for the next connection attempt.
- * @return the address of the last lookup.
- */
- public final InetSocketAddress getAddress()
- {
- return socketAddress;
- }
-
- /**
- * Gets the transport to use for the next connection attempt.
- * @return the transport of the last lookup.
- */
- public final String getTransport()
- {
- return transport;
- }
-
- /**
- * In case we are using an outbound proxy this method returns
- * a suitable string for use with Router.
- * The method returns <tt>null</tt> otherwise.
- *
- * @return the string of our outbound proxy if we are using one and
- * <tt>null</tt> otherwise.
- */
- public final String getOutboundProxyString()
- {
- if(socketAddress == null)
- return null;
-
- InetAddress proxyAddress = socketAddress.getAddress();
- StringBuilder proxyStringBuffer
- = new StringBuilder(proxyAddress.getHostAddress());
-
- if(proxyAddress instanceof Inet6Address)
- {
- proxyStringBuffer.insert(0, '[');
- proxyStringBuffer.append(']');
- }
-
- proxyStringBuffer.append(':');
- proxyStringBuffer.append(socketAddress.getPort());
- proxyStringBuffer.append('/');
- proxyStringBuffer.append(transport);
-
- return proxyStringBuffer.toString();
- }
-
- /**
- * Compares an InetAddress against the active outbound proxy. The comparison
- * is by reference, not equals.
- *
- * @param addressToTest The addres to test.
- * @return True when the InetAddress is the same as the outbound proxy.
- */
- public final boolean isSameInetAddress(InetAddress addressToTest)
- {
- // if the proxy is not yet initialized then this is not the provider
- // that caused this comparison
- if(socketAddress == null)
- return false;
- return addressToTest == socketAddress.getAddress();
- }
-
- /**
- * Retrieves the next address to use from DNS. Duplicate results are
- * suppressed.
- *
- * @return True if a new address is available through {@link #getAddress()},
- * false if the last address was reached. A new lookup from scratch
- * can be started by calling {@link #reset()}.
- * @throws DnssecException if there is a problem related to DNSSEC
- */
- public final boolean getNextAddress() throws DnssecException
- {
- boolean result;
- String key = null;
- do
- {
- result = getNextAddressFromDns();
- if(result && socketAddress != null)
- {
- key = getOutboundProxyString();
- if(!returnedAddresses.contains(key))
- {
- returnedAddresses.add(key);
- break;
- }
- }
- }
- while(result && returnedAddresses.contains(key));
- return result;
- }
-
- /**
- * Implementations must use this method to get the next address, but do not
- * have to care about duplicate addresses.
- *
- * @return True when a further address was available.
- * @throws DnssecException when a DNSSEC validation failure occured.
- */
- protected abstract boolean getNextAddressFromDns()
- throws DnssecException;
-
- /**
- * Resets the lookup to it's initial state. Overriders methods have to call
- * this method through a super-call.
- */
- public void reset()
- {
- returnedAddresses.clear();
- }
-
- /**
- * Factory method to create a proxy connection based on the account settings
- * of the protocol provider.
- *
- * @param pps the protocol provider that needs a SIP server connection.
- * @return An instance of a derived class.
- */
- public static ProxyConnection create(ProtocolProviderServiceSipImpl pps)
- {
- if (pps.getAccountID().getAccountPropertyBoolean(PROXY_AUTO_CONFIG,
- true))
- return new AutoProxyConnection((SipAccountIDImpl) pps.getAccountID(),
- pps.getDefaultTransport());
- else
- return new ManualProxyConnection((SipAccountIDImpl) pps.getAccountID());
- }
-}
+/*
+ * 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.sip.net;
+
+import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_AUTO_CONFIG;
+
+import java.net.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import net.java.sip.communicator.service.dns.*;
+
+/**
+ * Abstract class for the determining the address for the SIP proxy.
+ *
+ * @author Ingo Bauersachs
+ */
+public abstract class ProxyConnection
+{
+ private List<String> returnedAddresses = new LinkedList<String>();
+
+ protected String transport;
+ protected InetSocketAddress socketAddress;
+ protected final SipAccountIDImpl account;
+
+ /**
+ * Creates a new instance of this class.
+ * @param account the account of this SIP protocol instance
+ */
+ protected ProxyConnection(SipAccountIDImpl account)
+ {
+ this.account = account;
+ }
+
+ /**
+ * Gets the address to use for the next connection attempt.
+ * @return the address of the last lookup.
+ */
+ public final InetSocketAddress getAddress()
+ {
+ return socketAddress;
+ }
+
+ /**
+ * Gets the transport to use for the next connection attempt.
+ * @return the transport of the last lookup.
+ */
+ public final String getTransport()
+ {
+ return transport;
+ }
+
+ /**
+ * In case we are using an outbound proxy this method returns
+ * a suitable string for use with Router.
+ * The method returns <tt>null</tt> otherwise.
+ *
+ * @return the string of our outbound proxy if we are using one and
+ * <tt>null</tt> otherwise.
+ */
+ public final String getOutboundProxyString()
+ {
+ if(socketAddress == null)
+ return null;
+
+ InetAddress proxyAddress = socketAddress.getAddress();
+ StringBuilder proxyStringBuffer
+ = new StringBuilder(proxyAddress.getHostAddress());
+
+ if(proxyAddress instanceof Inet6Address)
+ {
+ proxyStringBuffer.insert(0, '[');
+ proxyStringBuffer.append(']');
+ }
+
+ proxyStringBuffer.append(':');
+ proxyStringBuffer.append(socketAddress.getPort());
+ proxyStringBuffer.append('/');
+ proxyStringBuffer.append(transport);
+
+ return proxyStringBuffer.toString();
+ }
+
+ /**
+ * Compares an InetAddress against the active outbound proxy. The comparison
+ * is by reference, not equals.
+ *
+ * @param addressToTest The addres to test.
+ * @return True when the InetAddress is the same as the outbound proxy.
+ */
+ public final boolean isSameInetAddress(InetAddress addressToTest)
+ {
+ // if the proxy is not yet initialized then this is not the provider
+ // that caused this comparison
+ if(socketAddress == null)
+ return false;
+ return addressToTest == socketAddress.getAddress();
+ }
+
+ /**
+ * Retrieves the next address to use from DNS. Duplicate results are
+ * suppressed.
+ *
+ * @return True if a new address is available through {@link #getAddress()},
+ * false if the last address was reached. A new lookup from scratch
+ * can be started by calling {@link #reset()}.
+ * @throws DnssecException if there is a problem related to DNSSEC
+ */
+ public final boolean getNextAddress() throws DnssecException
+ {
+ boolean result;
+ String key = null;
+ do
+ {
+ result = getNextAddressFromDns();
+ if(result && socketAddress != null)
+ {
+ key = getOutboundProxyString();
+ if(!returnedAddresses.contains(key))
+ {
+ returnedAddresses.add(key);
+ break;
+ }
+ }
+ }
+ while(result && returnedAddresses.contains(key));
+ return result;
+ }
+
+ /**
+ * Implementations must use this method to get the next address, but do not
+ * have to care about duplicate addresses.
+ *
+ * @return True when a further address was available.
+ * @throws DnssecException when a DNSSEC validation failure occured.
+ */
+ protected abstract boolean getNextAddressFromDns()
+ throws DnssecException;
+
+ /**
+ * Resets the lookup to it's initial state. Overriders methods have to call
+ * this method through a super-call.
+ */
+ public void reset()
+ {
+ returnedAddresses.clear();
+ }
+
+ /**
+ * Factory method to create a proxy connection based on the account settings
+ * of the protocol provider.
+ *
+ * @param pps the protocol provider that needs a SIP server connection.
+ * @return An instance of a derived class.
+ */
+ public static ProxyConnection create(ProtocolProviderServiceSipImpl pps)
+ {
+ if (pps.getAccountID().getAccountPropertyBoolean(PROXY_AUTO_CONFIG,
+ true))
+ return new AutoProxyConnection((SipAccountIDImpl) pps.getAccountID(),
+ pps.getDefaultTransport());
+ else
+ return new ManualProxyConnection((SipAccountIDImpl) pps.getAccountID());
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java
index 0027ec5..262b717 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,211 +15,211 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.protocol.sip.net;
-
-import java.security.cert.*;
-import java.text.*;
-import java.util.*;
-import java.util.regex.*;
-
-import javax.sip.address.*;
-
-import net.java.sip.communicator.impl.protocol.sip.*;
-import net.java.sip.communicator.service.certificate.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Matcher that extracts certificate identities according to <a
- * href="http://tools.ietf.org/html/rfc5922#section-7.1">RFC5922, Section
- * 7.1</a> and compares them with the rules from Section 7.2 and 7.3.
- * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false
- *
- * @author Ingo Bauersachs
- */
-public class RFC5922Matcher
- implements CertificateMatcher
-{
- /**
- * When set to true, enables strict validation of the hostname according to
- * <a href="http://tools.ietf.org/html/rfc5922#section-7.2">RFC5922 Section
- * 7.2</a>
- */
- public final static String PNAME_STRICT_RFC5922 =
- "net.java.sip.communicator.sip.tls.STRICT_RFC5922";
-
- private ProtocolProviderServiceSipImpl provider;
-
- /**
- * Creates a new instance of this class.
- * @param provider The SIP Provider to which this matcher belongs.
- */
- public RFC5922Matcher(ProtocolProviderServiceSipImpl provider)
- {
- this.provider = provider;
- }
-
- /** Our class logger. */
- private static final Logger logger = Logger
- .getLogger(CertificateMatcher.class);
-
- /*
- * (non-Javadoc)
- *
- * @see
- * net.java.sip.communicator.service.certificate.CertificateMatcher#verify
- * (java.lang.Iterable, java.security.cert.X509Certificate)
- */
- public void verify(Iterable<String> identitiesToTest, X509Certificate cert)
- throws CertificateException
- {
- boolean strict = SipActivator.getConfigurationService()
- .getBoolean(PNAME_STRICT_RFC5922, false);
-
- // if any of the identities is contained in the certificate we're good
- boolean oneMatched = false;
- Iterable<String> certIdentities = extractCertIdentities(cert);
- for (String identity : identitiesToTest)
- {
- // check if the intended hostname is contained in one of the
- // hostnames of the certificate according to
- // http://tools.ietf.org/html/rfc5922#section-7.2
- for(String dnsName : certIdentities)
- {
- try
- {
- if(NetworkUtils.compareDnsNames(dnsName, identity) == 0)
- {
- // one of the hostnames matched, we're good to go
- return;
- }
-
- if(!strict
- // is a wildcard name
- && dnsName.startsWith("*.")
- // contains at least two dots (*.example.com)
- && identity.indexOf(".") < identity.lastIndexOf(".")
- // compare *.example.com stripped to example.com with
- // - foo.example.com stripped to example.com
- // - foo.bar.example.com to bar.example.com
- && NetworkUtils.compareDnsNames(
- dnsName.substring(2),
- identity.substring(identity.indexOf(".")+1)) == 0)
- {
- // the wildcard matched, we're good to go
- return;
- }
- }
- catch (ParseException e)
- {} // we don't care - this hostname did not match
- }
- }
- if (!oneMatched)
- throw new CertificateException("None of <" + identitiesToTest
- + "> matched by the rules of RFC5922 to the cert with CN="
- + cert.getSubjectDN());
- }
-
- private Iterable<String> extractCertIdentities(X509Certificate cert)
- {
- List<String> certIdentities = new ArrayList<String>();
- Collection<List<?>> subjAltNames = null;
- try
- {
- subjAltNames = cert.getSubjectAlternativeNames();
- }
- catch (CertificateParsingException ex)
- {
- logger.error("Error parsing TLS certificate", ex);
- }
- // subjAltName types are defined in rfc2459
- final Integer dnsNameType = 2;
- final Integer uriNameType = 6;
- if (subjAltNames != null)
- {
- if (logger.isDebugEnabled())
- logger.debug("found subjAltNames: " + subjAltNames);
-
- // First look for a URI in the subjectAltName field
- for (List<?> altName : subjAltNames)
- {
- // 0th position is the alt name type
- // 1st position is the alt name data
- if (altName.get(0).equals(uriNameType))
- {
- SipURI altNameUri;
- try
- {
- altNameUri =
- provider.getAddressFactory().createSipURI(
- (String) altName.get(1));
- // only sip URIs are allowed
- if (!"sip".equals(altNameUri.getScheme()))
- continue;
- // user certificates are not allowed
- if (altNameUri.getUser() != null)
- continue;
- String altHostName = altNameUri.getHost();
- if (logger.isDebugEnabled())
- {
- logger.debug("found uri " + altName.get(1)
- + ", hostName " + altHostName);
- }
- certIdentities.add(altHostName);
- }
- catch (ParseException e)
- {
- logger.error("certificate contains invalid uri: "
- + altName.get(1));
- }
- }
-
- }
- // DNS An implementation MUST accept a domain name system
- // identifier as a SIP domain identity if and only if no other
- // identity is found that matches the "sip" URI type described
- // above.
- if (certIdentities.isEmpty())
- {
- for (List<?> altName : subjAltNames)
- {
- if (altName.get(0).equals(dnsNameType))
- {
- if (logger.isDebugEnabled())
- logger.debug("found dns " + altName.get(1));
- certIdentities.add(altName.get(1).toString());
- }
- }
- }
- }
- else
- {
- // If and only if the subjectAltName does not appear in the
- // certificate, the implementation MAY examine the CN field of the
- // certificate. If a valid DNS name is found there, the
- // implementation MAY accept this value as a SIP domain identity.
- String dname = cert.getSubjectDN().getName();
- String cname = "";
- try
- {
- Pattern EXTRACT_CN =
- Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*");
- Matcher matcher = EXTRACT_CN.matcher(dname);
- if (matcher.matches())
- {
- cname = matcher.group(1);
- if (logger.isDebugEnabled())
- {
- logger.debug("found CN: " + cname + " from DN: "
- + dname);
- }
- certIdentities.add(cname);
- }
- }
- catch (Exception ex)
- {
- logger.error("exception while extracting CN", ex);
- }
- }
- return certIdentities;
- }
-}
+package net.java.sip.communicator.impl.protocol.sip.net;
+
+import java.security.cert.*;
+import java.text.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.sip.address.*;
+
+import net.java.sip.communicator.impl.protocol.sip.*;
+import net.java.sip.communicator.service.certificate.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Matcher that extracts certificate identities according to <a
+ * href="http://tools.ietf.org/html/rfc5922#section-7.1">RFC5922, Section
+ * 7.1</a> and compares them with the rules from Section 7.2 and 7.3.
+ * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false
+ *
+ * @author Ingo Bauersachs
+ */
+public class RFC5922Matcher
+ implements CertificateMatcher
+{
+ /**
+ * When set to true, enables strict validation of the hostname according to
+ * <a href="http://tools.ietf.org/html/rfc5922#section-7.2">RFC5922 Section
+ * 7.2</a>
+ */
+ public final static String PNAME_STRICT_RFC5922 =
+ "net.java.sip.communicator.sip.tls.STRICT_RFC5922";
+
+ private ProtocolProviderServiceSipImpl provider;
+
+ /**
+ * Creates a new instance of this class.
+ * @param provider The SIP Provider to which this matcher belongs.
+ */
+ public RFC5922Matcher(ProtocolProviderServiceSipImpl provider)
+ {
+ this.provider = provider;
+ }
+
+ /** Our class logger. */
+ private static final Logger logger = Logger
+ .getLogger(CertificateMatcher.class);
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * net.java.sip.communicator.service.certificate.CertificateMatcher#verify
+ * (java.lang.Iterable, java.security.cert.X509Certificate)
+ */
+ public void verify(Iterable<String> identitiesToTest, X509Certificate cert)
+ throws CertificateException
+ {
+ boolean strict = SipActivator.getConfigurationService()
+ .getBoolean(PNAME_STRICT_RFC5922, false);
+
+ // if any of the identities is contained in the certificate we're good
+ boolean oneMatched = false;
+ Iterable<String> certIdentities = extractCertIdentities(cert);
+ for (String identity : identitiesToTest)
+ {
+ // check if the intended hostname is contained in one of the
+ // hostnames of the certificate according to
+ // http://tools.ietf.org/html/rfc5922#section-7.2
+ for(String dnsName : certIdentities)
+ {
+ try
+ {
+ if(NetworkUtils.compareDnsNames(dnsName, identity) == 0)
+ {
+ // one of the hostnames matched, we're good to go
+ return;
+ }
+
+ if(!strict
+ // is a wildcard name
+ && dnsName.startsWith("*.")
+ // contains at least two dots (*.example.com)
+ && identity.indexOf(".") < identity.lastIndexOf(".")
+ // compare *.example.com stripped to example.com with
+ // - foo.example.com stripped to example.com
+ // - foo.bar.example.com to bar.example.com
+ && NetworkUtils.compareDnsNames(
+ dnsName.substring(2),
+ identity.substring(identity.indexOf(".")+1)) == 0)
+ {
+ // the wildcard matched, we're good to go
+ return;
+ }
+ }
+ catch (ParseException e)
+ {} // we don't care - this hostname did not match
+ }
+ }
+ if (!oneMatched)
+ throw new CertificateException("None of <" + identitiesToTest
+ + "> matched by the rules of RFC5922 to the cert with CN="
+ + cert.getSubjectDN());
+ }
+
+ private Iterable<String> extractCertIdentities(X509Certificate cert)
+ {
+ List<String> certIdentities = new ArrayList<String>();
+ Collection<List<?>> subjAltNames = null;
+ try
+ {
+ subjAltNames = cert.getSubjectAlternativeNames();
+ }
+ catch (CertificateParsingException ex)
+ {
+ logger.error("Error parsing TLS certificate", ex);
+ }
+ // subjAltName types are defined in rfc2459
+ final Integer dnsNameType = 2;
+ final Integer uriNameType = 6;
+ if (subjAltNames != null)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("found subjAltNames: " + subjAltNames);
+
+ // First look for a URI in the subjectAltName field
+ for (List<?> altName : subjAltNames)
+ {
+ // 0th position is the alt name type
+ // 1st position is the alt name data
+ if (altName.get(0).equals(uriNameType))
+ {
+ SipURI altNameUri;
+ try
+ {
+ altNameUri =
+ provider.getAddressFactory().createSipURI(
+ (String) altName.get(1));
+ // only sip URIs are allowed
+ if (!"sip".equals(altNameUri.getScheme()))
+ continue;
+ // user certificates are not allowed
+ if (altNameUri.getUser() != null)
+ continue;
+ String altHostName = altNameUri.getHost();
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("found uri " + altName.get(1)
+ + ", hostName " + altHostName);
+ }
+ certIdentities.add(altHostName);
+ }
+ catch (ParseException e)
+ {
+ logger.error("certificate contains invalid uri: "
+ + altName.get(1));
+ }
+ }
+
+ }
+ // DNS An implementation MUST accept a domain name system
+ // identifier as a SIP domain identity if and only if no other
+ // identity is found that matches the "sip" URI type described
+ // above.
+ if (certIdentities.isEmpty())
+ {
+ for (List<?> altName : subjAltNames)
+ {
+ if (altName.get(0).equals(dnsNameType))
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("found dns " + altName.get(1));
+ certIdentities.add(altName.get(1).toString());
+ }
+ }
+ }
+ }
+ else
+ {
+ // If and only if the subjectAltName does not appear in the
+ // certificate, the implementation MAY examine the CN field of the
+ // certificate. If a valid DNS name is found there, the
+ // implementation MAY accept this value as a SIP domain identity.
+ String dname = cert.getSubjectDN().getName();
+ String cname = "";
+ try
+ {
+ Pattern EXTRACT_CN =
+ Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*");
+ Matcher matcher = EXTRACT_CN.matcher(dname);
+ if (matcher.matches())
+ {
+ cname = matcher.group(1);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("found CN: " + cname + " from DN: "
+ + dname);
+ }
+ certIdentities.add(cname);
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.error("exception while extracting CN", ex);
+ }
+ }
+ return certIdentities;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java b/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
index f1c5856..9d088a2 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/net/SslNetworkLayer.java
@@ -18,6 +18,7 @@
package net.java.sip.communicator.impl.protocol.sip.net;
import gov.nist.core.net.*;
+import gov.nist.javax.sip.*;
import java.io.*;
import java.net.*;
@@ -421,4 +422,9 @@ public class SslNetworkLayer
return 0;
}
+
+ @Override
+ public void setSipStack(SipStackImpl sipStack)
+ {
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java
index c51a96c..e30b12d 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java
@@ -36,6 +36,7 @@ import org.jitsi.service.neomedia.*;
import org.jitsi.service.neomedia.MediaType;
import org.jitsi.service.neomedia.format.*;
import org.jitsi.util.*;
+import org.opentelecoms.javax.sdp.*;
/**
* The class contains a number of utility methods that are meant to facilitate
@@ -64,7 +65,7 @@ public class SdpUtils
/**
* A reference to the currently valid SDP factory instance.
*/
- private static final SdpFactory sdpFactory = SdpFactory.getInstance();
+ private static final SdpFactory sdpFactory = new NistSdpFactory();
/**
* The name of the SDP attribute that defines zrtp hello hash.
@@ -455,8 +456,21 @@ public class SdpUtils
while (iter.hasNext())
{
Map.Entry<String, String> ntry = iter.next();
- Attribute adv = sdpFactory.createAttribute(ntry.getKey(),
- payloadType + " " + ntry.getValue());
+ Attribute adv;
+ switch (ntry.getKey())
+ {
+ // RFC7587, Sect. 7 says there's no payload number for ptime
+ case "ptime":
+ case "maxptime":
+ adv = sdpFactory.createAttribute(ntry.getKey(),
+ ntry.getValue());
+ break;
+ default:
+ adv = sdpFactory.createAttribute(ntry.getKey(),
+ payloadType + " " + ntry.getValue());
+ break;
+ }
+
mediaAttributes.add(adv);
}
@@ -567,7 +581,7 @@ public class SdpUtils
public static SessionDescription createSessionDescription(
InetAddress localAddress,
String userName,
- Vector<MediaDescription> mediaDescriptions)
+ List<MediaDescription> mediaDescriptions)
throws OperationFailedException
{
SessionDescription sessDescr = null;
@@ -613,8 +627,9 @@ public class SdpUtils
sessDescr.setConnection(c);
if ( mediaDescriptions != null)
- sessDescr.setMediaDescriptions(mediaDescriptions);
-
+ {
+ sessDescr.setMediaDescriptions( new Vector<>(mediaDescriptions));
+ }
return sessDescr;
}
catch (SdpException exc)
@@ -655,7 +670,7 @@ public class SdpUtils
public static SessionDescription createSessionUpdateDescription(
SessionDescription descToUpdate,
InetAddress newConnectionAddress,
- Vector<MediaDescription> newMediaDescriptions)
+ List<MediaDescription> newMediaDescriptions)
throws OperationFailedException
{
SessionDescription update = createSessionDescription(
@@ -1711,7 +1726,7 @@ public class SdpUtils
* <tt>descs</tt> <tt>Vector</tt>.
*/
private static MediaDescription removeMediaDesc(
- Vector<MediaDescription> descs,
+ List<MediaDescription> descs,
MediaType type)
{
for (Iterator<MediaDescription> i = descs.iterator(); i.hasNext();)
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf
index c15c025..2015a17 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf
+++ b/src/net/java/sip/communicator/impl/protocol/sip/sip.provider.manifest.mf
@@ -5,9 +5,22 @@ Bundle-Vendor: jitsi.org
Bundle-Version: 0.0.1
Bundle-SymbolicName: net.java.sip.communicator.protocol.sip
Import-Package: ch.imvs.sdes4j.srtp,
+ gov.nist.core,
+ gov.nist.core.net,
+ gov.nist.javax.sip,
+ gov.nist.javax.sip.address,
+ gov.nist.javax.sip.header,
+ gov.nist.javax.sip.header.extensions,
+ gov.nist.javax.sip.message,
+ gov.nist.javax.sip.stack,
javax.net,
javax.net.ssl,
javax.security.auth.x500,
+ javax.sdp,
+ javax.sip,
+ javax.sip.address,
+ javax.sip.header,
+ javax.sip.message,
javax.xml.datatype,
javax.xml.namespace,
javax.xml.parsers,
@@ -76,6 +89,7 @@ Import-Package: ch.imvs.sdes4j.srtp,
org.jitsi.util.xml,
org.json.simple,
org.osgi.framework,
+ org.opentelecoms.javax.sdp,
org.w3c.dom,
org.xml.sax
Export-Package: net.java.sip.communicator.impl.protocol.sip,
@@ -88,6 +102,4 @@ Export-Package: net.java.sip.communicator.impl.protocol.sip,
net.java.sip.communicator.impl.protocol.sip.xcap.model.resourcelists,
net.java.sip.communicator.impl.protocol.sip.xcap.model.xcapcaps,
net.java.sip.communicator.impl.protocol.sip.xcap.model.xcaperror,
- net.java.sip.communicator.impl.protocol.sip.xcap.utils,
- javax.sdp,
- gov.nist.javax.sdp.fields
+ net.java.sip.communicator.impl.protocol.sip.xcap.utils
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ContactGroupSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ContactGroupSSHImpl.java
deleted file mode 100644
index 7de2e16..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ContactGroupSSHImpl.java
+++ /dev/null
@@ -1,580 +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.ssh;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * A simple, straightforward implementation of a ssh ContactGroup. Since
- * the SSH protocol is not a real one, 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 Shobhit Jindal
- */
-public class ContactGroupSSHImpl
- implements ContactGroup
-{
-
- /**
- * The name of this SSH 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 ContactGroupSSHImpl 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 = true;
-
- /**
- * The protocol provider that created us.
- */
- private ProtocolProviderServiceSSHImpl 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 ContactGroupSSHImpl with the specified name.
- *
- * @param groupName the name of the group.
- * @param parentProvider the protocol provider that created this group.
- */
- public ContactGroupSSHImpl(
- String groupName,
- ProtocolProviderServiceSSHImpl 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 ContactSSHImpl to add to this group.
- */
- public void addContact(ContactSSH 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 ContactGroupSSHImpl to add as a subgroup to this
- * group.
- */
- public void addSubgroup(ContactGroupSSHImpl subgroup)
- {
- this.subGroups.add(subgroup);
- subgroup.setParentGroup(this);
- }
-
- /**
- * Sets the group that is the new parent of this group
- * @param parent ContactGroupSSHImpl
- */
- void setParentGroup(ContactGroupSSHImpl 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 ContactGroupSSHImpl subgroup to remove.
- */
- public void removeSubGroup(ContactGroupSSHImpl subgroup)
- {
- this.subGroups.remove(subgroup);
- subgroup.setParentGroup(null);
- }
-
- /**
- * Returns the group that is parent of the specified sshGroup or null
- * if no parent was found.
- * @param sshGroup the group whose parent we're looking for.
- * @return the ContactGroupSSHImpl instance that sshGroup
- * belongs to or null if no parent was found.
- */
- public ContactGroupSSHImpl findGroupParent(
- ContactGroupSSHImpl sshGroup)
- {
- if ( subGroups.contains(sshGroup) )
- return this;
-
- Iterator<ContactGroup> subGroupsIter = subgroups();
- while (subGroupsIter.hasNext())
- {
- ContactGroupSSHImpl subgroup
- = (ContactGroupSSHImpl) subGroupsIter.next();
-
- ContactGroupSSHImpl parent
- = subgroup.findGroupParent(sshGroup);
-
- if(parent != null)
- return parent;
- }
- return null;
- }
-
- /**
- * Returns the group that is parent of the specified sshContact or
- * null if no parent was found.
- *
- * @param sshContact the contact whose parent we're looking for.
- * @return the ContactGroupSSHImpl instance that sshContact
- * belongs to or <tt>null</tt> if no parent was found.
- */
- public ContactGroupSSHImpl findContactParent(
- ContactSSHImpl sshContact)
- {
- if ( contacts.contains(sshContact) )
- return this;
-
- Iterator<ContactGroup> subGroupsIter = subgroups();
- while (subGroupsIter.hasNext())
- {
- ContactGroupSSHImpl subgroup
- = (ContactGroupSSHImpl) subGroupsIter.next();
-
- ContactGroupSSHImpl parent
- = subgroup.findContactParent(sshContact);
-
- 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())
- {
- ContactSSHImpl contact = (ContactSSHImpl) 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())
- {
- ContactGroupSSHImpl contactGroup
- = (ContactGroupSSHImpl) 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 ContactSSHImpl to remove from this group
- */
- public void removeContact(ContactSSHImpl 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 ContactSSHImpl
- */
- public ContactSSHImpl findContactByID(String id)
- {
- //first go through the contacts that are direct children.
- Iterator<Contact> contactsIter = contacts();
-
- while(contactsIter.hasNext())
- {
- ContactSSHImpl mContact = (ContactSSHImpl)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() )
- {
- ContactGroupSSHImpl mGroup = (ContactGroupSSHImpl)groupsIter.next();
-
- ContactSSHImpl 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())
- {
- ContactGroup group = subGroups.next();
- buff.append(group.toString());
- if (subGroups.hasNext())
- buff.append("\n");
- }
-
- buff.append("\nChildContacts="+countContacts()+":[");
-
- Iterator<Contact> contacts = contacts();
- while (contacts.hasNext())
- {
- Contact contact = 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 ContactGroupSSHImpl))
- return false;
-
- ContactGroupSSHImpl sshGroup
- = (ContactGroupSSHImpl)obj;
-
- if( ! sshGroup.getGroupName().equals(getGroupName())
- || ! sshGroup.getUID().equals(getUID())
- || sshGroup.countContacts() != countContacts()
- || sshGroup.countSubgroups() != countSubgroups())
- return false;
-
- //traverse child contacts
- Iterator<Contact> theirContacts = sshGroup.contacts();
-
- while(theirContacts.hasNext())
- {
- Contact theirContact = theirContacts.next();
- Contact ourContact = getContact(theirContact.getAddress());
-
- if(ourContact == null
- || !ourContact.equals(theirContact))
- return false;
- }
-
- //traverse subgroups
- Iterator<ContactGroup> theirSubgroups = sshGroup.subgroups();
-
- while(theirSubgroups.hasNext())
- {
- ContactGroup theirSubgroup = theirSubgroups.next();
- ContactGroup ourSubgroup = getGroup(theirSubgroup.getGroupName());
-
- if(ourSubgroup == null
- || !ourSubgroup.equals(theirSubgroup))
- return false;
- }
-
- return true;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSH.java b/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSH.java
deleted file mode 100644
index a13414b..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSH.java
+++ /dev/null
@@ -1,370 +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.ssh;
-
-import java.io.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-import com.jcraft.jsch.*;
-
-/**
- * This interface represents a Contact of SSH Type
- * As a SSH Session is specific to a contact, additional information needed
- * to maintain its state with the remote server is present here
- *
- * @author Shobhit Jindal
- */
-interface ContactSSH
- extends Contact
-{
- /**
- * An event type indicating that the message being received is a standard
- * conversation message sent by another contact.
- */
- public static final int CONVERSATION_MESSAGE_RECEIVED = 1;
-
- /**
- * An event type indicting that the message being received is a system
- * message being sent by the server or a system administrator.
- */
- public static final int SYSTEM_MESSAGE_RECEIVED = 2;
-
- //Following eight function declations to be moved to Contact
-
- /**
- * This method is only called when the contact is added to a new
- * <tt>ContactGroupSSHImpl</tt> by the
- * <tt>ContactGroupSSHImpl</tt> itself.
- *
- * @param newParentGroup the <tt>ContactGroupSSHImpl</tt> that is now
- * parent of this <tt>ContactSSHImpl</tt>
- */
- void setParentGroup (ContactGroupSSHImpl newParentGroup);
-
- /**
- * Sets <tt>sshPresenceStatus</tt> as the PresenceStatus that this
- * contact is currently in.
- * @param sshPresenceStatus the <tt>SSHPresenceStatus</tt>
- * currently valid for this contact.
- */
- public void setPresenceStatus (PresenceStatus sshPresenceStatus);
-
- /**
- * Returns the persistent presence operation set that this contact belongs
- * to.
- *
- * @return the <tt>OperationSetPersistentPresenceSSHImpl</tt> that
- * this contact belongs to.
- */
- public OperationSetPersistentPresence
- getParentPresenceOperationSet ();
-
- /**
- * Returns the BasicInstant Messaging operation set that this contact
- * belongs to.
- *
- * @return the <tt>OperationSetBasicInstantMessagingSSHImpl</tt> that
- * this contact belongs to.
- */
- public OperationSetBasicInstantMessaging
- getParentBasicInstantMessagingOperationSet ();
-
- /**
- * Returns the File Transfer operation set that this contact belongs
- * to.
- *
- * @return the <tt>OperationSetFileTransferSSHImpl</tt> that
- * this contact belongs to.
- */
- public OperationSetFileTransfer
- getFileTransferOperationSet ();
-
- /**
- * Return the type of message received from remote server
- *
- * @return messageType
- */
- public int getMessageType ();
-
- /**
- * Sets the type of message received from remote server
- *
- * @param messageType
- */
- public void setMessageType (int messageType);
-
- /**
- * Stores persistent data of the contact.
- *
- * @param persistentData of the contact
- */
- public void setPersistentData (String persistentData);
-
- /**
- * Makes the contact resolved or unresolved.
- *
- * @param resolved true to make the contact resolved; false to
- * make it unresolved
- */
- public void setResolved (boolean resolved);
-
- /**
- * 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);
-
- /**
- * Returns true if a command has been sent whos reply was not received yet
- * false otherwise
- *
- * @return commandSent
- */
- public boolean isCommandSent ();
-
- /**
- * Set the state of commandSent variable which determines whether a reply
- * to a command sent is awaited
- *
- * @param commandSent
- */
- public void setCommandSent (boolean commandSent);
-
- /**
- * Initializes the reader and writers associated with shell of this contact
- *
- * @param shellInputStream The InputStream of stack
- * @param shellOutputStream The OutputStream of stack
- */
- void initializeShellIO (InputStream shellInputStream,
- OutputStream shellOutputStream);
-
- /**
- * Closes the readers and writer associated with shell of this contact
- */
- void closeShellIO ();
-
- /**
- * Determines whether a connection to a remote server is already underway
- *
- * @return connectionInProgress
- */
- public boolean isConnectionInProgress ();
-
- /**
- * Sets the status of connection attempt to remote server
- *
- * @param connectionInProgress
- */
- public void setConnectionInProgress (boolean connectionInProgress);
-
-// /**
-// * Sets the PS1 prompt of the current shell of Contact
-// * This method is synchronized
-// *
-// * @param sshPrompt to be associated
-// */
-// public void setShellPrompt(String sshPrompt);
-//
-// /**
-// * Returns the PS1 prompt of the current shell of Contact
-// *
-// * @return sshPrompt
-// */
-// public String getShellPrompt();
-
-
- /**
- * Saves the details of contact in persistentData
- */
- public void savePersistentDetails ();
-
- /*
- * Returns the SSHContactInfo associated with this contact
- *
- * @return sshConfigurationForm
- */
- public SSHContactInfo getSSHConfigurationForm ();
-
- /**
- * Returns the JSch Stack identified associated with this contact
- *
- * @return jsch
- */
- JSch getJSch ();
-
- /**
- * Starts the timer and its task to periodically update the status of
- * remote machine
- */
- void startTimerTask ();
-
- /**
- * Stops the timer and its task to stop updating the status of
- * remote machine
- */
- void stopTimerTask ();
-
- /**
- * Sets the JSch Stack identified associated with this contact
- *
- * @param jsch to be associated
- */
- void setJSch (JSch jsch);
-
- /**
- * Returns the Username associated with this contact
- *
- * @return userName
- */
- String getUserName ();
-
- /**
- * Returns the Hostname associated with this contact
- *
- * @return hostName
- */
- String getHostName ();
-
- /**
- * Returns the Password associated with this contact
- *
- * @return password
- */
- String getPassword ();
-
- /**
- * Sets the Password associated with this contact
- *
- * @param password
- */
- void setPassword (String password);
-
- /**
- * Returns the SSH Session associated with this contact
- *
- * @return sshSession
- */
- Session getSSHSession ();
-
- /**
- * Sets the SSH Session associated with this contact
- *
- * @param sshSession the newly created SSH Session to be associated
- */
- void setSSHSession (Session sshSession);
-
- /**
- * Returns the SSH Shell Channel associated with this contact
- *
- * @return shellChannel
- */
- Channel getShellChannel ();
-
- /**
- * Sets the SSH Shell channel associated with this contact
- *
- * @param shellChannel to be associated with SSH Session of this contact
- */
- void setShellChannel (Channel shellChannel);
-
- /**
- * Sends a message a line to remote machine via the Shell Writer
- *
- * @param message to be sent
- * @throws IOException if message failed to be sent
- */
- public void sendLine (String message)
- throws IOException;
-
-// /**
-// * Reads a line from the remote machine via the Shell Reader
-// *
-// * @return message read
-// */
-// public String getLine()
-// throws IOException;
-
- /**
- * Returns the Input Stream associated with SSH Channel of this contact
- *
- * @return shellInputStream associated with SSH Channel of this contact
- */
- public InputStream getShellInputStream ();
-
-// /**
-// * Sets the Input Stream associated with SSH Channel of this contact
-// *
-// * @param shellInputStream to be associated with SSH Channel of this
-// * contact
-// */
-// public void setShellInputStream(InputStream shellInputStream);
-
- /**
- * Returns the Output Stream associated with SSH Channel of this contact
- *
- * @return shellOutputStream associated with SSH Channel of this contact
- */
- public OutputStream getShellOutputStream ();
-
-// /**
-// * Sets the Output Stream associated with SSH Channel of this contact
-// *
-// * @param shellOutputStream to be associated with SSH Channel of this
-// * contact
-// */
-// public void setShellOutputStream(OutputStream shellOutputStream);
-//
- /**
- * Returns the BufferedReader associated with SSH Channel of this contact
- *
- * @return shellReader associated with SSH Channel of this contact
- */
- public InputStreamReader getShellReader ();
-//
-// /**
-// * Sets the BufferedReader associated with SSH Channel of this contact
-// *
-// * @param shellReader to be associated with SSH Channel of this contact
-// */
-// public void setShellReader(BufferedReader shellReader);
-
- /**
- * Returns the PrintWriter associated with SSH Channel of this contact
- *
- * @return shellWriter associated with SSH Channel of this contact
- */
- public PrintWriter getShellWriter ();
-
-// /**
-// * Sets the PrintWriter associated with SSH Channel of this contact
-// *
-// * @param shellWriter to be associated with SSH Channel of this contact
-// */
-// public void setShellWriter(PrintWriter shellWriter);
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSHImpl.java
deleted file mode 100644
index 15da209..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ContactSSHImpl.java
+++ /dev/null
@@ -1,918 +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.ssh;
-
-import java.io.*;
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.Base64; // disambiguation
-import net.java.sip.communicator.util.Logger;
-
-import com.jcraft.jsch.*;
-// disambiguation
-
-/**
- * A Contact of SSH Type
- *
- * @author Shobhit Jindal
- */
-public class ContactSSHImpl
- extends AbstractContact
- implements ContactSSH
-{
- private static final Logger logger
- = Logger.getLogger(ContactSSHImpl.class);
-
- /**
- * This acts as a separator between details stored in persistent data
- */
- private final String separator =
- Resources.getString("impl.protocol.ssh.DETAILS_SEPARATOR");
-
- /**
- * The identifier for SSH Stack
- * Java Secure Channel JSch
- */
- private JSch jsch;
-
- /**
- * Interface for user to provide details about machine
- */
- private SSHContactInfo sshConfigurationForm;
-
- /**
- * A Timer Daemon to update the status of this contact
- */
- private Timer timer = new Timer(true);
-
- /**
- * A Daemon to retrieve and fire messages received from remote machine
- */
- private SSHReaderDaemon contactSSHReaderDaemon;
-
- /**
- * The id of the contact.
- */
- private String contactID = null;
-
- /**
- * The persistentData of the contact.
- */
- private String persistentData = null;
-
-// /**
-// * This stores the prompt string of shell
-// */
-// private String sshPrompt;
-
- /**
- * The provider that created us.
- */
- private ProtocolProviderServiceSSHImpl parentProvider = null;
-
- /**
- * The identifier of the type of message received from server
- */
- private int messageType;
-
-
-
- /**
- * The identifier for SSH Session with the remote server
- */
- private Session sshSession = null;
-
- /**
- * The identifier for a sshShellChannel with the remote server is of type
- * shell - for an interactive SSH Session with the remote machine
- *
- * Other types
- * sftp - to tranfer files from/to the remote machine
- * exec - X forwarding
- * direct-tcpip - stream forwarding
- */
- private Channel sshShellChannel = null;
-
- /**
- * The identifier for the Shell Input Stream associated with SSH Sesion
- */
- private InputStream shellInputStream = null;
-
- /**
- * The identifier for the Shell Output Stream associated with SSH Sesion
- */
- private OutputStream shellOutputStream = null;
-
- /**
- * Higher wrapper for shellInputStream
- */
- private InputStreamReader shellReader = null;
-
- /**
- * Higher wrapper for shellOutputStream
- */
- private PrintWriter shellWriter = null;
-
- /**
- * The group that belong to.
- */
- private ContactGroupSSHImpl parentGroup = null;
-
- /**
- * The presence status of the contact.
- */
- private PresenceStatus presenceStatus = SSHStatusEnum.NOT_AVAILABLE;
-
- /**
- * Determines whether this contact is persistent, i.e. member of the contact
- * list or whether it is here only temporarily.
- */
- 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;
-
- /**
- * Determines whether an connection attempt to remote server is already
- * underway
- */
- private boolean isConnectionInProgress = false;
-
- /**
- * Determines whether the message received from remote machine is as a
- * result of command sent to it
- */
- private boolean commandSent = false;
-
- /**
- * A lock to synchronize the access of commandSent boolean object
- * with the reader thread.
- */
- private final Object lock = new Object();
-
- /**
- * Creates an instance of a meta contact with the specified string used
- * as a name and identifier.
- *
- * @param id the identifier of this contact (also used as a name).
- * @param parentProvider the provider that created us.
- */
- public ContactSSHImpl(
- String id,
- ProtocolProviderServiceSSHImpl parentProvider)
- {
- this.contactID = id;
- this.parentProvider = parentProvider;
-
- this.sshConfigurationForm =
- new SSHContactInfo(this);
-
- this.savePersistentDetails();
- }
-
- /**
- * Initializes the reader and writers associated with shell of this contact
- *
- * @param shellInputStream The InputStream of stack
- * @param shellOutputStream The OutputStream of stack
- */
- public void initializeShellIO(
- InputStream shellInputStream,
- OutputStream shellOutputStream)
- {
- this.shellInputStream = shellInputStream;
- this.shellOutputStream = shellOutputStream;
- shellReader = new InputStreamReader(shellInputStream);
- shellWriter = new PrintWriter(shellOutputStream);
-
- contactSSHReaderDaemon = new SSHReaderDaemon(this);
- contactSSHReaderDaemon.setDaemon(true);
- contactSSHReaderDaemon.isActive(true);
- contactSSHReaderDaemon.start();
- }
-
- /**
- * Closes the readers and writer associated with shell of this contact
- */
- public void closeShellIO()
- {
- try
- {
- shellReader.close();
- shellInputStream.close();
- }
- catch(IOException ex)
- {}
-
- try
- {
- shellWriter.close();
- shellOutputStream.close();
- }
- catch(IOException ex)
- {}
-
- shellInputStream = null;
-
- shellReader = null;
-
- shellOutputStream = null;
-
- shellWriter = null;
-
- try
- {
- sshShellChannel.disconnect();
- }
- catch(Exception e)
- {}
-
- // Removing the reference to current channel
- // a new shell channel will be created for the next message
- sshShellChannel = null;
-
- // remove the reference of session if it were also disconnected
- // like in the case of exit command
- if(!sshSession.isConnected())
- {
- sshSession = null;
- jsch = null;
- }
-
- ((OperationSetPersistentPresenceSSHImpl)
- getParentPresenceOperationSet()).
- changeContactPresenceStatus(this, SSHStatusEnum.ONLINE);
- }
-
- /**
- * Sends a message a line to remote machine via the Shell Writer
- *
- * @param message to be sent
- */
- public void sendLine(String message)
- throws IOException
- {
-// logger.debug("SSH TO: " + this.contactID + ": " + message);
- shellWriter.println(message);
- shellWriter.flush();
- }
-
- /**
- * Reads a line from the remote machine via the Shell Reader
- *
- * @return message read
- */
-// public String getLine()
-// throws IOException
-// {
-// String line = shellReader.readLine();
-//// logger.debug("SSH FROM: " + this.contactID + ": " + line);
-//
-// // null is never returned normally, the reading attempt returs a
-// // string
-// // or blocks until one line is available
-// if(line == null)
-// {
-// sshShellChannel.disconnect();
-// sshShellChannel = null;
-// sshSession = null;
-// throw(new IOException("Unexpected Reply from remote Server"));
-// }
-// return line;
-// }
-
- /**
- * Starts the timer and its task to periodically update the status of
- * remote machine
- */
- public void startTimerTask()
- {
- timer.scheduleAtFixedRate(new ContactTimerSSHImpl(this),
- 2000, sshConfigurationForm.getUpdateInterval()*1000);
- }
-
- /**
- * Stops the timer and its task to stop updating the status of
- * remote machine
- */
- public void stopTimerTask()
- {
- timer.cancel();
- }
-
-
- /**
- * Saves the details of contact in persistentData seperated by
- * separator
- * Passowrd is saved unsecurely using Base64 encoding
- */
- public void savePersistentDetails()
- {
- persistentData =
- this.sshConfigurationForm.getHostName() +
- separator +
- this.sshConfigurationForm.getUserName() +
- separator +
- new String(Base64.encode(this.sshConfigurationForm.getPassword()
- .getBytes())) +
- separator + sshConfigurationForm.getPort() +
- separator +
- sshConfigurationForm.getTerminalType() +
- separator +
- sshConfigurationForm.getUpdateInterval();
- }
-
- /**
- * Stores persistent data in fields of the contact seperated by
- * separator.
- *
- * @param persistentData of the contact
- */
- public void setPersistentData(String persistentData)
- {
- try
- {
- this.persistentData = persistentData;
- int firstCommaIndex = this.persistentData.indexOf(separator);
- int secondCommaIndex = this.persistentData.indexOf(separator,
- firstCommaIndex +1);
- int thirdCommaIndex = this.persistentData.indexOf(separator,
- secondCommaIndex +1);
- int fourthCommaIndex = this.persistentData.indexOf(separator,
- thirdCommaIndex +1);
- int fifthCommaIndex = this.persistentData.indexOf(separator,
- fourthCommaIndex +1);
-
- if (logger.isDebugEnabled())
- logger.debug("Commas: " + firstCommaIndex + " " + secondCommaIndex + " "
- + thirdCommaIndex + " " +fourthCommaIndex + " "
- +fifthCommaIndex);
-
- this.sshConfigurationForm.setHostNameField(
- this.persistentData.substring(0,firstCommaIndex));
-
- this.sshConfigurationForm.setUserNameField(
- this.persistentData.substring(firstCommaIndex+1,
- secondCommaIndex));
-
- if( (thirdCommaIndex - secondCommaIndex) > 1)
- {
- if(this.persistentData.substring(secondCommaIndex+1).length()>0)
- this.sshConfigurationForm.setPasswordField(
- new String(Base64.decode(this.persistentData
- .substring(secondCommaIndex+1, thirdCommaIndex))));
- }
-
-
- this.sshConfigurationForm.setPort(
- this.persistentData.substring(thirdCommaIndex + 1,
- fourthCommaIndex));
-
- this.sshConfigurationForm.setTerminalType(
- this.persistentData.substring(fourthCommaIndex + 1,
- fifthCommaIndex));
-
- this.sshConfigurationForm.setUpdateInterval(
- Integer.parseInt(this.persistentData.substring(fifthCommaIndex+1)));
- }
- catch(Exception ex)
- {
- logger.error("Error setting persistent data!", ex);
- }
- }
-
- /**
- * Determines whether a connection to a remote server is already underway
- *
- * @return isConnectionInProgress
- */
- public boolean isConnectionInProgress()
- {
- return this.isConnectionInProgress;
- }
-
- /**
- * Sets the status of connection attempt to remote server
- * This method is synchronized
- *
- * @param isConnectionInProgress
- */
- public synchronized void setConnectionInProgress(
- boolean isConnectionInProgress)
- {
- this.isConnectionInProgress = isConnectionInProgress;
- }
-
- /**
- * Returns the SSHContactInfo associated with this contact
- *
- * @return sshConfigurationForm
- */
- public SSHContactInfo getSSHConfigurationForm()
- {
- return this.sshConfigurationForm;
- }
-
- /**
- * Returns the JSch Stack identified associated with this contact
- *
- * @return jsch
- */
- public JSch getJSch()
- {
- return this.jsch;
- }
-
- /**
- * Sets the JSch Stack identified associated with this contact
- *
- * @param jsch to be associated
- */
- public void setJSch(JSch jsch)
- {
- this.jsch = jsch;
- }
-
- /**
- * This method is only called when the contact is added to a new
- * <tt>ContactGroupSSHImpl</tt> by the
- * <tt>ContactGroupSSHImpl</tt> itself.
- *
- * @param newParentGroup the <tt>ContactGroupSSHImpl</tt> that is now
- * parent of this <tt>ContactSSHImpl</tt>
- */
- public void setParentGroup(ContactGroupSSHImpl newParentGroup)
- {
- this.parentGroup = newParentGroup;
- }
-
- /**
- * Returns the Hostname associated with this contact
- *
- * @return hostName
- */
- public String getHostName()
- {
- return sshConfigurationForm.getHostName();
- }
-
- /**
- * 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 contactID;
- }
-
- /**
- * 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 true if a command has been sent whos reply was not received yet
- * false otherwise
- *
- * @return commandSent
- */
- public boolean isCommandSent()
- {
- return this.commandSent;
- }
-
- /**
- * Set the state of commandSent variable which determines whether a reply
- * to a command sent is awaited
- */
- public void setCommandSent(boolean commandSent)
- {
- synchronized(lock)
- {
- this.commandSent = commandSent;
- }
- }
-
- /**
- * Return the type of message received from remote server
- *
- * @return messageType
- */
- public int getMessageType()
- {
- return this.messageType;
- }
-
- /**
- * Sets the type of message received from remote server
- *
- * @param messageType
- */
- public void setMessageType(int messageType)
- {
- this.messageType = messageType;
- }
-
- /**
- * Returns the status of the contact.
- *
- * @return presenceStatus
- */
- public PresenceStatus getPresenceStatus()
- {
- return this.presenceStatus;
- }
-
- /**
- * Sets <tt>sshPresenceStatus</tt> as the PresenceStatus that this
- * contact is currently in.
- * @param sshPresenceStatus the <tt>SSHPresenceStatus</tt>
- * currently valid for this contact.
- */
- public void setPresenceStatus(PresenceStatus sshPresenceStatus)
- {
- this.presenceStatus = sshPresenceStatus;
- }
-
- /**
- * 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
- */
- public boolean isLocal()
- {
- return true;
- }
-
- /**
- * Returns the group that contains this contact.
- * @return a reference to the <tt>ContactGroupSSHImpl</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("ContactSSHImpl[ 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 persistent data of the contact.
- *
- * @return persistentData of the contact
- */
- public String getPersistentData()
- {
- return persistentData;
- }
-
- /**
- * 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>OperationSetPersistentPresenceSSHImpl</tt> that
- * this contact belongs to.
- */
- public OperationSetPersistentPresence
- getParentPresenceOperationSet()
- {
- return
- parentProvider
- .getOperationSet(OperationSetPersistentPresence.class);
- }
-
- /**
- * Returns the BasicInstant Messaging operation set that this contact
- * belongs to.
- *
- * @return the <tt>OperationSetBasicInstantMessagingSSHImpl</tt> that
- * this contact belongs to.
- */
- public OperationSetBasicInstantMessaging
- getParentBasicInstantMessagingOperationSet()
- {
- return
- parentProvider
- .getOperationSet(OperationSetBasicInstantMessaging.class);
- }
-
- /**
- * Returns the File Transfer operation set that this contact belongs
- * to.
- *
- * @return the <tt>OperationSetFileTransferSSHImpl</tt> that
- * this contact belongs to.
- */
- public OperationSetFileTransfer
- getFileTransferOperationSet()
- {
- return parentProvider.getOperationSet(OperationSetFileTransfer.class);
- }
-
-
- /**
- * Returns the SSH Session associated with this contact
- *
- * @return sshSession
- */
- public Session getSSHSession()
- {
- return this.sshSession;
- }
-
- /**
- * Sets the SSH Session associated with this contact
- *
- * @param sshSession the newly created SSH Session to be associated
- */
- public void setSSHSession(Session sshSession)
- {
- this.sshSession = sshSession;
- }
-
- /**
- * Returns the SSH Shell Channel associated with this contact
- *
- * @return sshShellChannel
- */
- public Channel getShellChannel()
- {
- return this.sshShellChannel;
- }
-
- /**
- * Sets the SSH Shell channel associated with this contact
- *
- * @param sshShellChannel to be associated with SSH Session of this contact
- */
- public void setShellChannel(Channel sshShellChannel)
- {
- this.sshShellChannel = sshShellChannel;
- }
-
- /**
- * Returns the Input Stream associated with SSH Channel of this contact
- *
- * @return shellInputStream associated with SSH Channel of this contact
- */
- public InputStream getShellInputStream()
- {
- return this.shellInputStream;
- }
-
-// /**
-// * Sets the Input Stream associated with SSH Channel of this contact
-// *
-// * @param shellInputStream to be associated with SSH Channel of
-// * this contact
-// */
-// public void setShellInputStream(InputStream shellInputStream)
-// {
-// this.shellInputStream = shellInputStream;
-// }
-
- /**
- * Returns the Output Stream associated with SSH Channel of this contact
- *
- * @return shellOutputStream associated with SSH Channel of this contact
- */
- public OutputStream getShellOutputStream()
- {
- return this.shellOutputStream;
- }
-
-// /**
-// * Sets the Output Stream associated with SSH Channel of this contact
-// *
-// * @param shellOutputStream to be associated with SSH Channel of this contact
-// */
-// public void setShellOutputStream(OutputStream shellOutputStream)
-// {
-// this.shellOutputStream = shellOutputStream;
-// }
-
- /**
- * Returns the BufferedReader associated with SSH Channel of this contact
- *
- * @return shellReader associated with SSH Channel of this contact
- */
- public InputStreamReader getShellReader()
- {
- return this.shellReader;
- }
-
-// /**
-// * Sets the BufferedReader associated with SSH Channel of this contact
-// *
-// * @param shellReader to be associated with SSH Channel of this contact
-// */
-// public void setShellReader(BufferedReader shellReader)
-// {
-// this.shellReader = shellReader;
-// }
-
- /**
- * Returns the PrintWriter associated with SSH Channel of this contact
- *
- * @return shellWriter associated with SSH Channel of this contact
- */
- public PrintWriter getShellWriter()
- {
- return this.shellWriter;
- }
-
-// /**
-// * Sets the PrintWriter associated with SSH Channel of this contact
-// *
-// * @param shellWriter to be associated with SSH Channel of this contact
-// */
-// public void setShellWriter(PrintWriter shellWriter)
-// {
-// this.shellWriter = shellWriter;
-// }
-
- /**
- * Returns the userName associated with SSH Channel of this contact
- *
- * @return userName associated with SSH Channel of this contact
- */
- public String getUserName()
- {
- return sshConfigurationForm.getUserName();
- }
-
- /**
- * Returns the password associated with SSH Channel of this contact
- *
- * @return password associated with SSH Channel of this contact
- */
- public String getPassword()
- {
- return sshConfigurationForm.getPassword();
- }
-
- /**
- * Sets the Password associated with this contact
- *
- * @param password
- */
- public void setPassword(String password)
- {
- this.sshConfigurationForm.setPasswordField(password);
- savePersistentDetails();
- }
-
-// /**
-// * Sets the PS1 prompt of the current shell of Contact
-// *
-// * @param sshPrompt to be associated
-// */
-// public void setShellPrompt(String sshPrompt)
-// {
-// this.sshPrompt = sshPrompt;
-// }
-//
-// /**
-// * Returns the PS1 prompt of the current shell of Contact
-// *
-// * @return sshPrompt
-// */
-// public String getShellPrompt()
-// {
-// return this.sshPrompt;
-// }
-
- /**
- * Return the current status message of this contact.
- *
- * @return the current status message
- */
- public String getStatusMessage()
- {
- return presenceStatus.getStatusName();
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ContactTimerSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ContactTimerSSHImpl.java
deleted file mode 100644
index 4e3664d..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ContactTimerSSHImpl.java
+++ /dev/null
@@ -1,116 +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.ssh;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-import net.java.sip.communicator.util.*;
-
-/**
- * Timer Task to update the reachability status of SSH Contact in contact list.
- * (Reachability of remote machine from user's machine)
- * The timer is started at either of the two places
- * - A new contact - OperationSetPersistentPresenceSSHImpl
- * .createUnresolvedContact
- * - Existing Contact - OperationSetPersistentPresenceSSHImpl.subscribe
- *
- * @author Shobhit Jindal
- */
-public class ContactTimerSSHImpl
- extends TimerTask
-{
- private static final Logger logger
- = Logger.getLogger(OperationSetFileTransferSSHImpl.class);
-
- /**
- * The contact ID of the remote machine
- */
- private ContactSSH sshContact;
-
- /**
- * PersistentPresence Identifer assoiciated with SSH Contact
- */
- private OperationSetPersistentPresenceSSHImpl persistentPresence;
-
- /**
- * The method which is called at regular intervals to update the status
- * of remote machines
- *
- * Presently only ONLINE and OFFILINE status are checked
- */
- @Override
- public void run()
- {
- try
- {
- InetAddress remoteMachine = InetAddress.getByName(
- sshContact.getSSHConfigurationForm().getHostName());
-
- //check if machine is reachable
- if(remoteMachine.isReachable(
- sshContact.getSSHConfigurationForm().getUpdateInterval()))
- {
- if (sshContact.getPresenceStatus().equals(SSHStatusEnum.OFFLINE)
- || sshContact.getPresenceStatus().equals(SSHStatusEnum
- .NOT_AVAILABLE))
- {
- // change status to online
- persistentPresence.changeContactPresenceStatus(
- sshContact, SSHStatusEnum.ONLINE);
-
- if (logger.isDebugEnabled())
- logger.debug("SSH Host " + sshContact
- .getSSHConfigurationForm().getHostName() + ": Online");
- }
-
- }
- else throw new IOException();
-
- }
- catch (IOException ex)
- {
- if (sshContact.getPresenceStatus().equals(SSHStatusEnum.ONLINE)
- || sshContact.getPresenceStatus().equals(
- SSHStatusEnum.NOT_AVAILABLE))
- {
- persistentPresence.changeContactPresenceStatus(
- sshContact, SSHStatusEnum.OFFLINE);
-
- if (logger.isDebugEnabled())
- logger.debug("SSH Host " + sshContact.getSSHConfigurationForm()
- .getHostName() + ": Offline");
- }
- }
- }
- /**
- * Creates a new instance of ContactTimerSSHImpl
- *
- * @param sshContact the <tt>Contact</tt>
- */
- public ContactTimerSSHImpl(ContactSSH sshContact)
- {
- super();
- this.sshContact = sshContact;
- this.persistentPresence = (OperationSetPersistentPresenceSSHImpl)
- sshContact.getParentPresenceOperationSet();
- }
-
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/FileTransferSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/FileTransferSSHImpl.java
deleted file mode 100644
index 1e95032..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/FileTransferSSHImpl.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.ssh;
-
-import java.io.*;
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * SSH implementation of the <tt>AbstractFileTransfer</tt>.
- *
- * @author Yana Stamcheva
- */
-public class FileTransferSSHImpl
- extends AbstractFileTransfer
-{
- private final SSHFileTransferDaemon fileTransfer;
-
- private final Date initialDate;
-
- /**
- * Creates an SSH implementation of the file transfer interface.
- *
- * @param fileTransfer the SSH file transfer
- * @param date the initial date of the transfer
- */
- public FileTransferSSHImpl( SSHFileTransferDaemon fileTransfer,
- Date date)
- {
- this.fileTransfer = fileTransfer;
- this.initialDate = date;
- }
-
- /**
- * Cancels this file transfer. When this method is called transfer should
- * be interrupted.
- */
- @Override
- public void cancel()
- {
- // TODO: Implement cancel() for SSH file transfer.
- }
-
- /**
- * Returns the number of bytes already transfered through this file transfer.
- *
- * @return the number of bytes already transfered through this file transfer
- */
- @Override
- public long getTransferedBytes()
- {
- // TODO: Implement getTransferedBytes() for SSH file transfer.
- return 0;
- }
-
- public int getDirection()
- {
- return IN;
- }
-
- public File getLocalFile()
- {
- return null;
- }
-
- public Contact getContact()
- {
- return null;
- }
-
- public String getID()
- {
- return null;
- }
-
- public Date getInitialDate()
- {
- return initialDate;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/MessageSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/MessageSSHImpl.java
deleted file mode 100644
index a8324f7..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/MessageSSHImpl.java
+++ /dev/null
@@ -1,63 +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.ssh;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * Very simple message implementation for the SSH protocol.
- *
- * @author Shobhit Jindal
- * @author Lubomir Marinov
- */
-public class MessageSSHImpl
- extends AbstractMessage
-{
-
- /**
- * The content type of the message.
- */
- public static String contentType = "text/plain";
-
- /**
- * Creates a message instance according to the specified parameters.
- *
- * @param content the message body
- * @param contentType message content type or null for text/plain
- * @param contentEncoding message encoding or null for UTF8
- * @param subject the subject of the message or null for no subject.
- */
- public MessageSSHImpl(String content, String contentType,
- String contentEncoding, String subject)
- {
- super(content, null, contentEncoding, subject);
-
- MessageSSHImpl.contentType = contentType;
- }
-
- /**
- * Returns the type of the content of this message.
- *
- * @return the type of the content of this message.
- */
- @Override
- public String getContentType()
- {
- return contentType;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java
deleted file mode 100644
index 9073ad5..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java
+++ /dev/null
@@ -1,312 +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.ssh;
-
-import java.io.*;
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-
-/**
- * Instant messaging functionality for the SSH protocol.
- *
- * @author Shobhit Jindal
- */
-public class OperationSetBasicInstantMessagingSSHImpl
- extends AbstractOperationSetBasicInstantMessaging
-{
-
- /**
- * The currently valid persistent presence operation set..
- */
- private OperationSetPersistentPresenceSSHImpl opSetPersPresence = null;
-
- /**
- * The currently valid file transfer operation set
- */
- private OperationSetFileTransferSSHImpl fileTransfer;
-
- /**
- * The protocol provider that created us.
- */
- private ProtocolProviderServiceSSHImpl parentProvider = null;
-
- /**
- * 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.
- */
- public OperationSetBasicInstantMessagingSSHImpl(
- ProtocolProviderServiceSSHImpl provider)
- {
- this.parentProvider = provider;
-
- this.opSetPersPresence
- = (OperationSetPersistentPresenceSSHImpl)
- provider
- .getOperationSet(OperationSetPersistentPresence.class);
- }
-
- @Override
- public Message createMessage(String content, String contentType,
- String encoding, String subject)
- {
- return new MessageSSHImpl(content, contentType, encoding, subject);
- }
-
- /**
- * Sends the <tt>message</tt> to the destination indicated by the
- * <tt>to</tt> contact. An attempt is made to re-establish the shell
- * connection if the current one is invalid.
- * The reply from server is sent by a seperate reader thread
- *
- * @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 ICQ 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 ContactSSHImpl) )
- throw new IllegalArgumentException(
- "The specified contact is not a SSH contact."
- + to);
-
- ContactSSH sshContact = (ContactSSH)to;
-
- // making sure no messages are sent and no new threads are triggered,
- // until a thread trying to connect to remote server returns
- if(sshContact.isConnectionInProgress())
- {
- deliverMessage(
- createMessage("A connection attempt is in progress"),
- (ContactSSHImpl)to);
- return;
- }
-
- if( !parentProvider.isShellConnected(sshContact) )
- {
-
- try
- {
- /**
- * creating a new SSH session / shell channel
- * - first message
- * - session is timed out
- * - network problems
- */
- parentProvider.connectShell(sshContact, message);
-
- //the first message is ignored
- return;
- }
- catch (Exception ex)
- {
- throw new IllegalStateException(ex.getMessage());
- }
- }
-
- if(wrappedMessage(message.getContent(), sshContact))
- {
- fireMessageDelivered(message, to);
- return;
- }
-
- try
- {
- sshContact.sendLine(message.getContent());
- sshContact.setCommandSent(true);
- }
- catch (IOException ex)
- {
- // Closing IO Streams
- sshContact.closeShellIO();
-
- throw new IllegalStateException(ex.getMessage());
- }
-
- fireMessageDelivered(message, to);
- }
-
- /**
- * Check the message for wrapped Commands
- * All commands begin with /
- *
- * @param message from user
- * @param sshContact of the remote machine
- *
- * @return true if the message had commands, false otherwise
- */
- private boolean wrappedMessage(
- String message,
- ContactSSH sshContact)
- {
- if(message.startsWith("/upload"))
- {
- int firstSpace = message.indexOf(' ');
-
- try
- {
- sshContact.getFileTransferOperationSet().sendFile(
- sshContact,
- null,
- message.substring(message.indexOf(' ', firstSpace+1) + 1),
- message.substring(
- firstSpace+1,
- message.indexOf(' ', firstSpace+1)));
- }
- catch (Exception e)
- {
- e.printStackTrace();
- }
-
- return true;
- }
- else if(message.startsWith("/download"))
- {
- int firstSpace = message.indexOf(' ');
-
- try
- {
- sshContact.getFileTransferOperationSet().sendFile(
- null,
- sshContact,
- message.substring(firstSpace+1, message.indexOf(' ',
- firstSpace+1)),
- message.substring(message.indexOf(' ', firstSpace+1) + 1));
- }
- catch(Exception e)
- {
- e.printStackTrace();
- }
- return true;
- }
- return false;
- }
-
- /**
- * In case the <tt>to</tt> Contact corresponds to another ssh
- * 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.
- */
- void deliverMessage(
- Message message,
- ContactSSH to)
- {
- String userID = to.getAddress();
-
- //if the user id is owr own id, then this message is being routed to us
- //from another instance of the ssh provider.
- if (userID.equals(this.parentProvider.getAccountID().getUserID()))
- {
- //check who is the provider sending the message
- String sourceUserID
- = to.getProtocolProvider().getAccountID().getUserID();
-
- //check whether they are in our contact list
- Contact from = opSetPersPresence.findContactByID(sourceUserID);
-
- //and if not - add them there as volatile.
- if(from == null)
- {
- from = opSetPersPresence.createVolatileContact(sourceUserID);
- }
-
- //and now fire the message received event.
- fireMessageReceived(message, from);
- }
- else
- {
- //if userID is not our own, try an check whether another provider
- //has that id and if yes - deliver the message to them.
- ProtocolProviderServiceSSHImpl sshProvider
- = this.opSetPersPresence.findProviderForSSHUserID(userID);
- if(sshProvider != null)
- {
- OperationSetBasicInstantMessagingSSHImpl opSetIM
- = (OperationSetBasicInstantMessagingSSHImpl)
- sshProvider
- .getOperationSet(
- OperationSetBasicInstantMessaging.class);
- opSetIM.deliverMessage(message, to);
- }
- else
- {
- //if we got here then "to" is simply someone in our contact
- //list so let's just echo the message.
- fireMessageReceived(message, to);
- }
- }
- }
-
- /**
- * 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
- protected void fireMessageReceived(Message message, Contact from)
- {
- fireMessageEvent(
- new MessageReceivedEvent(
- message,
- from,
- new Date(),
- ((ContactSSH) from).getMessageType()));
- }
-
- /**
- * Determines whether the SSH protocol provider supports
- * sending and receiving offline messages.
- *
- * @return <tt>false</tt>
- */
- public boolean isOfflineMessagingSupported()
- {
- return false;
- }
-
- /**
- * Determines wheter 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 MessageSSHImpl.contentType.equals(contentType);
- }
-
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetFileTransferSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetFileTransferSSHImpl.java
deleted file mode 100644
index 61d8160..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetFileTransferSSHImpl.java
+++ /dev/null
@@ -1,161 +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.ssh;
-
-import java.io.*;
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * This class provides operations to upload/download files to remote machines
- *
- * @author Shobhit Jindal
- */
-public class OperationSetFileTransferSSHImpl
- implements OperationSetFileTransfer
-{
- private static final Logger logger
- = Logger.getLogger(OperationSetFileTransferSSHImpl.class);
-
- /**
- * Currently registered message listeners.
- */
- private Vector<FileTransferListener> fileTransferListeners
- = new Vector<FileTransferListener>();
-
- /**
- * The protocol provider that created us.
- */
- private ProtocolProviderServiceSSHImpl parentProvider = null;
-
-
- /**
- * Creates a new instance of OperationSetFileTransferSSHImpl
- *
- * @param parentProvider the parent protocol provider service
- */
- public OperationSetFileTransferSSHImpl(
- ProtocolProviderServiceSSHImpl parentProvider)
- {
- this.parentProvider = parentProvider;
- }
-
- /**
- * Registers a FileTransferListener with this operation set so that it gets
- * notifications of start, complete, failure of file transfers
- *
- * @param listener the <tt>FileListener</tt> to register.
- */
- public void addFileTransferListener(
- FileTransferListener listener)
- {
- synchronized (fileTransferListeners)
- {
- if(!fileTransferListeners.contains(listener))
- fileTransferListeners.add(listener);
- }
- }
-
- public void removeFileTransferListener(
- FileTransferListener listener)
- {
- synchronized (fileTransferListeners)
- {
- fileTransferListeners.remove(listener);
- }
- }
-
- /**
- * Sends a file transfer request to the given <tt>toContact</tt>.
- * @param toContact the contact that should receive the file
- * @param file the file to send
- */
- public FileTransfer sendFile( Contact toContact,
- File file)
- {
- return this.sendFile( toContact,
- null,
- file.getAbsolutePath(),
- file.getAbsolutePath());
- }
-
- /**
- * The file transfer method to/from the remote machine
- * either toContact is null(we are downloading file from remote machine
- * or fromContact is null(we are uploading file to remote machine
- *
- * @param toContact - the file recipient
- * @param fromContact - the file sender
- * @param remotePath - the identifier for the remote file
- * @param localPath - the identifier for the local file
- */
- public FileTransfer sendFile(
- Contact toContact,
- Contact fromContact,
- String remotePath,
- String localPath)
- {
- if(toContact == null)
- {
- SSHFileTransferDaemon fileTransferDaemon
- = new SSHFileTransferDaemon(
- (ContactSSH)fromContact,
- parentProvider);
-
- if(localPath.endsWith(System.getProperty("file.separator")))
- localPath += remotePath.substring(remotePath.lastIndexOf(
- System.getProperty("file.separator")) + 1);
-
- fileTransferDaemon.downloadFile(
- remotePath,
- localPath);
-
- return new FileTransferSSHImpl(fileTransferDaemon, new Date());
- }
- else if(fromContact == null)
- {
- SSHFileTransferDaemon fileTransferDaemon
- = new SSHFileTransferDaemon(
- (ContactSSH) toContact,
- parentProvider);
-
- fileTransferDaemon.uploadFile(
- remotePath,
- localPath);
-
- return new FileTransferSSHImpl(fileTransferDaemon, new Date());
- }
-
- // code should not reach here
- // assert false;
- logger.error("we should not be here !");
- return null;
- }
-
- /**
- * Returns the maximum file length supported by the protocol in bytes.
- * @return the file length that is supported.
- */
- public long getMaximumFileLength()
- {
- return 2048*1024*1024;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetPersistentPresenceSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetPersistentPresenceSSHImpl.java
deleted file mode 100644
index d4c63bd..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/OperationSetPersistentPresenceSSHImpl.java
+++ /dev/null
@@ -1,980 +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.ssh;
-
-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 SSH 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 Shobhit Jindal
- */
-public class OperationSetPersistentPresenceSSHImpl
- extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceSSHImpl>
-{
- private static final Logger logger =
- Logger.getLogger(OperationSetPersistentPresenceSSHImpl.class);
-
- /**
- * The root of the ssh contact list.
- */
- private ContactGroupSSHImpl contactListRoot = null;
-
- /**
- * The currently active status message.
- */
- private String statusMessage = "Online";
-
- /**
- * Our default presence status.
- */
- private PresenceStatus presenceStatus = SSHStatusEnum.ONLINE;
-
- /**
- * Creates an instance of this operation set keeping a reference to the
- * specified parent <tt>provider</tt>.
- * @param provider the ProtocolProviderServiceSSHImpl instance that
- * created us.
- */
- public OperationSetPersistentPresenceSSHImpl(
- ProtocolProviderServiceSSHImpl provider)
- {
- super(provider);
-
- contactListRoot = new ContactGroupSSHImpl("RootGroup", provider);
-
- //add our unregistration listener
- parentProvider.addRegistrationStateChangeListener(
- new UnregistrationListener());
- }
-
- /**
- * This function changes the status of contact as well as that of the
- * provider
- *
- * @param sshContact the contact of the remote machine
- * @param newStatus new status of the contact
- */
- public void changeContactPresenceStatus(
- ContactSSH sshContact,
- PresenceStatus newStatus)
- {
- PresenceStatus oldStatus = sshContact.getPresenceStatus();
- sshContact.setPresenceStatus(newStatus);
- fireContactPresenceStatusChangeEvent(
- sshContact
- , sshContact.getParentContactGroup()
- , oldStatus);
- fireProviderStatusChangeEvent(oldStatus);
- }
-
- /**
- * 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)
- {
- ContactGroupSSHImpl newGroup
- = new ContactGroupSSHImpl(groupName, parentProvider);
-
- ((ContactGroupSSHImpl)parent).addSubgroup(newGroup);
-
- this.fireServerStoredGroupEvent(
- newGroup, ServerStoredGroupEvent.GROUP_CREATED_EVENT);
- }
-
- /**
- * A SSH Provider method to use for fast filling of a contact list.
- *
- * @param contactGroup the group to add
- */
- public void addSSHGroup(ContactGroupSSHImpl contactGroup)
- {
- contactListRoot.addSubgroup(contactGroup);
- }
-
- /**
- * A SSH 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 addSSHGroupAndFireEvent(
- ContactGroupSSHImpl parent,
- ContactGroupSSHImpl 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 the protocol specific contact instance representing the local
- * user.
- *
- * @return the Contact (address, phone number, or uin) that the Provider
- * implementation is communicating on behalf of.
- */
- public Contact getLocalContact()
- {
- return null;
- }
-
- /**
- * 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 SSHStatusEnum.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)
- {
- ContactSSHImpl sshContact
- = (ContactSSHImpl)contactToMove;
-
- ContactGroupSSHImpl parentSSHGroup
- = findContactParent(sshContact);
-
- parentSSHGroup.removeContact(sshContact);
-
- //if this is a volatile contact then we haven't really subscribed to
- //them so we'd need to do so here
- if(!sshContact.isPersistent())
- {
- //first tell everyone that the volatile contact was removed
- fireSubscriptionEvent(sshContact
- , parentSSHGroup
- , SubscriptionEvent.SUBSCRIPTION_REMOVED);
-
- try
- {
- //now subscribe
- this.subscribe(newParent, contactToMove.getAddress());
-
- //now tell everyone that we've added the contact
- fireSubscriptionEvent(sshContact
- , newParent
- , SubscriptionEvent.SUBSCRIPTION_CREATED);
- }
- catch (Exception ex)
- {
- logger.error("Failed to move contact "
- + sshContact.getAddress()
- , ex);
- }
- }
- else
- {
- ( (ContactGroupSSHImpl) newParent)
- .addContact(sshContact);
-
- fireSubscriptionMovedEvent(contactToMove
- , parentSSHGroup
- , 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.
- */
- public void publishPresenceStatus(
- PresenceStatus status,
- String statusMessage)
- throws IllegalArgumentException,
- IllegalStateException
- {
- PresenceStatus oldPresenceStatus = this.presenceStatus;
- this.presenceStatus = status;
- this.statusMessage = statusMessage;
-
- this.fireProviderStatusChangeEvent(oldPresenceStatus);
-
-
-// //since we are not a real protocol, we set the contact presence status
-// //ourselves and make them have the same status as ours.
-// changePresenceStatusForAllContacts( getServerStoredContactListRoot()
-// , getPresenceStatus());
-//
-// //now check whether we are in someone else's contact list and modify
-// //our status there
-// List contacts = findContactsPointingToUs();
-//
-// Iterator contactsIter = contacts.iterator();
-// while (contactsIter.hasNext())
-// {
-// ContactSSHImpl contact
-// = (ContactSSHImpl) contactsIter.next();
-//
-// PresenceStatus oldStatus = contact.getPresenceStatus();
-// contact.setPresenceStatus(status);
-// contact.getParentPresenceOperationSet()
-// .fireContactPresenceStatusChangeEvent(
-// contact
-// , contact.getParentContactGroup()
-// , oldStatus);
-//
-// }
- }
-
-
-
- /**
- * 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>ContactSSHImpl</tt> whose status we'd like
- * to set.
- * @param newStatus the new status we'd like to set to <tt>contact</tt>.
- */
- private void changePresenceStatusForContact(
- ContactSSH 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.
- */
- private void changePresenceStatusForAllContacts(
- ContactGroup parent,
- PresenceStatus newStatus)
- {
- //first set the status for contacts in this group
- Iterator<Contact> childContacts = parent.contacts();
-
- while(childContacts.hasNext())
- {
- ContactSSHImpl contact
- = (ContactSSHImpl)childContacts.next();
-
- if(findProviderForSSHUserID(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 sshGroup or null
- * if no parent was found.
- * @param sshGroup the group whose parent we're looking for.
- * @return the ContactGroupSSHImpl instance that sshGroup
- * belongs to or null if no parent was found.
- */
- public ContactGroupSSHImpl findGroupParent(
- ContactGroupSSHImpl sshGroup)
- {
- return contactListRoot.findGroupParent(sshGroup);
- }
-
- /**
- * Returns the group that is parent of the specified sshContact or
- * null if no parent was found.
- * @param sshContact the contact whose parent we're looking for.
- * @return the ContactGroupSSHImpl instance that sshContact
- * belongs to or null if no parent was found.
- */
- public ContactGroupSSHImpl findContactParent(
- ContactSSH sshContact)
- {
- return (ContactGroupSSHImpl)sshContact
- .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
- {
- ContactGroupSSHImpl sshGroup
- = (ContactGroupSSHImpl)group;
-
- ContactGroupSSHImpl parent = findGroupParent(sshGroup);
-
- if(parent == null)
- {
- throw new IllegalArgumentException(
- "group " + group
- + " does not seem to belong to this protocol's contact "
- + "list.");
- }
-
- parent.removeSubGroup(sshGroup);
-
- this.fireServerStoredGroupEvent(
- sshGroup, 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)
- {
- ((ContactGroupSSHImpl)group).setGroupName(newName);
-
- this.fireServerStoredGroupEvent(
- group, ServerStoredGroupEvent
- .GROUP_RENAMED_EVENT);
- }
-
-
- /**
- * 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
- {
- ContactSSH sshContact = new ContactSSHImpl(contactIdentifier,
- parentProvider);
-
-/* ProtocolProviderServiceSSHImpl.getUIService().getConfigurationWindow()
- .setVisible(true);
-*/
- sshContact.setParentGroup((ContactGroupSSHImpl)parent);
- sshContact.getSSHConfigurationForm().setVisible(true);
-
-
-
-/* Gets the domain name or IP address of the sshContact machine via the
- * UI Service Interface
- sshContact.setPersistentData(ProtocolProviderServiceSSHImpl
- .getUIService().getPopupDialog()
- .showInputPopupDialog("Enter Domain Name or IP Address of "
- + sshContact.getDisplayName()));
-
- // contact is added to list later after the user has provided
- // details in SSHConfigurationForm
-
- // addContactToList method is called
-*/
- }
-
- /**
- * Add a contact to the specified group
- *
- * @param parent the group
- * @param sshContact the contact
- */
- public void addContactToList(
- ContactGroup parent,
- ContactSSH sshContact)
- {
- // Adds the sshContact to the sshContact list
-
- ((ContactGroupSSHImpl)parent).addContact(sshContact);
-
- fireSubscriptionEvent(sshContact,
- parent,
- SubscriptionEvent.SUBSCRIPTION_CREATED);
-
- //notify presence listeners for the status change.
- fireContactPresenceStatusChangeEvent(sshContact
- , parent
- , SSHStatusEnum.NOT_AVAILABLE);
-
- sshContact.startTimerTask();
- }
-
- /**
- * 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
- {
- ContactGroupSSHImpl parentGroup
- = (ContactGroupSSHImpl)((ContactSSHImpl)contact)
- .getParentContactGroup();
-
- parentGroup.removeContact((ContactSSHImpl)contact);
-
- fireSubscriptionEvent(contact,
- ((ContactSSHImpl)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)
- {
- ContactSSH contact = new ContactSSHImpl(
- address,
- parentProvider);
-
- contact.setPersistentData(persistentData);
- contact.startTimerTask();
-
- // SSH Contacts are resolved by default
- contact.setResolved(true);
-
- ( (ContactGroupSSHImpl) parent).addContact(contact);
-
- fireSubscriptionEvent(contact,
- parent,
- SubscriptionEvent.SUBSCRIPTION_CREATED);
-
- //since we don't have any server, we'll simply resolve the contact
- //ourselves as if we've just received an event from the server telling
- //us that it has been resolved.
- fireSubscriptionEvent(
- contact, parent, SubscriptionEvent.SUBSCRIPTION_RESOLVED);
-
- return contact;
- }
-
- /**
- * Looks for a ssh protocol provider registered for a user id matching
- * <tt>sshUserID</tt>.
- *
- * @param sshUserID the ID of the SSH user whose corresponding
- * protocol provider we'd like to find.
- * @return ProtocolProviderServiceSSHImpl a ssh protocol
- * provider registered for a user with id <tt>sshUserID</tt> or null
- * if there is no such protocol provider.
- */
- public ProtocolProviderServiceSSHImpl
- findProviderForSSHUserID(String sshUserID)
- {
- BundleContext bc = SSHActivator.getBundleContext();
-
- String osgiQuery = "(&"
- + "(" + ProtocolProviderFactory.PROTOCOL
- + "=" + ProtocolNames.SSH + ")"
- + "(" + ProtocolProviderFactory.USER_ID
- + "=" + sshUserID + ")"
- + ")";
-
- 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 (ProtocolProviderServiceSSHImpl)bc.getService(refs[0]);
- }
-
- return null;
- }
-
- /**
- * Looks for ssh protocol providers that have added us to their
- * contact list and returns list of all contacts representing us in these
- * providers.
- *
- * @return a list of all contacts in other providers' contact lists that
- * point to us.
- */
- public List<Contact> findContactsPointingToUs()
- {
- List<Contact> contacts = new LinkedList<Contact>();
- BundleContext bc = SSHActivator.getBundleContext();
-
- String osgiQuery =
- "(" + ProtocolProviderFactory.PROTOCOL
- + "=SSH)";
-
- 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);
- }
-
- for (int i =0; refs != null && i < refs.length; i++)
- {
- ProtocolProviderServiceSSHImpl gibProvider
- = (ProtocolProviderServiceSSHImpl)bc.getService(refs[i]);
-
- OperationSetPersistentPresenceSSHImpl opSetPersPresence
- = (OperationSetPersistentPresenceSSHImpl)gibProvider
- .getOperationSet(OperationSetPersistentPresence.class);
-
- Contact contact = opSetPersPresence.findContactByID(
- parentProvider.getAccountID().getUserID());
-
- if (contact != null)
- contacts.add(contact);
- }
-
- return contacts;
- }
-
-
- /**
- * 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)
- {
- ContactGroupSSHImpl newGroup
- = new ContactGroupSSHImpl(
- ContactGroupSSHImpl.createNameFromUID(groupUID)
- , parentProvider);
- newGroup.setResolved(false);
-
- //if parent is null then we're adding under root.
- if(parentGroup == null)
- parentGroup = getServerStoredContactListRoot();
-
- ((ContactGroupSSHImpl)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 ssh 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 (! evt.getNewState().equals(RegistrationState.UNREGISTERED)
- && !evt.getNewState().equals(RegistrationState
- .AUTHENTICATION_FAILED)
- && !evt.getNewState().equals(RegistrationState.CONNECTION_FAILED))
- {
- return;
- }
-
- //send event notifications saying that all our buddies are
- //offline. The icq 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())
- {
- ContactSSHImpl contact
- = (ContactSSHImpl) contactsIter.next();
-
- PresenceStatus oldContactStatus
- = contact.getPresenceStatus();
-
- if (!oldContactStatus.isOnline())
- continue;
-
- contact.setPresenceStatus(SSHStatusEnum.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.
- */
- private ContactGroupSSHImpl getNonPersistentGroup()
- {
- for (int i = 0
- ; i < getServerStoredContactListRoot().countSubgroups()
- ; i++)
- {
- ContactGroupSSHImpl gr =
- (ContactGroupSSHImpl)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.
- *
- * @param contactAddress the address of the volatile contact we'd like to
- * create.
- * @return the newly created volatile contact.
- */
- public ContactSSHImpl createVolatileContact(String contactAddress)
- {
- //First create the new volatile contact;
- ContactSSHImpl newVolatileContact = new ContactSSHImpl(
- contactAddress,
- this.parentProvider);
-
- newVolatileContact.setPersistent(false);
-
-
- //Check whether a volatile group already exists and if not create
- //one
- ContactGroupSSHImpl theVolatileGroup = getNonPersistentGroup();
-
-
- //if the parent volatile group is null then we create it
- if (theVolatileGroup == null)
- {
- theVolatileGroup = new ContactGroupSSHImpl(
- SSHActivator.getResources().getI18NString(
- "service.gui.NOT_IN_CONTACT_LIST_GROUP_NAME")
- , parentProvider);
- theVolatileGroup.setResolved(false);
- theVolatileGroup.setPersistent(false);
- theVolatileGroup.addContact(newVolatileContact);
-
- 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;
- }
-
- /**
- * DUMMY METHOD
- * 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)
- {
- }
-
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolIconSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolIconSSHImpl.java
deleted file mode 100644
index 592b2b1..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolIconSSHImpl.java
+++ /dev/null
@@ -1,159 +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.ssh;
-
-import java.io.*;
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Represents the SSH protocol icon. Implements the <tt>ProtocolIcon</tt>
- * interface in order to provide a SSH logo image in two different sizes.
- *
- * @author Shobhit Jindal
- */
-public class ProtocolIconSSHImpl
- implements ProtocolIcon
-{
- private static Logger logger
- = Logger.getLogger(ProtocolIconSSHImpl.class);
-
- /**
- * 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.ssh.SSH_16x16"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_32x32,
- getImageInBytes("service.protocol.ssh.SSH_32x32"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_48x48,
- getImageInBytes("service.protocol.ssh.SSH_48x48"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_64x64,
- getImageInBytes("service.protocol.ssh.SSH_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,
- SSHActivator.getResources().getImagePath(
- "service.protocol.ssh.SSH_16x16"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_32x32,
- SSHActivator.getResources().getImagePath(
- "service.protocol.ssh.SSH_32x32"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_48x48,
- SSHActivator.getResources().getImagePath(
- "service.protocol.ssh.SSH_48x48"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_64x64,
- SSHActivator.getResources().getImagePath(
- "service.protocol.ssh.SSH_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.
- *
- * @return TRUE if a icon with the given 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 the icon
- */
- 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("protocolIconSsh");
- }
-
- /**
- * 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 = SSHActivator.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;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSH.java b/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSH.java
deleted file mode 100644
index 8c38f60..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSH.java
+++ /dev/null
@@ -1,49 +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.ssh;
-
-import net.java.sip.communicator.service.protocol.*;
-
-import org.osgi.framework.*;
-
-/**
- *
- * @author Shobhit Jindal
- */
-public abstract class ProtocolProviderFactorySSH
- extends ProtocolProviderFactory
-{
- /**
- * The name of a property representing the IDENTITY_FILE of the protocol for
- * a ProtocolProviderFactory.
- */
- public static final String IDENTITY_FILE = "IDENTITY_FILE";
-
- /**
- * The name of a property representing the KNOWN_HOSTS_FILE of the protocol
- * for a ProtocolProviderFactory.
- */
- public static final String KNOWN_HOSTS_FILE = "KNOWN_HOSTS_FILE";
-
- protected ProtocolProviderFactorySSH(BundleContext bundleContext,
- String protocolName)
- {
- super(bundleContext, protocolName);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSHImpl.java
deleted file mode 100644
index 68e33bc..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderFactorySSHImpl.java
+++ /dev/null
@@ -1,179 +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.ssh;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-import org.osgi.framework.*;
-
-/**
- * The SSH protocol provider factory creates instances of the SSH
- * protocol provider service. One Service instance corresponds to one account.
- *
- * @author Shobhit Jindal
- */
-public class ProtocolProviderFactorySSHImpl
- extends ProtocolProviderFactorySSH
-{
-
- /**
- * Creates an instance of the ProtocolProviderFactorySSHImpl.
- */
- public ProtocolProviderFactorySSHImpl()
- {
- super(SSHActivator.getBundleContext(), ProtocolNames.SSH);
- }
-
- /**
- * 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 = SSHActivator.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 SSHAccountID(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 acces
- //the configuration service and check for a stored password.
- this.storeAccount(accountID, false);
-
- accountID = loadAccount(accountProperties);
-
-/* ServiceReference ppServiceRef = context
- .getServiceReference(ProtocolProviderService.class.getName());
-
- ProtocolProviderService ppService = (ProtocolProviderService)
- context.getService(ppServiceRef);
-
- OperationSetPersistentPresence operationSetPersistentPresence =
- (OperationSetPersistentPresence) ppService.getOperationSet(
- OperationSetPersistentPresence.class);
-
- try
- {
- // The below should never fail for SSH accounts
- operationSetPersistentPresence.subscribe(userIDStr);
-
- }
- catch(OperationFailedException ex)
- {
- ex.printStackTrace();
- }
-*/
- return accountID;
- }
-
- @Override
- protected AccountID createAccountID(String userID, Map<String, String> accountProperties)
- {
- return new SSHAccountID(userID, accountProperties);
- }
-
- @Override
- protected ProtocolProviderService createService(String userID,
- AccountID accountID)
- {
- ProtocolProviderServiceSSHImpl service =
- new ProtocolProviderServiceSSHImpl();
-
- service.initialize(userID, accountID);
- return service;
- }
-
-// /**
-// * Saves the password for the specified account after scrambling it a bit
-// * so that it is not visible from first sight (Method remains highly
-// * insecure).
-// *
-// * @param accountID the AccountID for the account whose password we're
-// * storing.
-// * @param passwd the password itself.
-// *
-// * @throws java.lang.IllegalArgumentException if no account corresponding
-// * to <tt>accountID</tt> has been previously stored.
-// */
-// public void storePassword(AccountID accountID, String passwd)
-// throws IllegalArgumentException
-// {
-// super.storePassword(SSHActivator.getBundleContext(),
-// accountID,
-// String.valueOf(Base64.encode(passwd.getBytes())));
-// }
-//
-// /**
-// * Returns the password last saved for the specified account.
-// *
-// * @param accountID the AccountID for the account whose password we're
-// * looking for..
-// *
-// * @return a String containing the password for the specified accountID.
-// *
-// * @throws java.lang.IllegalArgumentException if no account corresponding
-// * to <tt>accountID</tt> has been previously stored.
-// */
-// public String loadPassword(AccountID accountID)
-// throws IllegalArgumentException
-// {
-// String password = super.loadPassword(SSHActivator.getBundleContext()
-// , accountID );
-// return(String.valueOf(Base64.decode(password)));
-// }
-
- @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/ssh/ProtocolProviderServiceSSHImpl.java b/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderServiceSSHImpl.java
deleted file mode 100644
index a4b16e6..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ProtocolProviderServiceSSHImpl.java
+++ /dev/null
@@ -1,662 +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.ssh;
-
-import java.io.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.Logger;
-
-import org.osgi.framework.*;
-
-import com.jcraft.jsch.*;
-
-/**
- * A SSH implementation of the ProtocolProviderService.
- *
- * @author Shobhit Jindal
- */
-public class ProtocolProviderServiceSSHImpl
- extends AbstractProtocolProviderService
-{
- private static final Logger logger
- = Logger.getLogger(ProtocolProviderServiceSSHImpl.class);
-
- /**
- * The name of this protocol.
- */
- public static final String SSH_PROTOCOL_NAME = ProtocolNames.SSH;
-
-// /**
-// * The identifier for SSH Stack
-// * Java Secure Channel JSch
-// */
-// JSch jsch = new JSch();
-
- /**
- * The test command given after each command to determine the reply length
- * of the command
- */
- //private final String testCommand =
- // Resources.getString("testCommand");
-
- /**
- * A reference to the protocol provider of UIService
- */
- private static ServiceReference ppUIServiceRef;
-
- /**
- * Connection timeout to a remote server in milliseconds
- */
- private static int connectionTimeout = 30000;
-
- /**
- * A reference to UI Service
- */
- private static UIService uiService;
-
- /**
- * The id of the account that this protocol provider represents.
- */
- private AccountID accountID = null;
-
- /**
- * We use this to lock access to initialization.
- */
- private final Object initializationLock = new Object();
-
- private OperationSetBasicInstantMessagingSSHImpl basicInstantMessaging;
-
- private OperationSetFileTransferSSHImpl fileTranfer;
-
- /**
- * Indicates whether or not the provider is initialized and ready for use.
- */
- private boolean isInitialized = false;
-
- /**
- * The logo corresponding to the ssh protocol.
- */
- private ProtocolIconSSHImpl sshIcon
- = new ProtocolIconSSHImpl();
-
- /**
- * The registration state of SSH Provider is taken to be registered by
- * default as it doesn't correspond to the state on remote server
- */
- private RegistrationState currentRegistrationState
- = RegistrationState.REGISTERED;
-
- /**
- * The default constructor for the SSH protocol provider.
- */
- public ProtocolProviderServiceSSHImpl()
- {
- if (logger.isTraceEnabled())
- logger.trace("Creating a ssh provider.");
-
- try
- {
- // converting to milliseconds
- connectionTimeout = Integer.parseInt(Resources.getString(
- "connectionTimeout")) * 1000;
- }
- catch(NumberFormatException ex)
- {
- logger.error("Connection Timeout set to 30 seconds");
- }
- }
-
- /**
- * 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 ssh 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
- OperationSetPersistentPresenceSSHImpl persistentPresence =
- new OperationSetPersistentPresenceSSHImpl(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
- basicInstantMessaging = new
- OperationSetBasicInstantMessagingSSHImpl(
- this);
- addSupportedOperationSet(
- OperationSetBasicInstantMessaging.class,
- basicInstantMessaging);
-
- //initialze the file transfer operation set
- fileTranfer = new OperationSetFileTransferSSHImpl(this);
- addSupportedOperationSet(
- OperationSetFileTransfer.class,
- fileTranfer);
-
- isInitialized = true;
- }
- }
-
- /**
- * Determines whether a vaild session exists for the contact of remote
- * machine.
- *
- * @param sshContact ID of SSH Contact
- *
- * @return <tt>true</tt> if the session is connected
- * <tt>false</tt> otherwise
- */
- public boolean isSessionValid(ContactSSH sshContact)
- {
- Session sshSession = sshContact.getSSHSession();
- if( sshSession != null)
- if(sshSession.isConnected())
- return true;
-
- // remove reference to an unconnected SSH Session, if any
- sshContact.setSSHSession(null);
- return false;
- }
-
- /**
- * Determines whether the contact is connected to shell of remote machine
- * as a precheck for any further operation
- *
- * @param sshContact ID of SSH Contact
- *
- * @return <tt>true</tt> if the contact is connected
- * <tt>false</tt> if the contact is not connected
- */
- public boolean isShellConnected(ContactSSH sshContact)
- {
- // a test command may also be run here
-
- if(isSessionValid(sshContact))
- {
- return(sshContact.getShellChannel() != null);
- }
-
- /*
- * Above should be return(sshContact.getShellChannel() != null
- * && sshContact.getShellChannel().isConnected());
- *
- * but incorrect reply from stack for isConnected()
- */
-
- return false;
- }
-
- /**
- * Creates a shell channel to the remote machine
- * a new jsch session is also created if the current one is invalid
- *
- * @param sshContact the contact of the remote machine
- * @param firstMessage the first message
- */
- public void connectShell(
- final ContactSSH sshContact,
- final Message firstMessage)
- {
- sshContact.setConnectionInProgress(true);
-
- final Thread newConnection = new Thread((new Runnable()
- {
- public void run()
- {
- OperationSetPersistentPresenceSSHImpl persistentPresence
- = (OperationSetPersistentPresenceSSHImpl)sshContact
- .getParentPresenceOperationSet();
-
- persistentPresence.changeContactPresenceStatus(
- sshContact,
- SSHStatusEnum.CONNECTING);
-
- try
- {
- if(!isSessionValid(sshContact))
- createSSHSessionAndLogin(sshContact);
-
- createShellChannel(sshContact);
-
- //initializing the reader and writers of ssh contact
-
- persistentPresence.changeContactPresenceStatus(
- sshContact,
- SSHStatusEnum.CONNECTED);
-
- showWelcomeMessage(sshContact);
-
- sshContact.setMessageType(ContactSSH
- .CONVERSATION_MESSAGE_RECEIVED);
-
- sshContact.setConnectionInProgress(false);
-
- Thread.sleep(1500);
-
- sshContact.setCommandSent(true);
-
- basicInstantMessaging.sendInstantMessage(
- sshContact,
- firstMessage);
- }
- // rigorous Exception Checking in future
- catch (Exception ex)
- {
- persistentPresence.changeContactPresenceStatus(
- sshContact,
- SSHStatusEnum.NOT_AVAILABLE);
-
- ex.printStackTrace();
- }
- finally
- {
- sshContact.setConnectionInProgress(false);
- }
- }
- }));
-
- newConnection.start();
- }
-
- /**
- * Creates a channel for shell type in the current session
- * channel types = shell, sftp, exec(X forwarding),
- * direct-tcpip(stream forwarding) etc
- *
- * @param sshContact ID of SSH Contact
- * @throws IOException if the shell channel cannot be created
- */
- public void createShellChannel(ContactSSH sshContact)
- throws IOException
- {
- try
- {
- Channel shellChannel = sshContact.getSSHSession()
- .openChannel("shell");
-
- //initalizing the reader and writers of ssh contact
- sshContact.initializeShellIO(shellChannel.getInputStream(),
- shellChannel.getOutputStream());
-
- ((ChannelShell)shellChannel).setPtyType(
- sshContact.getSSHConfigurationForm().getTerminalType());
-
- //initializing the shell
- shellChannel.connect(1000);
-
- sshContact.setShellChannel(shellChannel);
-
- sshContact.sendLine("export PS1=");
- }
- catch (JSchException ex)
- {
- sshContact.setSSHSession(null);
- throw new IOException("Unable to create shell channel to remote" +
- " server");
- }
- }
-
- /**
- * Closes the Shell channel are associated IO Streams
- *
- * @param sshContact ID of SSH Contact
- * @throws JSchException if something went wrong in JSch
- * @throws IOException if I/O exception occurred
- */
- public void closeShellChannel(ContactSSH sshContact) throws
- JSchException,
- IOException
- {
- sshContact.closeShellIO();
- sshContact.getShellChannel().disconnect();
- sshContact.setShellChannel(null);
- }
-
- /**
- * Creates a SSH Session with a remote machine and tries to login
- * according to the details specified by Contact
- * An appropriate message is shown to the end user in case the login fails
- *
- * @param sshContact ID of SSH Contact
- *
- * @throws JSchException if a JSch is unable to create a SSH Session with
- * the remote machine
- * @throws InterruptedException if the thread is interrupted before session
- * connected or is timed out
- * @throws OperationFailedException if not of above reasons :-)
- */
- public void createSSHSessionAndLogin(ContactSSH sshContact) throws
- JSchException,
- OperationFailedException,
- InterruptedException
- {
- if (logger.isInfoEnabled())
- logger.info("Creating a new SSH Session to "
- + sshContact.getHostName());
-
- // creating a new JSch Stack identifier for contact
- JSch jsch = new JSch();
-
- String knownHosts =
- accountID.getAccountPropertyString("KNOWN_HOSTS_FILE");
-
- if(!knownHosts.equals("Optional"))
- jsch.setKnownHosts(knownHosts);
-
- String identitiyKey =
- accountID.getAccountPropertyString("IDENTITY_FILE");
-
- String userName = sshContact.getUserName();
-
- // use the name of system user if the contact has not supplied SSH
- // details
- if(userName.equals(""))
- userName = System.getProperty("user.name");
-
- if(!identitiyKey.equals("Optional"))
- jsch.addIdentity(identitiyKey);
-
- // creating a new session for the contact
- Session session = jsch.getSession(
- userName,
- sshContact.getHostName(),
- sshContact.getSSHConfigurationForm().getPort());
-
- /**
- * Creating and associating User Info with the session
- * User Info passes authentication from sshContact to SSH Stack
- */
- SSHUserInfo sshUserInfo = new SSHUserInfo(sshContact);
-
- session.setUserInfo(sshUserInfo);
-
- /**
- * initializing the session
- */
- session.connect(connectionTimeout);
-
- int count = 0;
-
- // wait for session to get connected
- while(!session.isConnected() && count<=30000)
- {
- Thread.sleep(1000);
- count += 1000;
- if (logger.isTraceEnabled())
- logger.trace("SSH:" + sshContact.getHostName()
- + ": Sleep zzz .. " );
- }
-
- // if timeout have exceeded
- if(count>30000)
- {
- sshContact.setSSHSession(null);
- JOptionPane.showMessageDialog(
- null,
- "SSH Connection attempt to "
- + sshContact.getHostName() + " timed out");
-
- // error codes are not defined yet
- throw new OperationFailedException("SSH Connection attempt to " +
- sshContact.getHostName() + " timed out", 2);
- }
-
- sshContact.setJSch(jsch);
- sshContact.setSSHSession(session);
-
- if (logger.isInfoEnabled())
- logger.info("A new SSH Session to " + sshContact.getHostName()
- + " Created");
- }
-
- /**
- * Closes the SSH Session associated with the contact
- *
- * @param sshContact ID of SSH Contact
- */
- void closeSSHSession(ContactSSH sshContact)
- {
- sshContact.getSSHSession().disconnect();
- sshContact.setSSHSession(null);
- }
-
- /**
- * Presents the login welcome message to user
- *
- * @param sshContact ID of SSH Contact
- * @throws IOException if I/O exception occurred
- */
- public void showWelcomeMessage(ContactSSH sshContact)
- throws IOException
- {
-/* //sending the command
- sshContact.sendLine(testCommand);
-
- String reply = "", line = "";
-
- // message is extracted until the test Command ie echoed back
- while(line.indexOf(testCommand) == -1)
- {
- reply += line + "\n";
- line = sshContact.getLine();
- }
-
- uiService.getPopupDialog().showMessagePopupDialog
- (reply,"Message from " + sshContact.getDisplayName(),
- uiService.getPopupDialog().INFORMATION_MESSAGE);
-
- if(line.startsWith(testCommand))
- while(!sshContact.getLine().contains(testCommand));
-
- //one line output of testCommand
- sshContact.getLine();
-*/
- if (logger.isDebugEnabled())
- logger.debug("SSH: Welcome message shown");
- }
-
- /**
- * Returns a reference to UIServce for accessing UI related services
- *
- * @return uiService a reference to UIService
- */
- public static UIService getUIService()
- {
- return uiService;
- }
-
- /**
- * 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 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 SSH_PROTOCOL_NAME;
- }
-
- /**
- * 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
- {
- RegistrationState oldState = currentRegistrationState;
- currentRegistrationState = RegistrationState.REGISTERED;
-
- //get a reference to UI Service via its Service Reference
- ppUIServiceRef = SSHActivator.getBundleContext()
- .getServiceReference(UIService.class.getName());
-
- uiService = (UIService)SSHActivator.getBundleContext()
- .getService(ppUIServiceRef);
-
- 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 SSH 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;
-
- 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 ssh protocol icon.
- * @return the ssh protocol icon
- */
- public ProtocolIcon getProtocolIcon()
- {
- return sshIcon;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/Resources.java b/src/net/java/sip/communicator/impl/protocol/ssh/Resources.java
deleted file mode 100644
index 48b9b49..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/Resources.java
+++ /dev/null
@@ -1,53 +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.ssh;
-
-import net.java.sip.communicator.service.resources.*;
-
-/**
- * @author Shobhit Jindal
- */
-public class Resources
-{
- /**
- * The SSH logo imageID.
- */
- public static ImageID SSH_LOGO = new ImageID("protocolIconSsh");
-
- /**
- * Returns an string corresponding to the given key.
- *
- * @param key The key of the string.
- *
- * @return a string corresponding to the given key.
- */
- public static String getString(String key)
- {
- return SSHActivator.getResources().getI18NString(key);
- }
-
- /**
- * Loads an image from a given image identifier.
- * @param imageID The identifier of the image.
- * @return The image for the given identifier.
- */
- public static byte[] getImage(ImageID imageID)
- {
- return SSHActivator.getResources().getImageInBytes(imageID.getId());
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHAccountID.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHAccountID.java
deleted file mode 100644
index 8f9dda7..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHAccountID.java
+++ /dev/null
@@ -1,45 +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.ssh;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * The SSH implementation of a sip-communicator account id.
- * @author Shobhit Jindal
- */
-public class SSHAccountID
- extends AccountID
-{
- /**
- * Creates an account id from the specified id and account properties.
- *
- * @param userID the user identifier correspnding to thi account
- * @param accountProperties any other properties necessary for the account.
- */
- SSHAccountID(String userID, Map<String, String> accountProperties)
- {
- super(userID
- , accountProperties
- , "SSH"
- , "sip-communicator.org");
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHActivator.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHActivator.java
deleted file mode 100644
index 7108020..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHActivator.java
+++ /dev/null
@@ -1,145 +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.ssh;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.resources.*;
-import net.java.sip.communicator.util.*;
-
-import org.jitsi.service.resources.*;
-import org.osgi.framework.*;
-
-/**
- * Loads the SSH provider factory and registers its services in the OSGI
- * bundle context.
- *
- * @author Shobhit Jindal
- */
-public class SSHActivator
- implements BundleActivator
-{
- private static final Logger logger
- = Logger.getLogger(SSHActivator.class);
-
- /**
- * A reference to the registration of our SSH protocol provider
- * factory.
- */
- private ServiceRegistration sshPpFactoryServReg = null;
-
- /**
- * A reference to the SSH protocol provider factory.
- */
- private static ProtocolProviderFactorySSHImpl
- sshProviderFactory = null;
-
- /**
- * The currently valid bundle context.
- */
- private static BundleContext bundleContext = null;
-
- private static ResourceManagementService resourcesService;
-
- /**
- * Called when this bundle is started. In here we'll export the
- * ssh 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
- {
- bundleContext = context;
-
- Hashtable<String, String> hashtable = new Hashtable<String, String>();
- hashtable.put(ProtocolProviderFactory.PROTOCOL, "SSH");
-
- sshProviderFactory = new ProtocolProviderFactorySSHImpl();
-
- //reg the ssh provider factory.
- sshPpFactoryServReg = context.registerService(
- ProtocolProviderFactory.class.getName(),
- sshProviderFactory,
- hashtable);
-
- if (logger.isInfoEnabled())
- logger.info("SSH protocol implementation [STARTED].");
- }
-
- /**
- * Returns a reference to the bundle context that we were started with.
- * @return bundleContext a reference to the BundleContext instance
- * that we were started with.
- */
- 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 ProtocolProviderFactorySSHImpl
- getProtocolProviderFactory()
- {
- return sshProviderFactory;
- }
-
-
- /**
- * 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
- {
- sshProviderFactory.stop();
- sshPpFactoryServReg.unregister();
- if (logger.isInfoEnabled())
- logger.info("SSH protocol implementation [STOPPED].");
- }
-
- /**
- * Returns the <tt>ResourceManagementService</tt>.
- *
- * @return the <tt>ResourceManagementService</tt>.
- */
- public static ResourceManagementService getResources()
- {
- if (resourcesService == null)
- resourcesService =
- ResourceManagementServiceUtils.getService(bundleContext);
- return resourcesService;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHContactInfo.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHContactInfo.java
deleted file mode 100644
index 47bb484..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHContactInfo.java
+++ /dev/null
@@ -1,352 +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.ssh;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.text.*;
-
-import javax.swing.*;
-import javax.swing.text.*;
-
-import net.java.sip.communicator.plugin.desktoputil.*;
-
-/**
- * @author Shobhit Jindal
- */
-class SSHContactInfo
- extends SIPCommDialog
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- private ContactSSH sshContact;
-
- private JPanel mainPanel = new TransparentPanel();
- private JPanel machinePanel = new TransparentPanel();
- private JPanel detailNamesPanel = new TransparentPanel();
- private JPanel detailFieldsPanel = new TransparentPanel();
- private JPanel detailsPanel = new TransparentPanel();
-
- private JCheckBox addDetailsCheckBox = new SIPCommCheckBox("Add Details");
-
- private JButton doneButton = new JButton("Done");
- private JLabel machineID = new JLabel("Hostname / IP: ");
- private JTextField machineIDField = new JTextField();
- private JLabel userName = new JLabel("User Name: ");
- private JTextField userNameField = new JTextField();
- private JLabel password = new JLabel("Password: ");
- private JTextField passwordField = new JPasswordField();
- private JLabel port = new JLabel("Port: ");
-
- private JFormattedTextField portField;
- private JLabel secs = new JLabel("secs");
- private JLabel statusUpdate = new JLabel("Update Interval: ");
- private JLabel terminalType = new JLabel("Terminal Type: ");
- private JTextField terminalTypeField = new JTextField("SIP Communicator");
- private JSpinner updateTimer = new JSpinner();
-
- private JPanel emptyPanel1 = new TransparentPanel();
-
- private JPanel emptyPanel2 = new TransparentPanel();
-
- private JPanel emptyPanel3 = new TransparentPanel();
-
- private JPanel emptyPanel4 = new TransparentPanel();
-
- private JPanel emptyPanel5 = new TransparentPanel();
-
- private JPanel emptyPanel6 = new TransparentPanel();
-
- private JPanel emptyPanel7 = new TransparentPanel();
-
- private JPanel emptyPanel8 = new TransparentPanel();
-
- private JPanel emptyPanel9 = new TransparentPanel();
-
- private JPanel emptyPanel10 = new TransparentPanel();
-
- private JPanel emptyPanel11 = new TransparentPanel();
-
-// private ContactGroup contactGroup = null;
-
- /**
- * Creates a new instance of SSHContactInfo
- *
- * @param sshContact the concerned contact
- */
- public SSHContactInfo(ContactSSH sshContact) {
- super(true);
-
- this.sshContact = sshContact;
- initForm();
-
- this.getContentPane().add(mainPanel);
-
- this.setSize(370, 325);
-
- this.setResizable(false);
-
- this.setTitle("SSH: Account Details of " + sshContact.getDisplayName());
-
- Toolkit toolkit = Toolkit.getDefaultToolkit();
- Dimension screenSize = toolkit.getScreenSize();
-
- int x = (screenSize.width - this.getWidth()) / 2;
- int y = (screenSize.height - this.getHeight()) / 2;
-
- this.setLocation(x,y);
-
-// ProtocolProviderServiceSSHImpl.getUIService().getConfigurationWindow().
-// addConfigurationForm(this);
- }
-
- /**
- * initialize the form.
- */
- public void initForm() {
- updateTimer.setValue(30);
- MaskFormatter maskFormatter = new MaskFormatter();
- try {
- maskFormatter.setMask("#####");
- } catch (ParseException ex) {
- ex.printStackTrace();
- }
- maskFormatter.setAllowsInvalid(false);
- portField = new JFormattedTextField(maskFormatter);
- portField.setValue(22);
-
- userNameField.setEnabled(false);
- passwordField.setEditable(false);
- portField.setEnabled(false);
- terminalTypeField.setEnabled(false);
- updateTimer.setEnabled(false);
-
- mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
- machinePanel.setLayout(new BoxLayout(machinePanel, BoxLayout.X_AXIS));
- detailNamesPanel.setLayout(new BoxLayout(detailNamesPanel,
- BoxLayout.Y_AXIS));
- detailFieldsPanel.setLayout(new BoxLayout(detailFieldsPanel,
- BoxLayout.Y_AXIS));
- detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.X_AXIS));
-
- machinePanel.add(machineID);
- machinePanel.add(machineIDField);
-
- detailNamesPanel.add(userName);
- detailNamesPanel.add(emptyPanel1);
- detailNamesPanel.add(password);
- detailNamesPanel.add(emptyPanel2);
- detailNamesPanel.add(port);
- detailNamesPanel.add(emptyPanel3);
- detailNamesPanel.add(statusUpdate);
- detailNamesPanel.add(emptyPanel4);
- detailNamesPanel.add(terminalType);
-
- detailFieldsPanel.add(userNameField);
- detailFieldsPanel.add(emptyPanel5);
- detailFieldsPanel.add(passwordField);
- detailFieldsPanel.add(emptyPanel6);
- detailFieldsPanel.add(portField);
- detailFieldsPanel.add(emptyPanel7);
- detailFieldsPanel.add(updateTimer);
- detailFieldsPanel.add(emptyPanel8);
- detailFieldsPanel.add(terminalTypeField);
-
- detailsPanel.add(detailNamesPanel);
- detailsPanel.add(detailFieldsPanel);
-
- detailsPanel.setBorder(BorderFactory.createTitledBorder("Details"));
-
- mainPanel.add(emptyPanel9);
- mainPanel.add(machinePanel);
- mainPanel.add(addDetailsCheckBox);
- mainPanel.add(detailsPanel);
- mainPanel.add(emptyPanel10);
- mainPanel.add(doneButton);
- mainPanel.add(emptyPanel11);
-
- addDetailsCheckBox.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent event) {
- addDetailsCheckBox.setEnabled(false);
- userNameField.setEnabled(true);
- passwordField.setEditable(true);
- portField.setEnabled(true);
- terminalTypeField.setEnabled(true);
- updateTimer.setEnabled(true);
-
- userNameField.grabFocus();
- }
- });
-
- doneButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent event) {
- if(machineIDField.getText().equals("")) {
- machineIDField.setText("Field needed");
- return;
- }
-
- sshContact.savePersistentDetails();
-
- //add contact to contact list
- ((OperationSetPersistentPresenceSSHImpl)sshContact
- .getParentPresenceOperationSet())
- .addContactToList(
- sshContact.getParentContactGroup(),
- sshContact);
-
- setVisible(false);
- }
- });
- }
-
- /**
- * Return the ssh icon
- *
- * @return the ssh icon
- */
- public byte[] getIcon() {
- return Resources.getImage(Resources.SSH_LOGO);
- }
-
-//
-// public void setContactGroup(ContactGroup contactGroup)
-// {
-// this.contactGroup = contactGroup;
-// }
-//
-// public ContactGroup getContactGroup()
-// {
-// return this.contactGroup;
-// }
-
- /**
- * Sets the UserName of the dialog
- *
- * @param userName to be associated
- */
- public void setUserNameField(String userName) {
- this.userNameField.setText(userName);
- }
-
- /**
- * Sets the Password of the dialog
- *
- * @param password to be associated
- */
- public void setPasswordField(String password) {
- this.passwordField.setText(password);
- }
-
- /**
- * Return the hostname
- *
- * @return the hostname
- */
- public String getHostName() {
- return this.machineIDField.getText();
- }
-
- /**
- * Return the username
- *
- * @return the username
- */
- public String getUserName() {
- return this.userNameField.getText();
- }
-
- /**
- * Return the password
- *
- * @return the password in a clear form
- */
- public String getPassword() {
- return this.passwordField.getText();
- }
-
- /**
- * Return the terminal type
- *
- * @return the terminal type
- */
- public String getTerminalType() {
- return this.terminalTypeField.getText();
- }
-
- /**
- * Return the port
- *
- * @return the port value
- */
- public int getPort() {
- return Integer.parseInt(this.portField.getText().trim());
- }
-
- /**
- * Return the update interval
- *
- * @return the update interval
- */
- public int getUpdateInterval() {
- return Integer.parseInt(String.valueOf(this.updateTimer.getValue()));
- }
-
- /**
- * Sets the HostName of the dialog
- *
- * @param hostName to be associated
- */
- public void setHostNameField(String hostName) {
- this.machineIDField.setText(hostName);
- }
-
- /**
- * Sets the Terminal Type of the dialog
- *
- * @param termType to be associated
- */
- public void setTerminalType(String termType) {
- this.terminalTypeField.setText(termType);
- }
-
- /**
- * Sets the Update Interval of the dialog
- *
- * @param interval to be associated
- */
- public void setUpdateInterval(int interval) {
- this.updateTimer.setValue(interval);
- }
-
- /**
- * Sets the Port of the dialog
- *
- * @param port to be associated
- */
- public void setPort(String port) {
- this.portField.setText(port);
- }
-
- @Override
- protected void close(boolean isEscaped)
- {
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHFileTransferDaemon.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHFileTransferDaemon.java
deleted file mode 100644
index 3b522ed..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHFileTransferDaemon.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.ssh;
-
-import java.io.*;
-
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.Logger;
-
-import com.jcraft.jsch.*;
-
-/**
- * @author Shobhit Jindal
- */
-public class SSHFileTransferDaemon
- extends Thread
-{
- private static final Logger logger =
- Logger.getLogger(SSHFileTransferDaemon .class);
-
- /**
- * The contact of the remote machine
- */
- private ContactSSH sshContact;
-
- /**
- * The currently valid ssh protocol provider
- */
- private ProtocolProviderServiceSSHImpl ppService;
-
- /**
- * JSch Channel to be used for file transfer
- */
- private Channel fileTransferChannel;
-
- /**
- * The identifier for the Input Stream associated with SCP Channel
- */
- private InputStream scpInputStream = null;
-
- /**
- * The identifier for the Output Stream associated with SCP Channel
- */
- private OutputStream scpOutputStream = null;
-
- /**
- * Identifier of local file
- */
- private String localPath;
-
- /**
- * Identifier of remote file
- */
- private String remotePath;
-
- /**
- * File to be uploaded or saved
- */
- private File file;
-
- /**
- * The file input stream associated with the file to be uploaded
- */
- private FileInputStream fileInputStream;
-
- /**
- * The file output stream associated with the file to be uploaded
- */
- private FileOutputStream fileOutputStream;
-
- /**
- * The boolean which determines whether we are uploading or downloading
- * files
- */
- private boolean uploadFile;
-
- /**
- * The currently valid ssh persistent presence operation set
- */
- private OperationSetPersistentPresenceSSHImpl opSetPersPresence = null;
-
- /**
- * The currently valid ssh instant messaging operation set
- */
- private OperationSetBasicInstantMessagingSSHImpl instantMessaging = null;
-
- /**
- * Creates a new instance of SSHFileTransferDaemon
- *
- *
- * @param sshContact The contact of the remote machine
- * @param ppService The current ssh protocol provider
- */
- public SSHFileTransferDaemon(
- ContactSSH sshContact,
- ProtocolProviderServiceSSHImpl ppService)
- {
- super();
- this.sshContact = sshContact;
- this.opSetPersPresence = (OperationSetPersistentPresenceSSHImpl)
- ppService.getOperationSet(OperationSetPersistentPresence.class);
- this.instantMessaging = (OperationSetBasicInstantMessagingSSHImpl)
- ppService.getOperationSet(
- OperationSetBasicInstantMessaging.class);
- this.ppService = ppService;
- }
-
- /**
- * This method is called when file is to be transfered from local machine
- * to remote machine
- *
- * @param remotePath - the identifier for the remote file
- * @param localPath - the identifier for the local file
- */
- public void uploadFile(
- String remotePath,
- String localPath)
- {
- this.uploadFile = true;
- this.remotePath = remotePath;
- this.localPath = localPath;
-
- file = new File(localPath);
-
- start();
- }
-
- /**
- * This method is called when a file is to be downloaded from remote machine
- * to local machine
- *
- * @param remotePath - the identifier for the remote file
- * @param localPath - the identifier for the local file
- */
- public void downloadFile(
- String remotePath,
- String localPath)
- {
- this.uploadFile = false;
- this.remotePath = remotePath;
- this.localPath = localPath;
-
- file = new File(localPath);
-
- start();
- }
-
- /**
- * Background thread for the file transfer
- */
- @Override
- public void run()
- {
- //oldStatus to be resumed earlier
- PresenceStatus oldStatus = sshContact.getPresenceStatus();
-
- opSetPersPresence.changeContactPresenceStatus(
- sshContact,
- SSHStatusEnum.CONNECTING);
-
- try
- {
- //create a new JSch session if current is invalid
- if( !ppService.isSessionValid(sshContact))
- ppService.createSSHSessionAndLogin(sshContact);
-
- fileTransferChannel = sshContact.getSSHSession()
- .openChannel("exec");
- String command;
-
- // -p = Preserves modification times, access times, and modes from
- // the original file
- if(uploadFile)
- command = "scp -p -t " + remotePath;
- else
- command = "scp -f " + remotePath;
-
- //the command to be executed on the remote terminal
- ((ChannelExec)fileTransferChannel).setCommand(command);
-
- scpInputStream = fileTransferChannel.getInputStream();
- scpOutputStream = fileTransferChannel.getOutputStream();
-
- fileTransferChannel.connect();
-
- //file transfer is setup
- opSetPersPresence.changeContactPresenceStatus(
- sshContact,
- SSHStatusEnum.FILE_TRANSFER);
-
- if(uploadFile)
- {
- instantMessaging.deliverMessage(
- instantMessaging.createMessage(
- "Uploading " + file.getName() + " to server"),
- sshContact);
-
- upload();
- }
- else
- {
- instantMessaging.deliverMessage(
- instantMessaging.createMessage(
- "Downloading " + file.getName() + " from server"),
- sshContact);
-
- download();
- }
-
- }
- catch(Exception ex)
- {
- //presently errors(any type) are directly logged directly in chat
- instantMessaging.deliverMessage(
- instantMessaging.createMessage(ex.getMessage()),
- sshContact);
-
- logger.error(ex.getMessage());
-
- try
- {
- if(fileInputStream!=null)
- {
- fileInputStream.close();
- }
-
- if(fileOutputStream!=null)
- {
- fileOutputStream.close();
- }
- }
- catch(Exception e)
- {}
- }
-
- // restore old status
- opSetPersPresence.changeContactPresenceStatus(
- sshContact,
- oldStatus);
- }
-
- /**
- * Check for error in reading stream of remote machine
- *
- * @return 0 for success, 1 for error, 2 for fatal error, -1 otherwise
- * @throws IOException when the network goes down
- */
- private int checkAck(InputStream inputStream)
- throws IOException
- {
- int result = inputStream.read();
-
- // read error message
- if(result==1 || result==2)
- {
- StringBuffer buffer = new StringBuffer();
-
- int ch;
-
- do
- {
- //read a line of message
- ch = inputStream.read();
- buffer.append((char)ch);
-
- }while(ch != '\n');
-
- ProtocolProviderServiceSSHImpl
- .getUIService()
- .getPopupDialog()
- .showMessagePopupDialog(
- buffer.toString(),
- "File Transfer Error: "
- + sshContact.getDisplayName(),
- PopupDialog.ERROR_MESSAGE);
-
- logger.error(buffer.toString());
- }
-
- return result;
- }
-
- /**
- * Uploads the file to the remote server
- *
- * @throws IOException when the network goes down
- * @throws OperationFailedException when server behaves unexpectedly
- */
- private void upload()
- throws IOException,
- OperationFailedException
- {
- fileInputStream = new FileInputStream(file);
-
- byte[] buffer = new byte[1024];
- int result, bytesRead;
-
- if( (result = checkAck(scpInputStream)) !=0)
- throw new OperationFailedException("Error in Ack", result);
-
- // send "C0644 filesize filename", where filename should not include '/'
- long filesize= file.length();
- String command = "C0644 " + filesize + " ";
-
-// if(lfile.lastIndexOf('/')>0)
-// {
-// command+=lfile.substring(lfile.lastIndexOf('/')+1);
-// }
-// else
-// {
-// command+=lfile;
-// }
-
- command += file.getName() + "\n";
- if (logger.isTraceEnabled())
- logger.trace(command);
- scpOutputStream.write(command.getBytes());
- scpOutputStream.flush();
-
- if( (result = checkAck(scpInputStream)) !=0)
- throw new OperationFailedException("Error in Ack", result);
-
- while(true)
- {
- bytesRead = fileInputStream.read(buffer, 0, buffer.length);
- if(bytesRead <= 0)
- break;
-
- scpOutputStream.write(buffer, 0, bytesRead); //out.flush();
- }
- fileInputStream.close();
- fileInputStream = null;
-
- // send '\0'
- buffer[0]=0; scpOutputStream.write(buffer, 0, 1);
- scpOutputStream.flush();
-
- if( (result = checkAck(scpInputStream)) !=0)
- throw new OperationFailedException("Error in Ack", result);
-
- scpInputStream.close();
- scpOutputStream.close();
-
- fileTransferChannel.disconnect();
-
- instantMessaging.deliverMessage(
- instantMessaging.createMessage(file.getName()
- + " uploaded to Server"),
- sshContact);
- }
-
- /**
- * Downloads a file from the remote machine
- *
- * @throws IOException when the network goes down
- * @throws OperationFailedException when server behaves unexpectedly
- */
- private void download()
- throws IOException,
- OperationFailedException
- {
- fileOutputStream = new FileOutputStream(file);
-
- int result;
-
- byte[] buffer = new byte[1024];
-
- // send '\0'
- buffer[0]=0;
-
- scpOutputStream.write(buffer, 0, 1);
- scpOutputStream.flush();
-
- int ch = checkAck(scpInputStream);
-
- if(ch!='C')
- {
- throw new OperationFailedException("Invalid reply from server", 12);
- }
-
- // read '0644 '
- scpInputStream.read(buffer, 0, 5);
-
- long filesize=0L;
- while(true)
- {
- if(scpInputStream.read(buffer, 0, 1) < 0)
- {
- // error
- break;
- }
- if(buffer[0]==' ')break;
- filesize=filesize*10L+buffer[0]-'0';
- }
-
- String file=null;
- for(int i=0;true;i++)
- {
- scpInputStream.read(buffer, i, 1);
- if(buffer[i]==(byte)0x0a)
- {
- file=new String(buffer, 0, i);
- break;
- }
- }
-
- //System.out.println("filesize="+filesize+", file="+file);
-
- // send '\0'
- buffer[0]=0;
- scpOutputStream.write(buffer, 0, 1);
- scpOutputStream.flush();
-
- // read a content of lfile
- int foo;
- while(true)
- {
- if(buffer.length<filesize)
- foo=buffer.length;
- else
- foo=(int)filesize;
-
- foo = scpInputStream.read(buffer, 0, foo);
- if(foo<0)
- break;
-
- fileOutputStream.write(buffer, 0, foo);
- filesize-=foo;
- if(filesize==0L) break;
- }
- fileOutputStream.close();
- fileOutputStream=null;
-
- if( (result = checkAck(scpInputStream)) !=0)
- throw new OperationFailedException("Error in Ack", result);
-
- // send '\0'
- buffer[0]=0;
- scpOutputStream.write(buffer, 0, 1);
- scpOutputStream.flush();
-
- scpInputStream.close();
- scpOutputStream.close();
-
- fileTransferChannel.disconnect();
-
- instantMessaging.deliverMessage(
- instantMessaging.createMessage(
- this.file.getName() + " downloaded from Server"),
- sshContact);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHReaderDaemon.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHReaderDaemon.java
deleted file mode 100644
index 2c33b5d..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHReaderDaemon.java
+++ /dev/null
@@ -1,211 +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.ssh;
-
-import java.io.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- *
- * @author Shobhit Jindal
- */
-public class SSHReaderDaemon
- extends Thread
-{
-
- /**
- * A Buffer to aggregate replies to be sent as one message
- */
- private StringBuffer replyBuffer;
-
- /**
- * The identifier of Contact representing the remote machine
- */
- private ContactSSHImpl sshContact;
-
- /**
- * The identifier of the message received from server
- */
- private String message;
-
- /**
- * An identifier representing the state of Reader Daemon
- */
- private boolean isActive = false;
-
- /**
- * This OperationSet delivers incoming message
- */
- private OperationSetBasicInstantMessagingSSHImpl instantMessaging;
-
- /**
- * Input Stream of remote user to be read
- */
- private InputStream shellInputStream;
-
- /**
- * Buffered Reader associated with above input stream
- */
- private InputStreamReader shellReader;
-
-// /**
-// * This OperationSet delivers incoming message
-// */
-// private OperationSetPersistentPresenceSSHImpl persistentPresence;
-
- /**
- * Bytes available in Input Stream before reading
- */
- private int bytesAvailable;
-
- private int bytesRead;
-
- int bufferCount;
-
- char buf;
-
- /**
- * Creates a new instance of SSHReaderDaemon
- */
- public SSHReaderDaemon(ContactSSH sshContact)
- {
- this.sshContact = (ContactSSHImpl)sshContact;
- instantMessaging =
- (OperationSetBasicInstantMessagingSSHImpl)
- sshContact
- .getProtocolProvider()
- .getOperationSet(
- OperationSetBasicInstantMessaging.class);
- }
-
- /**
- * Reads the remote machine, updating the chat window as necessary
- * in a background thread
- */
- @Override
- public void run()
- {
- shellInputStream = sshContact.getShellInputStream();
- shellReader = sshContact.getShellReader();
- replyBuffer = new StringBuffer();
-
-
- try
- {
- do
- {
- bytesAvailable = shellInputStream.available();
-
- if(bytesAvailable == 0 )
- {
- // wait if more data is available
- // for a slower connection this value need to be raised
- // to avoid splitting of messages
- Thread.sleep(250);
- continue;
- }
-
- bufferCount = 0;
-
-// if(replyBuffer > 0)
-
- do
- {
- // store the responses in a buffer
- storeMessage(replyBuffer);
-
- Thread.sleep(250);
-
- bytesAvailable = shellInputStream.available();
-
- }while(bytesAvailable > 0 && bufferCount<16384);
-
- message = replyBuffer.toString();
-
- if(sshContact.isCommandSent())
- {
- // if the response is as a result of a command sent
- sshContact.setMessageType(
- ContactSSH.CONVERSATION_MESSAGE_RECEIVED);
-
- message = message.substring(message.indexOf('\n') + 1);
-
- sshContact.setCommandSent(false);
- }
- else
- {
- // server sent an asynchronous message to the terminal
- // display it as a system message
- sshContact.setMessageType(
- ContactSSH.SYSTEM_MESSAGE_RECEIVED);
-
- //popup disabled
-// JOptionPane.showMessageDialog(
-// null,
-// message,
-// "Message from " + sshContact.getDisplayName(),
-// JOptionPane.INFORMATION_MESSAGE);
- }
-
- instantMessaging.deliverMessage(
- instantMessaging.createMessage(message),
- sshContact);
-
- replyBuffer.delete(0, replyBuffer.length());
-
- }while(isActive);
- }
- catch(Exception ex)
- {
- ex.printStackTrace();
- }
- }
-
- /**
- * Stores the response from server in a temporary buffer
- * the bytes available are determined before the function is called
- *
- * @param replyBuffer to store the response from server
- *
- * @throws IOException if the network goes down
- */
- private void storeMessage(StringBuffer replyBuffer) throws IOException
- {
- do
- {
- buf = (char)shellInputStream.read();
-
-// System.out.println(String.valueOf(buf)+ " " + (int)buf);
-
- replyBuffer.append(String.valueOf(buf));
-
-// logger.debug(shellReader.readLine());
-
- bufferCount++;
-
- bytesAvailable--;
-
- }while(bytesAvailable>0 && bufferCount<32700);
- }
-
- public void isActive(boolean isActive)
- {
- this.isActive = isActive;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHStatusEnum.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHStatusEnum.java
deleted file mode 100644
index 0877399..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHStatusEnum.java
+++ /dev/null
@@ -1,138 +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.ssh;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * An implementation of <tt>PresenceStatus</tt> that enumerates all states that
- * a SSH contact can fall into.
- *
- * @author Shobhit Jindal
- */
-public class SSHStatusEnum
- extends PresenceStatus
-{
-
- /**
- * Indicates an Offline status or status with 0 connectivity.
- */
- public static final SSHStatusEnum OFFLINE
- = new SSHStatusEnum(
- 0,
- "Offline",
- ProtocolIconSSHImpl
- .getImageInBytes("service.protocol.ssh.OFFLINE_STATUS_ICON"));
-
- /**
- * The Not Available status. Indicates that the user has connectivity
- * but might not be able to immediately act (i.e. even less immediately
- * than when in an Away status ;-P ) upon initiation of communication.
- *
- */
- public static final SSHStatusEnum NOT_AVAILABLE
- = new SSHStatusEnum(
- 35,
- "Not Available",
- ProtocolIconSSHImpl
- .getImageInBytes("service.protocol.ssh.NA_STATUS_ICON"));
-
- /**
- * The Connecting status. Indicate that the user is connecting to remote
- * server
- */
- public static final SSHStatusEnum CONNECTING
- = new SSHStatusEnum(
- 55,
- "Connecting",
- ProtocolIconSSHImpl
- .getImageInBytes("service.protocol.ssh.CONNECTING_ICON"));
-
- /**
- * The Online status. Indicate that the user is able and willing to
- * communicate.
- */
- public static final SSHStatusEnum ONLINE
- = new SSHStatusEnum(
- 65,
- "Online",
- ProtocolIconSSHImpl
- .getImageInBytes("service.protocol.ssh.SSH_16x16"));
-
-
- /**
- * The Connecting status. Indicate that the user is connecting to remote
- * server
- */
- public static final SSHStatusEnum CONNECTED
- = new SSHStatusEnum(
- 70,
- "Connecting",
- ProtocolIconSSHImpl
- .getImageInBytes("service.protocol.ssh.CONNECTED_ICON"));
-
- /**
- * The File Transfer status. Indicate that the user is transfering a file
- * to/from a remote server
- */
- public static final SSHStatusEnum FILE_TRANSFER
- = new SSHStatusEnum(
- 75,
- "Transfering File",
- ProtocolIconSSHImpl
- .getImageInBytes("service.protocol.ssh.FILE_TRANSFER_ICON"));
-
- /**
- * Initialize the list of supported status states.
- */
- private static List<PresenceStatus> supportedStatusSet = new LinkedList<PresenceStatus>();
- static
- {
- supportedStatusSet.add(OFFLINE);
-// supportedStatusSet.add(NOT_AVAILABLE);
- supportedStatusSet.add(ONLINE);
-// supportedStatusSet.add(CONNECTING);
- }
-
- /**
- * Creates an instance of <tt>SSHPresneceStatus</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 SSHStatusEnum(int status,
- String statusName,
- byte[] statusIcon)
- {
- super(status, statusName, statusIcon);
- }
-
- /**
- * Returns an iterator over all status instances supproted by the ssh
- * provider.
- * @return an <tt>Iterator</tt> over all status instances supported by the
- * ssh provider.
- */
- static Iterator<PresenceStatus> supportedStatusSet()
- {
- return supportedStatusSet.iterator();
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/SSHUserInfo.java b/src/net/java/sip/communicator/impl/protocol/ssh/SSHUserInfo.java
deleted file mode 100644
index 082b05f..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/SSHUserInfo.java
+++ /dev/null
@@ -1,164 +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.ssh;
-
-import javax.swing.*;
-
-import com.jcraft.jsch.*;
-
-/**
- * SSHUserInfo passes authentication details to JSch SSH Stack
- *
- * @author Shobhit Jindal
- */
-class SSHUserInfo
- implements UserInfo,
- UIKeyboardInteractive
-{
- /**
- * The Contact of the remote machine
- */
- private ContactSSH sshContact;
-
- /**
- * Identifier for failure of authentication
- * more explanation below in promptPassword function
- */
- private boolean failedOnce = false;
-
- /**
- * Password field for requesting auth details from user
- */
- JTextField passwordField=new JPasswordField(20);
-
- /**
- * Creates a UserInfo instance
- *
- * @param sshContact the contact concerned
- */
- SSHUserInfo(ContactSSH sshContact)
- {
- this.sshContact = sshContact;
- }
-
- /**
- * Returns the password of account associated with this contact
- *
- * @return the password of account associated with this contact
- */
- public String getPassword()
- {
- return sshContact.getPassword();
- }
-
- /**
- * Prompt for accepting the cipher information of the remote server
- *
- * @param str the string to display
- *
- * @return the user's answer
- */
- public boolean promptYesNo(String str)
- {
- Object[] options={ "yes", "no" };
- int foo=JOptionPane.showOptionDialog(null,
- str,
- "Warning",
- JOptionPane.DEFAULT_OPTION,
- JOptionPane.QUESTION_MESSAGE,
- null, options, options[0]);
- return foo==0;
- }
-
- /**
- * Passphrase authentication presently not implemented
- *
- * @return null
- */
- public String getPassphrase()
- { return null; }
-
- /**
- * Passphrase authentication presently not implemented
- *
- * @return true
- */
- public boolean promptPassphrase(String message)
- { return true; }
-
- /**
- * Asks user to re-enter password information in case of an auth failure
- *
- * @param message the message to display
- *
- * @return the user's answer
- */
- public boolean promptPassword(String message)
- {
- /**
- * Auth always fails for the first time for Redhat based machines.
- * Trying again with the same password
- */
- if(!failedOnce)
- {
- failedOnce = true;
- return true;
- }
-
- Object[] ob={passwordField};
- int result=JOptionPane.showConfirmDialog(null, ob, "Auth Failed: "
- + message,
- JOptionPane.OK_CANCEL_OPTION);
-
- if(result==JOptionPane.OK_OPTION)
- {
- sshContact.setPassword(passwordField.getText());
- return true;
- }
-
- return false;
- }
-
- /**
- * Shows a message from server
- *
- * @param message The message to display
- */
- public void showMessage(String message)
- {
- JOptionPane.showMessageDialog(null, message);
- }
-
- /**
- * Keyboard Interactive Auth - not implemented
- */
- public String[] promptKeyboardInteractive(
- String destination,
- String name,
- String instruction,
- String[] prompt,
- boolean[] echo)
- {
- String response[] = new String[prompt.length];
- response[0] = sshContact.getPassword();
- return response;
- }
-
-
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/ssh/ssh.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/ssh/ssh.provider.manifest.mf
deleted file mode 100644
index eb9472f..0000000
--- a/src/net/java/sip/communicator/impl/protocol/ssh/ssh.provider.manifest.mf
+++ /dev/null
@@ -1,20 +0,0 @@
-Bundle-Activator: net.java.sip.communicator.impl.protocol.ssh.SSHActivator
-Bundle-Name: SSH Protocol Provider
-Bundle-Description: A bundle providing support for the SSH protocol.
-Bundle-Vendor: jitsi.org
-Bundle-Version: 0.0.1
-Bundle-SymbolicName: net.java.sip.communicator.protocol.ssh
-Import-Package: org.osgi.framework,
- javax.crypto,
- javax.crypto.interfaces,
- javax.crypto.spec,
- javax.swing,
- javax.swing.border,
- javax.swing.text,
- org.jitsi.service.configuration,
- net.java.sip.communicator.service.gui,
- net.java.sip.communicator.service.protocol,
- net.java.sip.communicator.service.protocol.event,
- org.jitsi.service.resources, net.java.sip.communicator.service.resources,
- net.java.sip.communicator.util,
- net.java.sip.communicator.plugin.desktoputil
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java
deleted file mode 100644
index 0514c16..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/AbstractContactGroupYahooImpl.java
+++ /dev/null
@@ -1,40 +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.yahoo;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * The Yahoo implementation of the service.protocol.ContactGroup interface. There
- * are two types of groups possible here. <tt>RootContactGroupYahooImpl</tt>
- * which is the root node of the ContactList itself and
- * <tt>ContactGroupYahooImpl</tt> which represents standard groups. The
- * reason for having those 2 is that generally, Yahoo groups may not contain
- * subgroups. A contact list on the other hand may not directly contain buddies.
- *
- *
- * The reason for having an abstract class is only - being able to esily
- * recognize our own (Yahoo) contacts.
- * @author Damian Minkov
- */
-public abstract class AbstractContactGroupYahooImpl
- implements ContactGroup
-{
-
-
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomInvitationYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomInvitationYahooImpl.java
deleted file mode 100644
index e849f6c..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomInvitationYahooImpl.java
+++ /dev/null
@@ -1,90 +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.yahoo;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * The Yahoo implementation of the <tt>AdHocChatRoomInvitation</tt> interface.
- *
- * @author Rupert Burchardi
- * @author Valentin Martinet
- */
-public class AdHocChatRoomInvitationYahooImpl
- implements AdHocChatRoomInvitation
-{
- /**
- * Corresponding chat room instance.
- */
- private AdHocChatRoom chatRoom;
- /**
- * The name of the inviter
- */
- private String inviter;
-
- /**
- * The invitation reason.
- */
- private String reason;
-
-
- /**
- * Creates an instance of the <tt>ChatRoomInvitationMsnImpl</tt> by
- * specifying the targetChatRoom, the inviter, the reason.
- *
- * @param targetChatRoom The <tt>AdHocChatRoom</tt> for which the invitation
- * is
- * @param inviter The <tt>Contact</tt>, which sent the invitation
- * @param reason The Reason for the invitation
- */
- public AdHocChatRoomInvitationYahooImpl( AdHocChatRoom targetChatRoom,
- String inviter,
- String reason)
- {
- this.chatRoom = targetChatRoom;
- this.inviter = inviter;
- this.reason = reason;
- }
-
- /**
- * Returns the corresponding chat room.
- * @return The ad-hoc chat room
- */
- public AdHocChatRoom getTargetAdHocChatRoom()
- {
- return chatRoom;
- }
-
- /**
- * Returns the corresponding inviter.
- * @return The name of the inviter
- */
- public String getInviter()
- {
- return inviter;
- }
-
- /**
- * Returns the invitation reason.
- * @return the invitation reason
- */
- public String getReason()
- {
- return reason;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomYahooImpl.java
deleted file mode 100644
index 2775f6f..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/AdHocChatRoomYahooImpl.java
+++ /dev/null
@@ -1,581 +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.yahoo;
-
-import java.io.*;
-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 ymsg.network.*;
-
-/**
- * Represents a Yahoo ad-hoc chat room, where multiple chat users could
- * communicate in a many-to-many fashion.
- *
- * @author Rupert Burchardi
- * @author Valentin Martinet
- */
-public class AdHocChatRoomYahooImpl
- implements AdHocChatRoom
-{
- private static final Logger logger = Logger
- .getLogger(AdHocChatRoomYahooImpl.class);
-
- /**
- * Listeners that will be notified of changes in member status in the room
- * such as member joined, left or being kicked or dropped.
- */
- private Vector<AdHocChatRoomParticipantPresenceListener> memberListeners
- = new Vector<AdHocChatRoomParticipantPresenceListener>();
-
- /**
- * Listeners that will be notified every time a new message is received on
- * this ad-hoc chat room.
- */
- private Vector<AdHocChatRoomMessageListener> messageListeners
- = new Vector<AdHocChatRoomMessageListener>();
-
- /**
- * The protocol provider that created us
- */
- private ProtocolProviderServiceYahooImpl provider = null;
-
- /**
- * The operation set that created us.
- */
- private OperationSetAdHocMultiUserChatYahooImpl opSetMuc = null;
-
- /**
- * The list of participants of this chat room.
- */
- private Hashtable<String, Contact> participants
- = new Hashtable<String, Contact>();
-
- /**
- * The nickname of this chat room local user participant.
- */
- private String nickname;
-
- /**
- * The yahoo conference model of this ad-hoc chat room, its the
- * representation of an ad-hoc chat room in the lib for this protocol.
- */
- private YahooConference yahooConference = null;
-
- /**
- * Creates an instance of a chat room that has been.
- *
- * @param multiUserChat
- * MultiUserChat
- * @param provider
- * a reference to the currently valid jabber protocol provider.
- */
- public AdHocChatRoomYahooImpl( YahooConference multiUserChat,
- ProtocolProviderServiceYahooImpl provider)
- {
- this.yahooConference = multiUserChat;
- this.provider = provider;
- this.opSetMuc = (OperationSetAdHocMultiUserChatYahooImpl) provider
- .getOperationSet(OperationSetAdHocMultiUserChat.class);
- }
-
- /**
- * Registers <tt>listener</tt> so that it would receive events every time a
- * new message is received on this chat room.
- *
- * @param listener A <tt>MessageListener</tt> that would be notified every
- * time a new message is received on this chat room.
- */
- public void addMessageListener(AdHocChatRoomMessageListener listener)
- {
- synchronized (messageListeners)
- {
- if (!messageListeners.contains(listener))
- messageListeners.add(listener);
- }
- }
-
- /**
- * Removes <tt>listener</tt> so that it won't receive any further message
- * events from this room.
- *
- * @param listener The <tt>MessageListener</tt> to remove from this room
- */
- public void removeMessageListener(AdHocChatRoomMessageListener listener)
- {
- synchronized (messageListeners)
- {
- messageListeners.remove(listener);
- }
- }
-
- /**
- * Adds a listener that will be notified of changes in our status in the
- * room.
- *
- * @param listener A participant status listener.
- */
- public void addParticipantPresenceListener(
- AdHocChatRoomParticipantPresenceListener listener)
- {
- synchronized (memberListeners)
- {
- if (!memberListeners.contains(listener))
- memberListeners.add(listener);
- }
- }
-
- /**
- * Removes a listener that was being notified of changes in the status of
- * other chat room participants.
- *
- * @param listener A participant status listener.
- */
- public void removeParticipantPresenceListener(
- AdHocChatRoomParticipantPresenceListener listener)
- {
- synchronized (memberListeners)
- {
- memberListeners.remove(listener);
- }
- }
-
- /**
- * Create a Message instance for sending a simple text messages with default
- * (text/plain) content type and encoding.
- *
- * @param messageText
- * the string content of the message.
- * @return Message the newly created message
- */
- public Message createMessage(String messageText)
- {
- Message msg = new MessageYahooImpl(messageText,
- OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE,
- OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING, null);
- return msg;
- }
-
- /**
- * Returns a <tt>List</tt> of <tt>Contact</tt>s corresponding to all members
- * currently participating in this room.
- *
- * @return a <tt>List</tt> of <tt>Contact</tt> corresponding to all room
- * members.
- */
- public List<Contact> getParticipants()
- {
- return new LinkedList<Contact>(participants.values());
- }
-
- /**
- * Updates the member list of the chat room.
- *
- */
- public void updateParticipantsList()
- {
- Iterator<?> it = yahooConference.getMembers().iterator();
-
- while (it.hasNext())
- {
- YahooUser user = (YahooUser) it.next();
- Contact contact;
- OperationSetPersistentPresenceYahooImpl presenceOpSet
- = (OperationSetPersistentPresenceYahooImpl) this
- .getParentProvider().getOperationSet(
- OperationSetPersistentPresence.class);
-
- contact = presenceOpSet.findContactByID(user.getId());
-
- if(!participants.containsKey(contact.getDisplayName()))
- {
- participants.put(contact.getDisplayName(), contact);
- }
- }
- }
-
- /**
- * Returns the identifier of this <tt>AdHocChatRoom</tt>.
- *
- * @return a <tt>String</tt> containing the identifier of this
- * <tt>AdHocChatRoom</tt>.
- */
- public String getIdentifier()
- {
- return yahooConference.getName();
- }
-
- /**
- * Returns the number of participants that are currently in this ad-hoc chat
- * room.
- *
- * @return the number of <tt>Contact</tt>s, currently participating in
- * this ad-hoc room.
- */
- public int getParticipantsCount()
- {
- return yahooConference.getMembers().size();
- }
-
- /**
- * Returns the name of this <tt>AdHocChatRoom</tt>.
- *
- * @return a <tt>String</tt> containing the name of this
- * <tt>AdHocChatRoom</tt>.
- */
- public String getName()
- {
- return yahooConference.getName();
- }
-
- /**
- * Returns the protocol provider service that created us.
- *
- * @return the protocol provider service that created us.
- */
- public ProtocolProviderService getParentProvider()
- {
- return provider;
- }
-
- /**
- * Returns the local user's nickname in the context of this chat room or
- * <tt>null</tt> if not currently joined.
- *
- * @return the nickname currently being used by the local user in the
- * context of the local ad-hoc chat room.
- */
-
- public String getUserNickname()
- {
- if(nickname == null)
- nickname = provider.getYahooSession().getLoginIdentity().getId();
-
- return nickname;
- }
-
- /**
- * Invites another user to this room. If we're not joined nothing will
- * happen.
- *
- * @param userAddress The identifier of the contact (email address or yahoo
- * id)
- * @param reason The invite reason, which is send to the invitee.
- */
- public void invite(String userAddress, String reason)
- {
- try
- {
- provider.getYahooSession().extendConference(yahooConference,
- userAddress, reason);
- }
- catch (IOException ioe)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to invite the user: " + userAddress
- + " Error: " + ioe);
- }
- }
-
- /**
- * Indicates whether or not this chat room is corresponding to a server
- * channel. Note: Returns always <code>false</code>.
- *
- * @return Always <code>false</code> since system chat room can't be joined
- * with current yahoo library.
- */
- public boolean isSystem()
- {
- return false;
- }
-
- /**
- * Joins this chat room with the nickname of the local user so that the user
- * would start receiving events and messages for it.
- *
- * @throws OperationFailedException with the corresponding code if an error
- * occurs while joining the room.
- */
- public void join() throws OperationFailedException
- {
- this.nickname = provider.getAccountID().getUserID();
- try
- {
- provider.getYahooSession().acceptConferenceInvite(yahooConference);
-
- // We don't specify a reason.
- opSetMuc.fireLocalUserPresenceEvent(this,
- LocalUserAdHocChatRoomPresenceChangeEvent.LOCAL_USER_JOINED,
- null);
- }
- catch (Exception e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Couldn't join the chat room: "
- + yahooConference.getName() + e);
- }
- }
-
- /**
- * Leave this chat room. Once this method is called, the user won't be
- * listed as a member of the chat room any more and no further chat events
- * will be delivered. Depending on the underlying protocol and
- * implementation leave() might cause the room to be destroyed if it has
- * been created by the local user.
- */
- public void leave()
- {
- try
- {
- provider.getYahooSession().leaveConference(yahooConference);
-
- Iterator< Map.Entry<String, Contact>> membersSet
- = participants.entrySet().iterator();
-
- while (membersSet.hasNext())
- {
- Map.Entry<String, Contact> memberEntry = membersSet.next();
- Contact participant = memberEntry.getValue();
-
- fireParticipantPresenceEvent(participant,
- AdHocChatRoomParticipantPresenceChangeEvent.CONTACT_LEFT,
- "Local user has left the chat room.");
- }
- }
- catch (IOException ioe)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to leave the chat room: "
- + yahooConference.getName() + " Error: " + ioe);
- }
-
- participants.clear();
- }
-
- /**
- * Sends the <tt>message</tt> to the destination indicated by the
- * <tt>to</tt> contact.
- *
- * @param message The <tt>Message</tt> to send.
- * @throws OperationFailedException if the underlying stack is not
- * registered or initialized or if the chat room is not joined.
- */
- public void sendMessage(Message message) throws OperationFailedException
- {
- assertConnected();
-
- try
- {
- provider.getYahooSession().sendConferenceMessage(yahooConference,
- message.getContent());
-
- AdHocChatRoomMessageDeliveredEvent msgDeliveredEvt
- = new AdHocChatRoomMessageDeliveredEvent(
- this,
- new Date(),
- message,
- ChatRoomMessageDeliveredEvent.CONVERSATION_MESSAGE_DELIVERED);
-
- fireMessageEvent(msgDeliveredEvt);
- }
- catch (Exception e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to send a conference message.");
- }
- }
-
- /**
- * Notifies all interested listeners that a
- * <tt>AdHocChatRoomMessageDeliveredEvent</tt>,
- * <tt>AdHocChatRoomMessageReceivedEvent</tt> or a
- * <tt>AdHocChatRoomMessageDeliveryFailedEvent</tt> has been fired.
- * @param evt The specific event
- */
- public void fireMessageEvent(EventObject evt)
- {
- Iterator<AdHocChatRoomMessageListener> listeners = null;
- synchronized (messageListeners)
- {
- listeners = new ArrayList<AdHocChatRoomMessageListener>(
- messageListeners).iterator();
- }
-
- while (listeners.hasNext())
- {
- AdHocChatRoomMessageListener listener = listeners.next();
-
- if (evt instanceof AdHocChatRoomMessageDeliveredEvent)
- {
- listener.messageDelivered(
- (AdHocChatRoomMessageDeliveredEvent) evt);
- }
- else if (evt instanceof AdHocChatRoomMessageReceivedEvent)
- {
- listener.messageReceived(
- (AdHocChatRoomMessageReceivedEvent) evt);
- }
- else if (evt instanceof AdHocChatRoomMessageDeliveryFailedEvent)
- {
- listener.messageDeliveryFailed(
- (AdHocChatRoomMessageDeliveryFailedEvent) evt);
- }
- }
- }
-
- /**
- * Creates the corresponding AdHocChatRoomParticipantPresenceChangeEvent and
- * notifies all <tt>AdHocChatRoomParticipantPresenceListener</tt>s that a
- * Contact has joined or left this <tt>AdHocChatRoom</tt>.
- *
- * @param participant the <tt>Contact</tt> that this
- * @param eventID the identifier of the event
- * @param eventReason the reason of the event
- */
- public void fireParticipantPresenceEvent(Contact participant, String eventID,
- String eventReason)
- {
- AdHocChatRoomParticipantPresenceChangeEvent evt
- = new AdHocChatRoomParticipantPresenceChangeEvent(this,
- participant,
- eventID,
- eventReason);
-
- if (logger.isTraceEnabled())
- logger.trace("Will dispatch the following ChatRoom event: " + evt);
-
- Iterator<AdHocChatRoomParticipantPresenceListener> listeners = null;
- synchronized (memberListeners)
- {
- listeners = new ArrayList<AdHocChatRoomParticipantPresenceListener>
- (memberListeners).iterator();
- }
-
- while (listeners.hasNext())
- {
- AdHocChatRoomParticipantPresenceListener listener = listeners.next();
-
- listener.participantPresenceChanged(evt);
- }
- }
-
- /**
- * Finds the participant of this ad-hoc chat room corresponding to the
- * given address.
- *
- * @param address the address to search for.
- * @return the participant of this chat room corresponding to the given
- * nick name.
- */
- public Contact findParticipantForAddress(String address)
- {
- Iterator<Contact> participantsIter
- = this.participants.values().iterator();
-
- while (participantsIter.hasNext())
- {
- Contact contact = participantsIter.next();
-
- if (contact.getAddress().equals(address))
- {
- return contact;
- }
- }
-
- return null;
- }
-
- /**
- * Removes the specified ad-hoc chat room participant from the participants
- * list of this ad-hoc chat room.
- * @param participant The member, who should be removed from the ad-hoc chat room
- * participants list.
- */
- public void removeChatRoomParticipant(Contact participant)
- {
- if(participant == null)
- return;
-
- participants.remove(participant.getDisplayName());
-
- fireParticipantPresenceEvent(participant,
- AdHocChatRoomParticipantPresenceChangeEvent.CONTACT_LEFT, null);
- }
-
- /**
- * Adds a participant to the ad-hoc chat room participant list.
- * @param participant The participant, who should be added to the ad-hoc
- * chat room participant list.
- */
- public void addChatRoomParticipant(Contact participant)
- {
- if (participant == null)
- return;
-
- if (!participants.containsKey(participant.getDisplayName()))
- {
- participants.put(participant.getDisplayName(), participant);
-
- fireParticipantPresenceEvent(participant,
- AdHocChatRoomParticipantPresenceChangeEvent.CONTACT_JOINED,
- null);
- }
- }
-
- /**
- * Returns the yahoo conference model of this chat room.
- * @return The yahoo conference.
- */
- public YahooConference getYahooConference()
- {
- return yahooConference;
- }
-
- /**
- * Utility method throwing an exception if the stack is not properly
- * initialized.
- * @throws java.lang.IllegalStateException if the underlying stack is
- * not registered and initialized.
- */
- private void assertConnected() throws IllegalStateException
- {
- if (provider == null)
- throw new IllegalStateException(
- "The provider must be non-null and signed on the "
- +"service before being able to communicate.");
- if (!provider.isRegistered())
- throw new IllegalStateException(
- "The provider must be signed on the service before "
- +"being able to communicate.");
- }
-
- /**
- * Determines whether this chat room should be stored in the configuration
- * file or not. If the chat room is persistent it still will be shown after a
- * restart in the chat room list. A non-persistent chat room will be only in
- * the chat room list until the the program is running.
- *
- * @return true if this chat room is persistent, false otherwise
- */
- public boolean isPersistent()
- {
- return false;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java
deleted file mode 100644
index 1f73aac..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactGroupYahooImpl.java
+++ /dev/null
@@ -1,445 +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.yahoo;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import ymsg.network.*;
-
-/**
- * The Yahoo implementation of the ContactGroup interface. Intances of this class
- * (contrary to <tt>RootContactGroupYahooImpl</tt>) may only contain buddies
- * and cannot have sub groups. Note that instances of this class only use the
- * corresponding smack source group for reading their names and only
- * initially fill their <tt>buddies</tt> <tt>java.util.List</tt> with
- * the ContactYahooImpl objects corresponding to those contained in the source
- * group at the moment it is being created. They would, however, never try to
- * sync or update their contents ulteriorly. This would have to be done through
- * the addContact()/removeContact() methods.
- * The content of buddies is created on creating of the group and when the smack
- * source group is changed.
- *
- * @author Damian Minkov
- * @author Emil Ivov
- */
-public class ContactGroupYahooImpl
- extends AbstractContactGroupYahooImpl
-{
- private final Map<String, Contact> buddies
- = new Hashtable<String, Contact>();
-
- private boolean isResolved = false;
-
- /**
- * The Yahoo Group corresponding to this contact group.
- */
- private YahooGroup yahooGroup = null;
-
- /**
- * a list that would always remain empty. We only use it so that we're able
- * to extract empty iterators
- */
- private final List<ContactGroup> dummyGroupsList
- = new LinkedList<ContactGroup>();
-
- private String tempId = null;
-
- private final ServerStoredContactListYahooImpl ssclCallback;
-
- /**
- * Creates an Yahoo group using the specified <tt>YahooGroup</tt> as
- * a source. The newly created group will always return the name of the
- * underlying RosterGroup and would thus automatically adapt to changes.
- * It would, however, not receive or try to poll for modifications of the
- * buddies it contains and would therefore have to be updated manually by
- * ServerStoredContactListImpl update will only be done if source group
- * is changed.
-
- * @param yahooGroup the Yahoo Group correspoinding to the group
- * @param groupMembers the group members that we should add to the group.
- * @param ssclCallback a callback to the server stored contact list
- * we're creating.
- * @param isResolved a boolean indicating whether or not the group has been
- * resolved against the server.
- */
- ContactGroupYahooImpl(
- YahooGroup yahooGroup,
- Vector<YahooUser> groupMembers,
- ServerStoredContactListYahooImpl ssclCallback,
- boolean isResolved)
- {
- this.yahooGroup = yahooGroup;
- this.isResolved = isResolved;
- this.ssclCallback = ssclCallback;
-
- for (YahooUser yahooUser : groupMembers)
- {
- //only add the contact if it doesn't already exist in some other
- //group. this would be necessary if Yahoo! one day start allowing
- //the same contact in more than one group, which is not quite
- //unlikely since most of the other protocols do it.
- if(ssclCallback.findContactByYahooUser(yahooUser) != null)
- {
- continue;
- }
-
-
- addContact(
- new ContactYahooImpl(yahooUser,ssclCallback, true, true));
- }
- }
-
- ContactGroupYahooImpl( String id,
- ServerStoredContactListYahooImpl ssclCallback)
- {
- this.tempId = id;
- this.isResolved = false;
- this.ssclCallback = ssclCallback;
- }
-
-
- /**
- * 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 buddies.size();
- }
-
- /**
- * Returns a reference to the root group which in Yahoo is the parent of
- * any other group since the protocol does not support subgroups.
- * @return a reference to the root group.
- */
- public ContactGroup getParentContactGroup()
- {
- return ssclCallback.getRootGroup();
- }
-
- /**
- * Adds the specified contact to the end of this group.
- * @param contact the new contact to add to this group
- */
- void addContact(ContactYahooImpl contact)
- {
- buddies.put(contact.getAddress().toLowerCase(), contact);
- }
-
-
- /**
- * Removes the specified contact from this contact group
- * @param contact the contact to remove.
- */
- void removeContact(ContactYahooImpl contact)
- {
- buddies.remove(contact.getAddress().toLowerCase());
- }
-
- /**
- * 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>. In case the group doesn't contain any
- * memebers it will return an empty iterator.
- */
- public Iterator<Contact> contacts()
- {
- return buddies.values().iterator();
- }
-
- /**
- * 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)
- {
- return this.findContact(id);
- }
-
- /**
- * Returns the name of this group.
- * @return a String containing the name of this group.
- */
- public String getGroupName()
- {
- if(isResolved)
- return ServerStoredContactListYahooImpl
- .replaceIllegalChars(yahooGroup.getName());
- else
- return tempId;
- }
-
- /**
- * Determines whether the group may contain subgroups or not.
- *
- * @return always false since only the root group may contain subgroups.
- */
- public boolean canContainSubgroups()
- {
- return false;
- }
-
- /**
- * Returns the subgroup with the specified index (i.e. always null since
- * this group may not contain subgroups).
- *
- * @param index the index of the <tt>ContactGroup</tt> to retrieve.
- * @return always null
- */
- public ContactGroup getGroup(int index)
- {
- return null;
- }
-
- /**
- * 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)
- {
- return null;
- }
-
- /**
- * Returns an empty iterator. Subgroups may only be present in the root
- * group.
- *
- * @return an empty iterator
- */
- public Iterator<ContactGroup> subgroups()
- {
- return dummyGroupsList.iterator();
- }
-
- /**
- * Returns the number of subgroups contained by this group, which is
- * always 0 since sub groups in the protocol may only be contained
- * by the root group - <tt>RootContactGroupImpl</tt>.
- * @return a 0 int.
- */
- public int countSubgroups()
- {
- return 0;
- }
-
- /**
- * Returns a hash code value for the object, which is actually the hashcode
- * value of the groupname.
- *
- * @return a hash code value for this ContactGroup.
- */
- @Override
- public int hashCode()
- {
- return getGroupName().hashCode();
- }
-
- /**
- * Indicates whether some other object is "equal to" this group.
- *
- * @param obj the reference object with which to compare.
- * @return <tt>true</tt> if this object is the same as the obj
- * argument; <tt>false</tt> otherwise.
- */
- @Override
- public boolean equals(Object obj)
- {
- if( obj == this )
- return true;
-
- if (obj == null
- || !(obj instanceof ContactGroupYahooImpl) )
- return false;
-
- if(!((ContactGroup)obj).getGroupName().equals(getGroupName()))
- return false;
-
- if(getProtocolProvider() != ((ContactGroup)obj).getProtocolProvider())
- return false;
-
- //since Yahoo does not support having two groups with the same name
- // at this point we could bravely state that the groups are the same
- // and not bother to compare buddies. (gotta check that though)
- return true;
- }
-
- /**
- * Returns the protocol provider that this group belongs to.
- * @return a reference to the ProtocolProviderService instance that this
- * ContactGroup belongs to.
- */
- public ProtocolProviderService getProtocolProvider()
- {
- return this.ssclCallback.getParentProvider();
- }
-
- /**
- * Returns a string representation of this group, in the form
- * YahooGroup.GroupName[size]{ buddy1.toString(), buddy2.toString(), ...}.
- * @return a String representation of the object.
- */
- @Override
- public String toString()
- {
- StringBuffer buff = new StringBuffer("YahooGroup.");
- buff.append(getGroupName());
- buff.append(", childContacts="+countContacts()+":[");
-
- Iterator<Contact> contacts = contacts();
- while (contacts.hasNext())
- {
- Contact contact = contacts.next();
- buff.append(contact.toString());
- if(contacts.hasNext())
- buff.append(", ");
- }
- return buff.append("]").toString();
- }
-
- /**
- * Returns the contact encapsulating with the spcieified name or
- * null if no such contact was found.
- *
- * @param id the id for the contact we're looking for.
- * @return the <tt>ContactYahooImpl</tt> corresponding to the specified
- * screnname or null if no such contact existed.
- */
- ContactYahooImpl findContact(String id)
- {
- if(id == null)
- return null;
- return (ContactYahooImpl)buddies.get(id.toLowerCase());
- }
-
- /**
- * 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 true;
- }
-
- /**
- * 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 group has been resolved against
- * the server. Unresolved group 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 and groups to their corresponding on-line
- * buddies.
- * @return true if the contact has been resolved (mapped against a buddy)
- * and false otherwise.
- */
- public boolean isResolved()
- {
- return isResolved;
- }
-
- /**
- * Resolve this contact group against the specified group
- * @param yahooGroup the server stored group
- */
- @SuppressWarnings("unchecked") //jymsg legacy code
- void setResolved(YahooGroup yahooGroup)
- {
- if(isResolved)
- return;
-
- this.isResolved = true;
-
- this.yahooGroup = yahooGroup;
-
- Vector<YahooUser> contacts = yahooGroup.getMembers();
- for (YahooUser item : contacts)
- {
- ContactYahooImpl contact =
- ssclCallback.findContactById(item.getId());
- if(contact != null)
- {
- contact.setResolved(item);
-
- ssclCallback.fireContactResolved(this, contact);
- }
- else
- {
- ContactYahooImpl newContact =
- new ContactYahooImpl(item, ssclCallback, true, true);
- addContact(newContact);
-
- ssclCallback.fireContactAdded(this, newContact);
- }
- }
- }
-
- /**
- * Returns a <tt>String</tt> that uniquely represnets the group. In this we
- * use the name of the group as an identifier. This may cause problems
- * though, in clase the name is changed by some other application between
- * consecutive runs of the sip-communicator.
- *
- * @return a String representing this group in a unique and persistent
- * way.
- */
- public String getUID()
- {
- return getGroupName();
- }
-
- /**
- * The source group we are encapsulating
- * @return YahooGroup
- */
- YahooGroup getSourceGroup()
- {
- return yahooGroup;
- }
-
- /**
- * Change the source group
- * change the buddies
- *
- * @param newGroup YahooGroup
- */
- void setSourceGroup(YahooGroup newGroup)
- {
- this.yahooGroup = newGroup;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java
deleted file mode 100644
index b23ee3d..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/ContactYahooImpl.java
+++ /dev/null
@@ -1,397 +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.yahoo;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.yahooconstants.*;
-import net.java.sip.communicator.util.*;
-import ymsg.network.*;
-
-/**
- * The Yahoo implementation of the service.protocol.Contact interface.
- * @author Damian Minkov
- * @author Emil Ivov
- */
-public class ContactYahooImpl
- extends AbstractContact
-{
- private static final Logger logger =
- Logger.getLogger(ContactYahooImpl.class);
-
- private YahooUser contact = null;
- private byte[] image = null;
- private PresenceStatus status = YahooStatusEnum.OFFLINE;
- private ServerStoredContactListYahooImpl ssclCallback = null;
- private boolean isPersistent = false;
- private boolean isResolved = false;
- private boolean isVolatile = false;
-
- private String yahooID = null;
- private String id = null;
-
- private String statusMessage = null;
-
- /**
- * Creates an YahooContactImpl with custom yahooID
- * @param yahooID sets the contact Id if its different from the YahooUser id
- * @param contact the contact object that we will be encapsulating.
- * @param ssclCallback a reference to the ServerStoredContactListImpl
- * instance that created us.
- * @param isPersistent determines whether this contact is persistent or not.
- * @param isResolved specifies whether the contact has been resolved against
- * the server contact list
- */
- ContactYahooImpl(
- String yahooID,
- YahooUser contact,
- ServerStoredContactListYahooImpl ssclCallback,
- boolean isPersistent,
- boolean isResolved)
- {
- this.yahooID = yahooID;
-
- this.contact = contact;
- this.ssclCallback = ssclCallback;
- this.isPersistent = isPersistent;
- this.isResolved = isResolved;
-
- if(contact != null)
- id = contact.getId();
- else if(yahooID != null)
- id = YahooSession.getYahooUserID(yahooID);
- }
-
- /**
- * Creates an YahooContactImpl
- * @param contact the contact object that we will be encapsulating.
- * @param ssclCallback a reference to the ServerStoredContactListImpl
- * instance that created us.
- * @param isPersistent determines whether this contact is persistent or not.
- * @param isResolved specifies whether the contact has been resolved against
- * the server contact list
- */
- ContactYahooImpl(
- YahooUser contact,
- ServerStoredContactListYahooImpl ssclCallback,
- boolean isPersistent,
- boolean isResolved)
- {
- this(null, contact, ssclCallback, isPersistent, isResolved);
- }
-
- /**
- * Creates volatile or unresolved contact
- */
- ContactYahooImpl(
- String id,
- ServerStoredContactListYahooImpl ssclCallback,
- boolean isResolved,
- boolean isPersistent,
- boolean isVolatile)
- {
- this.yahooID = id;
- this.ssclCallback = ssclCallback;
- this.isPersistent = isPersistent;
- this.isResolved = isResolved;
- this.isVolatile = isVolatile;
-
- if(id != null)
- this.id = YahooSession.getYahooUserID(yahooID);
- }
-
- /**
- * Returns the Yahoo Userid of this contact
- * @return the Yahoo Userid of this contact
- */
- public String getAddress()
- {
- // if the contact is volatile or with custom id return it
- if(yahooID != null)
- return yahooID;
- // otherwise return the supplied contact id
- else
- return contact.getId();
- }
-
- /**
- * Returns the custom yahooID if set
- */
- String getYahooID()
- {
- return yahooID;
- }
-
- /**
- * Returns the contact Id.
- * If contact missing the yahooID without @yahoo.com part is returned
- */
- String getID()
- {
- return id;
- }
-
- /**
- * Returns whether the contact is volatile.
- */
- boolean isVolatile()
- {
- return isVolatile;
- }
-
- /**
- * Returns an avatar if one is already present or <tt>null</tt> in case it
- * is not in which case it the method also queues the contact for image
- * updates.
- *
- * @return the avatar of this contact or <tt>null</tt> if no avatar is
- * currently available.
- */
- public byte[] getImage()
- {
- return getImage(true);
- }
-
- /**
- * Returns a reference to the image assigned to this contact. If no image
- * is present and the retrieveIfNecessary flag is true, we schedule the
- * image for retrieval from the server.
- *
- * @param retrieveIfNecessary specifies whether the method should queue
- * this contact for avatar update from the server.
- *
- * @return a reference to the image currently stored by this contact.
- */
- public byte[] getImage(boolean retrieveIfNecessary)
- {
- try
- {
- if(retrieveIfNecessary)
- {
- if(ssclCallback.getParentProvider() == null
- || !ssclCallback.getParentProvider().isRegistered())
- {
- throw new IllegalStateException(
- "The provider must be signed on the service before "
- +"being able to communicate.");
- }
-
- YahooSession ses = ssclCallback.getParentProvider().
- getYahooSession();
- if(image == null && ses != null)
- ses.requestPicture(id);
- }
- }
- catch (Exception e)
- {
- if (logger.isInfoEnabled())
- logger.info("Error requesting image!", e);
- }
-
- if(logger.isDebugEnabled())
- logger.debug("returning picture " + image);
-
- return image;
- }
-
- /**
- * Used to set the image of the contact if it is updated
- *
- * @param image a photo/avatar associated with this contact.
- */
- protected void setImage(byte[] image)
- {
- if (logger.isInfoEnabled())
- logger.info("setting image " + image);
-
- this.image = image;
- }
-
- /**
- * 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("YahooContact[ id=");
- buff.append(getAddress()).append("]");
-
- return buff.toString();
- }
-
- /**
- * Sets the status that this contact is currently in. The method is to
- * only be called as a result of a status update received from the server.
- *
- * @param status the YahooStatusEnum that this contact is currently in.
- */
- void updatePresenceStatus(PresenceStatus status)
- {
- this.status = status;
- }
-
- /**
- * Returns the status of the contact as per the last status update we've
- * received for it. Note that this method is not to perform any network
- * operations and will simply return the status received in the last
- * status update message. If you want a reliable way of retrieving someone's
- * status, you should use the <tt>queryContactStatus()</tt> method in
- * <tt>OperationSetPresence</tt>.
- * @return the PresenceStatus that we've received in the last status update
- * pertaining to this contact.
- */
- public PresenceStatus getPresenceStatus()
- {
- return status;
- }
-
- /**
- * Returns a String that could be used by any user interacting modules for
- * referring to this contact. An alias is not necessarily unique but is
- * often more human readable than an address (or id).
- * @return a String that can be used for referring to this contact when
- * interacting with the user.
- */
- public String getDisplayName()
- {
- return getAddress();
- }
-
- /**
- * Returns a reference to the contact group that this contact is currently
- * a child of or null if the underlying protocol does not suppord persistent
- * presence.
- * @return a reference to the contact group that this contact is currently
- * a child of or null if the underlying protocol does not suppord persistent
- * presence.
- */
- public ContactGroup getParentContactGroup()
- {
- return ssclCallback.findContactGroup(this);
- }
-
-
- /**
- * Returns a reference to the protocol provider that created the contact.
- * @return a refererence to an instance of the ProtocolProviderService
- */
- public ProtocolProviderService getProtocolProvider()
- {
- return ssclCallback.getParentProvider();
- }
-
- /**
- * 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 this contact is to be considered persistent or not. The
- * method is to be used _only_ when a non-persistent contact has been added
- * to the contact list and its encapsulated VolatileBuddy has been repalced
- * with a standard buddy.
- * @param persistent true if the buddy is to be considered persistent and
- * false for volatile.
- */
- void setPersistent(boolean persistent)
- {
- this.isPersistent = persistent;
- }
-
- /**
- * Resolve this contact against the given entry
- * @param entry the server stored entry
- */
- void setResolved(YahooUser entry)
- {
- if(isResolved)
- return;
-
- this.isResolved = true;
- contact = entry;
- isVolatile = false;
- }
-
- /**
- * Returns the persistent data
- * @return the persistent data
- */
- 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;
- }
-
- public void setPersistentData(String persistentData)
- {
- }
-
- /**
- * Get source contact
- * @return YahooContact
- */
- YahooUser getSourceContact()
- {
- return contact;
- }
-
- /**
- * Return the current status message of this contact.
- *
- * @return the current status message
- */
- public String getStatusMessage()
- {
- return statusMessage;
- }
-
- /**
- * Sets the current status message for this contact
- * @param statusMessage the message
- */
- protected void setStatusMessage(String statusMessage)
- {
- this.statusMessage = statusMessage;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/FileTransferImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/FileTransferImpl.java
deleted file mode 100644
index 3f42079..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/FileTransferImpl.java
+++ /dev/null
@@ -1,117 +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.yahoo;
-
-import java.io.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * The Filetransfer imeplementation for yahoo.
- * @author Damian Minkov
- */
-public class FileTransferImpl
- extends AbstractFileTransfer
-
-{
- private ProtocolProviderServiceYahooImpl yahooProvider;
- private String id = null;
- private Contact contact = null;
- private File file = null;
- private int direction = -1;
- private long transferedBytes;
-
- public FileTransferImpl(ProtocolProviderServiceYahooImpl yahooProvider,
- String id, Contact contact, File file, int direction)
- {
- this.yahooProvider = yahooProvider;
- this.id = id;
- this.contact = contact;
- this.file = file;
- this.direction = direction;
- }
-
- /**
- * Cancels this file transfer. When this method is called transfer should
- * be interrupted.
- */
- @Override
- public void cancel()
- {
- yahooProvider.getYahooSession().cancelRunningFileTransfer(id);
- }
-
- /**
- * Returns the number of bytes already transfered through this file transfer.
- *
- * @return the number of bytes already transfered through this file transfer
- */
- @Override
- public long getTransferedBytes()
- {
- return transferedBytes;
- }
-
- /**
- * Uniquie ID that is identifying the FileTransfer
- * if the request has been accepted.
- *
- * @return the id.
- */
- public String getID()
- {
- return id;
- }
-
- /**
- * The file transfer direction.
- * @return returns the direction of the file transfer : IN or OUT.
- */
- public int getDirection()
- {
- return direction;
- }
-
- /**
- * Returns the file that is transfered.
- *
- * @return the file
- */
- public File getLocalFile()
- {
- return file;
- }
-
- /**
- * Returns the contact that we are transfering files with.
- * @return the contact.
- */
- public Contact getContact()
- {
- return contact;
- }
-
- /**
- * @param transferedBytes the transferedBytes to set
- */
- public void setTransferedBytes(long transferedBytes)
- {
- this.transferedBytes = transferedBytes;
- }
-
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/IncomingFileTransferRequestYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/IncomingFileTransferRequestYahooImpl.java
deleted file mode 100644
index 25561d0..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/IncomingFileTransferRequestYahooImpl.java
+++ /dev/null
@@ -1,190 +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.yahoo;
-
-import java.io.*;
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-
-/**
- * Implementation of the incoming file transfer request.
- *
- * @author Damian Minkov
- */
-public class IncomingFileTransferRequestYahooImpl
- implements IncomingFileTransferRequest
-{
- private String id;
-
- /**
- * The yahoo provider.
- */
- private ProtocolProviderServiceYahooImpl yahooProvider;
-
- private final OperationSetFileTransferYahooImpl fileTransferOpSet;
-
- private Contact sender;
-
- private Date date;
-
- private String fileName;
-
- private long fileSize;
-
- public IncomingFileTransferRequestYahooImpl(
- ProtocolProviderServiceYahooImpl yahooProvider,
- OperationSetFileTransferYahooImpl fileTransferOpSet,
- Contact sender,
- Date date,
- String fileName,
- String fileSize,
- String id)
- {
- this.yahooProvider = yahooProvider;
- this.fileTransferOpSet = fileTransferOpSet;
- this.sender = sender;
- this.date = date;
- this.fileName = fileName;
-
- try
- {
- this.fileSize = Long.valueOf(fileSize);
- }
- catch (NumberFormatException e)
- {}
-
- this.id = id;
- }
-
- /**
- * Unique ID that is identifying the request and then the FileTransfer
- * if the request has been accepted.
- *
- * @return the id.
- */
- public String getID()
- {
- return id;
- }
-
- /**
- * Returns a String that represents the name of the file that is being
- * received.
- * If there is no name, returns null.
- * @return a String that represents the name of the file
- */
- public String getFileName()
- {
- return fileName;
- }
-
- /**
- * Returns a String that represents the description of the file that is
- * being received.
- * If there is no description available, returns null.
- *
- * @return a String that represents the description of the file
- */
- public String getFileDescription()
- {
- return "";
- }
-
- /**
- * Returns a long that represents the size of the file that is being
- * received.
- * If there is no file size available, returns null.
- *
- * @return a long that represents the size of the file
- */
- public long getFileSize()
- {
- return fileSize;
- }
-
- /**
- * Returns a String that represents the name of the sender of the file
- * being received.
- * If there is no sender name available, returns null.
- *
- * @return a String that represents the name of the sender
- */
- public Contact getSender()
- {
- return sender;
- }
-
- /**
- * Function called to accept and receive the file.
- *
- * @param file the file to accept
- * @return the <tt>FileTransfer</tt> object managing the transfer
- */
- public FileTransfer acceptFile(File file)
- {
- AbstractFileTransfer incomingTransfer = null;
-
- incomingTransfer =
- new FileTransferImpl(yahooProvider,
- id, sender, file, FileTransfer.IN);
-
- yahooProvider.getYahooSession().fileTransferAccept(id, file);
-
- FileTransferCreatedEvent event
- = new FileTransferCreatedEvent(incomingTransfer, new Date());
-
- fileTransferOpSet.fireFileTransferCreated(event);
-
- incomingTransfer.fireStatusChangeEvent(
- FileTransferStatusChangeEvent.PREPARING);
-
- return incomingTransfer;
- }
-
- /**
- * Function called to refuse the file.
- */
- public void rejectFile()
- {
- yahooProvider.getYahooSession().fileTransferReject(id);
-
- fileTransferOpSet.fireFileTransferRequestRejected(
- new FileTransferRequestEvent(
- fileTransferOpSet, this, this.getDate()));
- }
-
- /**
- * @return the date
- */
- public Date getDate()
- {
- return date;
- }
-
- /**
- * Returns the thumbnail contained in this request.
- *
- * @return the thumbnail contained in this request
- */
- public byte[] getThumbnail()
- {
- return null;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java
deleted file mode 100644
index e35a3c1..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/MessageYahooImpl.java
+++ /dev/null
@@ -1,48 +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.yahoo;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * A simple implementation of the <tt>Message</tt> interface. Right now the
- * message only supports test contents and no binary data.
- *
- * @author Damian Minkov
- * @author Lubomir Marinov
- */
-public class MessageYahooImpl
- extends AbstractMessage
-{
-
- /**
- * Creates an instance of this Message with the specified parameters.
- *
- * @param content the text content of the message.
- * @param contentType a MIME string indicating the content type of the
- * <tt>content</tt> String.
- * @param contentEncoding a MIME String indicating the content encoding of
- * the <tt>content</tt> String.
- * @param subject the subject of the message or null for empty.
- */
- public MessageYahooImpl(String content, String contentType,
- String contentEncoding, String subject)
- {
- super(content, contentType, contentEncoding, subject);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetAdHocMultiUserChatYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetAdHocMultiUserChatYahooImpl.java
deleted file mode 100644
index dd5f73f..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetAdHocMultiUserChatYahooImpl.java
+++ /dev/null
@@ -1,714 +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.yahoo;
-
-import java.io.*;
-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 ymsg.network.*;
-import ymsg.network.event.*;
-
-/**
- * A Yahoo implementation of the ad-hoc multi user chat operation set.
- *
- * @author Rupert Burchardi
- * @author Valentin Martinet
- * @author Yana Stamcheva
- */
-public class OperationSetAdHocMultiUserChatYahooImpl
-implements OperationSetAdHocMultiUserChat
-{
- private static final Logger logger =
- Logger.getLogger(OperationSetAdHocMultiUserChatYahooImpl.class);
-
- /**
- * A list of listeners subscribed for invitations multi user chat events.
- */
- private final List<AdHocChatRoomInvitationListener> invitationListeners
- = new Vector<AdHocChatRoomInvitationListener>();
-
- /**
- * A list of listeners subscribed for events indicating rejection of a multi
- * user chat invitation sent by us.
- */
- private final List<AdHocChatRoomInvitationRejectionListener>
- invitationRejectionListeners
- = new Vector<AdHocChatRoomInvitationRejectionListener>();
-
- /**
- * Listeners that will be notified of changes in our status in the room such
- * as us being kicked, banned, or granted admin permissions.
- */
- private final List<LocalUserAdHocChatRoomPresenceListener> presenceListeners
- = new Vector<LocalUserAdHocChatRoomPresenceListener>();
-
- /**
- * A list of the rooms that are currently open by this account.
- */
- private final Hashtable<String, AdHocChatRoomYahooImpl> chatRoomCache
- = new Hashtable<String, AdHocChatRoomYahooImpl>();
-
- /**
- * The currently valid Yahoo protocol provider service implementation.
- */
- private final ProtocolProviderServiceYahooImpl yahooProvider;
-
- /**
- * The operation set for the basic instant messaging, provides some message
- * format functions.
- */
- private final OperationSetBasicInstantMessagingYahooImpl opSetBasic;
-
- /**
- * Instantiates the user operation set with a currently valid instance of
- * the Yahoo protocol provider.
- *
- * @param yahooProvider a currently valid instance of
- * ProtocolProviderServiceYahooImpl.
- */
- OperationSetAdHocMultiUserChatYahooImpl(
- ProtocolProviderServiceYahooImpl yahooProvider)
- {
- this.yahooProvider = yahooProvider;
-
- yahooProvider
- .addRegistrationStateChangeListener(new RegistrationStateListener());
-
- opSetBasic =
- (OperationSetBasicInstantMessagingYahooImpl) yahooProvider
- .getOperationSet(OperationSetBasicInstantMessaging.class);
- }
-
- /**
- * Adds a listener to invitation notifications.
- *
- * @param listener An invitation listener.
- */
- public void addInvitationListener(AdHocChatRoomInvitationListener listener)
- {
- synchronized (invitationListeners)
- {
- if (!invitationListeners.contains(listener))
- invitationListeners.add(listener);
- }
- }
-
- /**
- * Removes a listener that was being notified of changes in our status in a
- * room such as us being kicked, banned or dropped.
- *
- * @param listener the <tt>LocalUserAdHocChatRoomPresenceListener</tt>.
- */
- public void removeInvitationListener(
- AdHocChatRoomInvitationListener listener)
- {
- synchronized (invitationListeners)
- {
- invitationListeners.remove(listener);
- }
- }
-
- /**
- * Subscribes <tt>listener</tt> so that it would receive events indicating
- * rejection of a multi user chat invitation that we've sent earlier.
- *
- * @param listener the listener that we'll subscribe for invitation
- * rejection events.
- */
-
- public void addInvitationRejectionListener(
- AdHocChatRoomInvitationRejectionListener listener)
- {
- synchronized (invitationRejectionListeners)
- {
- if (!invitationRejectionListeners.contains(listener))
- invitationRejectionListeners.add(listener);
- }
- }
-
- /**
- * Removes <tt>listener</tt> from the list of invitation listeners
- * registered to receive invitation rejection events.
- *
- * @param listener the invitation listener to remove.
- */
- public void removeInvitationRejectionListener(
- AdHocChatRoomInvitationRejectionListener listener)
- {
- synchronized (invitationRejectionListeners)
- {
- invitationRejectionListeners.remove(listener);
- }
- }
-
- /**
- * Adds a listener that will be notified of changes in our status in a chat
- * room such as us being kicked, banned or dropped.
- *
- * @param listener the <tt>LocalUserAdHocChatRoomPresenceListener</tt>.
- */
- public void addPresenceListener(
- LocalUserAdHocChatRoomPresenceListener listener)
- {
- synchronized (presenceListeners)
- {
- if (!presenceListeners.contains(listener))
- presenceListeners.add(listener);
- }
- }
-
- /**
- * Removes a listener that was being notified of changes in our status in a
- * room such as us being kicked, banned or dropped.
- *
- * @param listener the <tt>LocalUserChatRoomPresenceListener</tt>.
- */
- public void removePresenceListener(
- LocalUserAdHocChatRoomPresenceListener listener)
- {
- synchronized (presenceListeners)
- {
- presenceListeners.remove(listener);
- }
- }
-
- /**
- * Creates a room with the named <tt>roomName</tt> and according to the
- * specified <tt>roomProperties</tt> on the server that this protocol
- * provider is currently connected to. Note the roomProperties also contain
- * users that we like to invite to the chatRoom, this is required in the
- * yahoo protocol.
- *
- * @param roomName the name of the <tt>AdHocChatRoom</tt> to create.
- * @param roomProperties properties specifying how the room should be
- * created.
- *
- * @throws OperationFailedException if the room couldn't be created for some
- * reason (e.g. room already exists; user already joined to an
- * existent room or user has no permissions to create a chat
- * room).
- *
- * @return ChatRoom the chat room that we've just created.
- */
- public AdHocChatRoom createAdHocChatRoom(String roomName,
- Map<String, Object> roomProperties)
- throws OperationFailedException
- {
- return createAdHocChatRoom(roomName, (String[]) null, "");
- }
-
- /**
- * Creates an ad-hoc room with the named <tt>adHocRoomName</tt> and in
- * including to the specified <tt>contacts</tt>. When the method returns the
- * ad-hoc room the local user will have joined it.
- *
- * @return the ad-hoc room that has been just created
- * @param adHocRoomName the name of the room to be created
- * @param contacts the list of contacts ID
- * @param reason the reason for contacts' invitation
- * @throws OperationFailedException if the room couldn't be created for
- * some reason
- */
- public AdHocChatRoom createAdHocChatRoom( String adHocRoomName,
- List<String> contacts,
- String reason)
- throws OperationFailedException
- {
- String[] contactsToInvite = new String[contacts.size()];
- for(int i=0; i<contacts.size(); i++)
- {
- contactsToInvite[i] = contacts.get(i);
- }
- return createAdHocChatRoom(
- adHocRoomName, contactsToInvite, reason);
- }
-
- /**
- * Creates an ad-hoc room with the named <tt>adHocRoomName</tt> and in
- * including to the specified <tt>contacts</tt>. When the method returns the
- * ad-hoc room the local user will have joined it.
- *
- * @param roomName name of the chatroom
- * @param invitedContacts contacts to be invited to this room
- * @param reason reason of this invitation
- * @return AdHocChatRoom the ad-hoc room that has been just created
- * @throws OperationFailedException
- */
- private AdHocChatRoom createAdHocChatRoom(
- String roomName,
- String[] invitedContacts,
- String reason)
- throws OperationFailedException
- {
- if (invitedContacts == null)
- invitedContacts = new String[0];
-
- AdHocChatRoom chatRoom = null;
-
- try
- {
- YahooConference conference =
- yahooProvider.getYahooSession().createConference(
- invitedContacts, // users invited to this conference
- reason, // invite message / topic
- yahooProvider.getYahooSession().getLoginIdentity());
-
- chatRoom = createLocalChatRoomInstance(conference);
- }
- catch (Exception e)
- {
- String errorMessage
- = "Failed to create chat room with name: " + roomName;
-
- if (logger.isDebugEnabled())
- logger.debug(errorMessage, e);
- throw new OperationFailedException(errorMessage,
- OperationFailedException.CHAT_ROOM_NOT_JOINED, e);
- }
- chatRoom.join();
- return chatRoom;
- }
-
- /**
- * Creates a <tt>AdHocChatRoom</tt> instance from the specified Yahoo
- * conference.
- *
- * @param yahooConference The chat room model from the yahoo lib.
- *
- * @return AdHocChatRoom the chat room that we've just created.
- */
- private AdHocChatRoomYahooImpl createLocalChatRoomInstance(
- YahooConference yahooConference)
- {
- synchronized (chatRoomCache)
- {
- AdHocChatRoomYahooImpl newChatRoom
- = new AdHocChatRoomYahooImpl(yahooConference, yahooProvider);
-
- chatRoomCache.put(yahooConference.getName(), newChatRoom);
-
- return newChatRoom;
- }
- }
-
- /**
- * Creates a <tt>AdHocChatRoom</tt> instance (where the inviter is
- * represented by inviterID parameter) from the specified Yahoo conference.
- *
- * @param yahooConference The chat room model from the yahoo lib.
- * @param inviterID inviter's Yahoo ID which has to be added as room member
- *
- * @return AdHocChatRoom the chat room that we've just created.
- */
- private AdHocChatRoomYahooImpl createLocalChatRoomInstance(
- YahooConference yahooConference, String inviterID)
- {
- synchronized (chatRoomCache)
- {
- AdHocChatRoomYahooImpl newChatRoom
- = new AdHocChatRoomYahooImpl(yahooConference, yahooProvider);
-
- OperationSetPersistentPresenceYahooImpl opSetPresence =
- (OperationSetPersistentPresenceYahooImpl) yahooProvider
- .getOperationSet(OperationSetPersistentPresence.class);
-
- newChatRoom.addChatRoomParticipant(
- opSetPresence.findContactByID(inviterID));
- chatRoomCache.put(yahooConference.getName(), newChatRoom);
-
- return newChatRoom;
- }
- }
-
- /**
- * Returns the <tt>AdHocChatRoomYahooImpl</tt> corresponding to the given
- * <tt>conference</tt> if such exists, otherwise returns null.
- *
- * @param conference the <tt>YahooConference</tt>, for which we're searching
- * correspondence
- * @return the <tt>AdHocChatRoomYahooImpl</tt> corresponding to the given
- * <tt>conference</tt> if such exists, otherwise returns null
- */
- private AdHocChatRoomYahooImpl getLocalChatRoomInstance(
- YahooConference conference)
- {
- synchronized (chatRoomCache)
- {
- for (AdHocChatRoomYahooImpl chatRoom : chatRoomCache.values())
- {
- if (chatRoom.getYahooConference().equals(conference))
- return chatRoom;
- }
- }
-
- return null;
- }
-
- /**
- * Informs the sender of an invitation that we decline their invitation.
- *
- * @param invitation the connection to use for sending the rejection.
- * @param rejectReason the reason to reject the given invitation
- */
- public void rejectInvitation(AdHocChatRoomInvitation invitation,
- String rejectReason)
- {
- AdHocChatRoomYahooImpl chatRoom =
- (AdHocChatRoomYahooImpl) invitation.getTargetAdHocChatRoom();
-
- try
- {
- yahooProvider.getYahooSession().declineConferenceInvite(
- chatRoom.getYahooConference(), rejectReason);
- }
- catch (IOException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to reject Invitation: " + e);
- }
- }
-
- /**
- * Delivers a <tt>AdHocChatRoomInvitationReceivedEvent</tt> to all
- * registered <tt>AdHocChatRoomInvitationListener</tt>s.
- *
- * @param targetChatRoom the room that invitation refers to
- * @param inviter the inviter that sent the invitation
- * @param reason the reason why the inviter sent the invitation
- */
- public void fireInvitationEvent(AdHocChatRoom targetChatRoom,
- String inviter, String reason)
- {
- AdHocChatRoomInvitationYahooImpl invitation =
- new AdHocChatRoomInvitationYahooImpl(targetChatRoom, inviter,
- reason);
-
- AdHocChatRoomInvitationReceivedEvent evt =
- new AdHocChatRoomInvitationReceivedEvent(this, invitation,
- new Date(System.currentTimeMillis()));
-
- Iterable<AdHocChatRoomInvitationListener> listeners;
- synchronized (invitationListeners)
- {
- listeners
- = new ArrayList<AdHocChatRoomInvitationListener>(
- invitationListeners);
- }
-
- for (AdHocChatRoomInvitationListener listener : listeners)
- listener.invitationReceived(evt);
- }
-
- /**
- * Delivers a <tt>AdHocChatRoomInvitationRejectedEvent</tt> to all
- * registered <tt>AdHocChatRoomInvitationRejectionListener</tt>s.
- *
- * @param sourceChatRoom the room that invitation refers to
- * @param invitee the name of the invitee that rejected the invitation
- * @param reason the reason of the rejection
- */
- public void fireInvitationRejectedEvent(AdHocChatRoom sourceChatRoom,
- String invitee, String reason)
- {
- AdHocChatRoomInvitationRejectedEvent evt =
- new AdHocChatRoomInvitationRejectedEvent(
- this, sourceChatRoom, invitee,
- reason, new Date(System.currentTimeMillis()));
-
- Iterable<AdHocChatRoomInvitationRejectionListener> listeners;
- synchronized (invitationRejectionListeners)
- {
- listeners
- = new ArrayList<AdHocChatRoomInvitationRejectionListener>(
- invitationRejectionListeners);
- }
-
- for (AdHocChatRoomInvitationRejectionListener listener : listeners)
- listener.invitationRejected(evt);
- }
-
- /**
- * Delivers a <tt>LocalUserAdHocChatRoomPresenceChangeEvent</tt> to all
- * registered <tt>LocalUserAdHocChatRoomPresenceListener</tt>s.
- *
- * @param chatRoom the <tt>ChatRoom</tt> which has been joined, left, etc.
- * @param eventType the type of this event; one of LOCAL_USER_JOINED,
- * LOCAL_USER_LEFT, etc.
- * @param reason the reason
- */
- public void fireLocalUserPresenceEvent(AdHocChatRoom chatRoom,
- String eventType, String reason)
- {
- LocalUserAdHocChatRoomPresenceChangeEvent evt =
- new LocalUserAdHocChatRoomPresenceChangeEvent(
- this, chatRoom, eventType,
- reason);
-
- Iterable<LocalUserAdHocChatRoomPresenceListener> listeners;
- synchronized (presenceListeners)
- {
- listeners =
- new ArrayList<LocalUserAdHocChatRoomPresenceListener>(
- presenceListeners);
- }
-
- for (LocalUserAdHocChatRoomPresenceListener listener : listeners)
- listener.localUserAdHocPresenceChanged(evt);
- }
-
- /**
- * Create a Message instance for sending arbitrary MIME-encoding content.
- *
- * @param content content value
- * @param contentType the MIME-type for <tt>content</tt>
- * @param contentEncoding encoding used for <tt>content</tt>
- * @param subject a <tt>String</tt> subject or <tt>null</tt> for now
- * subject.
- * @return the newly created message.
- * @throws UnsupportedEncodingException missing utf-8 in platform we use.
- */
- private Message createMessage(byte[] content, String contentType,
- String contentEncoding, String subject)
- throws UnsupportedEncodingException
- {
- return new MessageYahooImpl(new String(content, "UTF-8"), contentType,
- contentEncoding, subject);
- }
-
- /**
- * Creates a message by a given message text.
- *
- * @param messageText The message text.
- * @return the newly created message.
- */
- public Message createMessage(String messageText)
- {
- return new MessageYahooImpl(messageText,
- OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE,
- OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING, null);
- }
-
- /**
- * Our listener that will tell us when we're registered to yahoo network.
- *
- */
- private class RegistrationStateListener
- implements RegistrationStateChangeListener
- {
- /**
- * The method is called by a ProtocolProvider implementation whenever a
- * change in the registration state of the corresponding provider had
- * occurred.
- *
- * @param evt ProviderStatusChangeEvent the event describing the status
- * change.
- */
- public void registrationStateChanged(RegistrationStateChangeEvent evt)
- {
- if (evt.getNewState() == RegistrationState.REGISTERED)
- {
- yahooProvider.getYahooSession().addSessionListener(
- new YahooMessageListener());
- }
- }
- }
-
- /**
- * Our group chat message listener, it extends the SessionAdapter from the
- * the Yahoo library.
- *
- */
- private class YahooMessageListener
- extends SessionAdapter
- {
-
- @Override
- public void conferenceInviteDeclinedReceived(SessionConferenceEvent ev)
- {
- if (logger.isDebugEnabled())
- logger.debug("Group Chat invite declined received. "
- + ev.toString());
- try
- {
- AdHocChatRoom chatRoom = getLocalChatRoomInstance(ev.getRoom());
-
- fireInvitationRejectedEvent(chatRoom, ev.getFrom(), ev
- .getMessage());
- }
- catch (Exception e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Error: " + e);
- }
- }
-
- @Override
- public void conferenceInviteReceived(SessionConferenceEvent ev)
- {
- if (logger.isDebugEnabled())
- logger.debug("Conference Invite Received: " + ev.toString());
-
- try
- {
- AdHocChatRoom chatRoom = getLocalChatRoomInstance(ev.getRoom());
-
- if (chatRoom == null)
- {
- chatRoom =
- createLocalChatRoomInstance(ev.getRoom(), ev.getFrom());
-
- fireInvitationEvent(
- chatRoom, ev.getFrom(), ev.getMessage());
- }
-
- }
- catch (Exception e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Error: " + e);
- }
- }
-
- @Override
- public void conferenceLogoffReceived(SessionConferenceEvent ev)
- {
- if (logger.isDebugEnabled())
- logger.debug("Conference Logoff Received: " + ev.toString());
-
- try
- {
- AdHocChatRoomYahooImpl chatRoom
- = getLocalChatRoomInstance(ev.getRoom());
-
- if (chatRoom != null)
- {
- Contact participant =
- chatRoom.findParticipantForAddress(ev.getFrom());
-
- chatRoom.removeChatRoomParticipant(participant);
- }
- }
- catch (Exception e)
- {
- logger
- .debug("Failed to remove a user from the chat room. " + e);
- }
- }
-
- @Override
- public void conferenceLogonReceived(SessionConferenceEvent ev)
- {
- if (logger.isDebugEnabled())
- logger.debug("Conference Logon Received: " + ev.toString());
-
- try
- {
- AdHocChatRoomYahooImpl chatRoom
- = getLocalChatRoomInstance(ev.getRoom());
-
- if (chatRoom != null)
- {
- OperationSetPersistentPresenceYahooImpl presenceOpSet =
- (OperationSetPersistentPresenceYahooImpl) chatRoom
- .getParentProvider().getOperationSet(
- OperationSetPersistentPresence.class);
-
- Contact participant =
- presenceOpSet.findContactByID(ev.getFrom());
-
- chatRoom.addChatRoomParticipant(participant);
- }
- }
- catch (Exception e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to add a user to the chat room. " + e);
- }
- }
-
- @Override
- public void conferenceMessageReceived(SessionConferenceEvent ev)
- {
- if (logger.isDebugEnabled())
- logger.debug("Conference Message Received: " + ev.toString());
-
- try
- {
- String formattedMessage = ev.getMessage();
- if (logger.isDebugEnabled())
- logger.debug("original message received : " + formattedMessage);
-
- formattedMessage = opSetBasic.decodeMessage(formattedMessage);
- if (logger.isDebugEnabled())
- logger.debug("formatted Message : " + formattedMessage);
- // As no indications in the protocol is it html or not. No harm
- // to set all messages html - doesn't affect the appearance of
- // the gui
-
- Message newMessage =
- createMessage(
- formattedMessage.getBytes("UTF-8"),
- OperationSetBasicInstantMessaging.HTML_MIME_TYPE,
- OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING,
- null);
-
- AdHocChatRoomYahooImpl chatRoom =
- getLocalChatRoomInstance(ev.getRoom());
-
- if (chatRoom != null)
- {
- Contact member =
- chatRoom.findParticipantForAddress(ev.getFrom());
-
- AdHocChatRoomMessageReceivedEvent msgReceivedEvent =
- new AdHocChatRoomMessageReceivedEvent(
- chatRoom,
- member,
- new Date(),
- newMessage,
- AdHocChatRoomMessageReceivedEvent
- .CONVERSATION_MESSAGE_RECEIVED);
-
- chatRoom.fireMessageEvent(msgReceivedEvent);
- }
- }
- catch (Exception e)
- {
- logger
- .debug("Error while receiving a multi user chat message: "
- + e);
- }
-
- }
-
- @Override
- public void connectionClosed(SessionEvent ev)
- {
- if (logger.isDebugEnabled())
- logger.debug("Connection Closed: " + ev.toString());
- }
- }
-
- public List<AdHocChatRoom> getAdHocChatRooms()
- {
- return new ArrayList<AdHocChatRoom>(chatRoomCache.values());
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java
deleted file mode 100644
index 84059de..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java
+++ /dev/null
@@ -1,649 +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.yahoo;
-
-import java.io.*;
-import java.util.*;
-import java.util.regex.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-import ymsg.network.event.*;
-import ymsg.support.*;
-
-/**
- * A straightforward implementation of the basic instant messaging operation
- * set.
- *
- * @author Damian Minkov
- * @author Symphorien Wanko
- * @author Keio Kraaner
- */
-public class OperationSetBasicInstantMessagingYahooImpl
- extends AbstractOperationSetBasicInstantMessaging
- implements OperationSetInstantMessageFiltering
-{
- /**
- * Logger for this class
- */
- private static final Logger logger =
- Logger.getLogger(OperationSetBasicInstantMessagingYahooImpl.class);
-
- /**
- * Yahoo has limit of message length. If exceeded
- * message is not delivered and no notification is received for that.
- */
- private static final int MAX_MESSAGE_LENGTH = 800; // 949
-
- /**
- * A regexp that is used to escape some chars in messages.
- */
- private static final Pattern MESSAGE_CHARS_ESCAPE = Pattern.compile("([.()^&$*|])");
-
- /**
- * A list of filters registered for message events.
- */
- private final List<EventFilter> eventFilters = new ArrayList<EventFilter>();
-
- /**
- * The provider that created us.
- */
- private ProtocolProviderServiceYahooImpl yahooProvider = null;
-
- /**
- * Message decoder allows to convert Yahoo formated messages, which can
- * contains some specials characters, to HTML or to plain text.
- */
- private final MessageDecoder messageDecoder = new MessageDecoder();
-
- /**
- * A reference to the persistent presence operation set that we use
- * to match incoming messages to <tt>Contact</tt>s and vice versa.
- */
- private OperationSetPersistentPresenceYahooImpl opSetPersPresence = null;
- private static final Pattern FONT_SIZE_0_PATTERN = Pattern.compile("(<font) (.*) size=\"0\">");
- private static final Pattern FONT_SIZE_INT_PATTERN = Pattern.compile("(<font) (.*) size=\"(\\d+)\">");
-
- /**
- * Creates an instance of this operation set.
- * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt>
- * that created us and that we'll use for retrieving the underlying aim
- * connection.
- */
- OperationSetBasicInstantMessagingYahooImpl(
- ProtocolProviderServiceYahooImpl provider)
- {
- this.yahooProvider = provider;
- provider.addRegistrationStateChangeListener(
- new RegistrationStateListener());
- }
-
- /**
- * Determines wheter 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 wheter 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)
- {
- if(contentType.equals(DEFAULT_MIME_TYPE) ||
- contentType.equals(HTML_MIME_TYPE))
- return true;
- else
- return false;
- }
-
- @Override
- public Message createMessage(String content, String contentType,
- String encoding, String subject)
- {
- return new MessageYahooImpl(content, contentType, encoding, subject);
- }
-
- /**
- * 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 stack is
- * not registered and initialized.
- * @throws IllegalArgumentException if <tt>to</tt> is not an
- * instance of ContactImpl.
- */
- public void sendInstantMessage(Contact to, Message message)
- throws IllegalStateException, IllegalArgumentException
- {
- assertConnected();
-
- if( !(to instanceof ContactYahooImpl) )
- throw new IllegalArgumentException(
- "The specified contact is not a Yahoo contact."
- + to);
-
- try
- {
- String toUserID = ((ContactYahooImpl) to).getID();
-
- MessageDeliveredEvent msgDeliveryPendingEvt =
- new MessageDeliveredEvent(message, to, new Date());
-
- MessageDeliveredEvent[] msgDeliveryPendingEvts = messageDeliveryPendingTransform(msgDeliveryPendingEvt);
-
- if (msgDeliveryPendingEvts == null || msgDeliveryPendingEvts.length == 0)
- return;
-
- for (MessageDeliveredEvent event : msgDeliveryPendingEvts)
- {
- byte[] msgBytesToBeSent =
- event.getSourceMessage().getContent().trim()
- .getBytes("UTF-8");
-
- // split the message in parts with max allowed length
- // and send them all
- do
- {
- if (msgBytesToBeSent.length > MAX_MESSAGE_LENGTH)
- {
- byte[] tmp1 = new byte[MAX_MESSAGE_LENGTH];
- System.arraycopy(msgBytesToBeSent, 0, tmp1, 0,
- MAX_MESSAGE_LENGTH);
-
- byte[] tmp2 =
- new byte[msgBytesToBeSent.length
- - MAX_MESSAGE_LENGTH];
- System.arraycopy(msgBytesToBeSent, MAX_MESSAGE_LENGTH,
- tmp2, 0, tmp2.length);
-
- msgBytesToBeSent = tmp2;
-
- yahooProvider.getYahooSession().sendMessage(toUserID,
- new String(tmp1, "UTF-8"));
- }
- else
- {
- yahooProvider.getYahooSession().sendMessage(toUserID,
- new String(msgBytesToBeSent, "UTF-8"));
- }
-
- MessageDeliveredEvent msgDeliveredEvt =
- new MessageDeliveredEvent(message, to, new Date());
-
- if (msgDeliveredEvt != null)
- fireMessageEvent(msgDeliveredEvt);
- }
- while (msgBytesToBeSent.length > MAX_MESSAGE_LENGTH);
- }
- }
- catch (IOException ex)
- {
- logger.fatal("Cannot Send Message! " + ex.getMessage());
- MessageDeliveryFailedEvent evt =
- new MessageDeliveryFailedEvent(
- message,
- to,
- MessageDeliveryFailedEvent.NETWORK_FAILURE);
-
- if (evt != null)
- fireMessageEvent(evt);
- }
- }
-
- /**
- * Utility method throwing an exception if the stack is not properly
- * initialized.
- * @throws IllegalStateException if the underlying stack is
- * not registered and initialized.
- */
- private void assertConnected() throws IllegalStateException
- {
- if (yahooProvider == null)
- throw new IllegalStateException(
- "The provider must be non-null and signed on the "
- +"service before being able to communicate.");
- if (!yahooProvider.isRegistered())
- throw new IllegalStateException(
- "The provider must be signed on the service before "
- +"being able to communicate.");
- }
-
- /**
- * Our listener that will tell us when we're registered to
- */
- private class RegistrationStateListener
- implements RegistrationStateChangeListener
- {
- /**
- * The method is called by a ProtocolProvider implementation whenver
- * a change in the registration state of the corresponding provider had
- * occurred.
- * @param evt ProviderStatusChangeEvent the event describing the status
- * change.
- */
- public void registrationStateChanged(RegistrationStateChangeEvent evt)
- {
- if (logger.isDebugEnabled())
- logger.debug("The provider changed state from: "
- + evt.getOldState()
- + " to: " + evt.getNewState());
-
- if (evt.getNewState() == RegistrationState.REGISTERED)
- {
- opSetPersPresence =
- (OperationSetPersistentPresenceYahooImpl) yahooProvider
- .getOperationSet(OperationSetPersistentPresence.class);
-
- yahooProvider.getYahooSession().
- addSessionListener(new YahooMessageListener());
- }
- }
- }
-
- /**
- * Delivers the specified event to all registered message listeners.
- * @param evt the <tt>EventObject</tt> that we'd like delivered to all
- * registered message listerners.
- */
- @Override
- protected void fireMessageEvent(EventObject evt)
- {
- // check if this event should be filtered out
- Iterator<EventFilter> filters;
- synchronized (eventFilters)
- {
- filters = new ArrayList<EventFilter>(eventFilters).iterator();
- }
- // return if a filter has filtered this event out
- boolean filtered = false;
- while (filters.hasNext())
- {
- try
- {
- if (filters.next().filterEvent(evt))
- {
- filtered = true;
- }
- }
- catch(Exception exc)
- {
- logger.error("An exception occurred while filtering an event.",
- exc);
- }
- }
-
- if (filtered)
- {
- if (logger.isTraceEnabled())
- logger.trace("Message event filtered.");
- return;
- }
-
- super.fireMessageEvent(evt);
- }
-
- /**
- * This class provides methods to listen for yahoo events which interest us.
- */
- private class YahooMessageListener
- extends SessionAdapter
- {
- /**
- * Overrides <tt>messageReceived</tt> from <tt>SessionAdapter</tt>,
- * called when we receive a new intant message.
- *
- * @param ev Event with information on the received message
- */
- @Override
- public void messageReceived(SessionEvent ev)
- {
- handleNewMessage(ev);
- }
-
- /**
- * Overrides <tt>offlineMessageReceived</tt> from <tt>SessionAdapter</tt>,
- * called when we receive a message which has been sent to us
- * when we were offline.
- *
- * @param ev Event with information on the received message
- */
- @Override
- public void offlineMessageReceived(SessionEvent ev)
- {
- handleNewMessage(ev);
- }
-
- /**
- * Overrides <tt>newMailReceived</tt> from <tt>SessionAdapter</tt>,
- * called when yahoo alert us that there is a new message in our mailbox.
- * There is two types of notification, the first one provides only
- * the number of unread mails and the second gives informations about
- * a precise new mail. Here, we care about only the second case in which
- * we should always have the email of the sender of the mail.
- *
- * @param ev Event with information on the received email
- */
- @Override
- public void newMailReceived(SessionNewMailEvent ev)
- {
- // why, if I provide mail@yahoo.FR when registering my account,
- // SC later tells me that my email address is mail@yahoo.COM ??
- // because of this users will always be sent on yahoo.com mail
- // login page rather than their usual (yahoo.XXX) login page.
- String myEmail = yahooProvider.getAccountID().getAccountAddress();
-
- // we don't process incoming email event without source address.
- // it allows us to avoid some spams
- if ((ev.getEmailAddress() == null)
- || (ev.getEmailAddress().indexOf('@') < 0))
- {
- return;
- }
-
- String yahooMailLogon = "http://mail."
- + myEmail.substring(myEmail.indexOf('@') + 1);
-
- yahooMailLogon = "&nbsp;&nbsp;&nbsp;&nbsp;<a href=\""
- + yahooMailLogon + "\">"
- + yahooMailLogon + "</a>";
-
- // FIXME Escape HTML!
- String newMail = YahooActivator.getResources().getI18NString(
- "service.gui.NEW_MAIL",
- new String[]{ev.getFrom(),
- "&lt;" + ev.getEmailAddress() + "&gt;",
- ev.getSubject(),
- "&nbsp;&nbsp;&nbsp;&nbsp;"+yahooMailLogon}) ;
-
- Message newMailMessage = new MessageYahooImpl(
- newMail,
- HTML_MIME_TYPE,
- DEFAULT_MIME_ENCODING,
- null);
-
- Contact sourceContact = opSetPersPresence.
- findContactByID(ev.getEmailAddress());
-
- if (sourceContact == null)
- {
- if (logger.isDebugEnabled())
- logger.debug("received a new mail from an unknown contact: "
- + ev.getFrom()
- + " &lt;" + ev.getEmailAddress() + "&gt;");
- //create the volatile contact
- sourceContact = opSetPersPresence
- .createVolatileContact(ev.getEmailAddress());
- }
- MessageReceivedEvent msgReceivedEvt
- = new MessageReceivedEvent(
- newMailMessage, sourceContact, new Date(),
- MessageReceivedEvent.SYSTEM_MESSAGE_RECEIVED);
-
- fireMessageEvent(msgReceivedEvt);
- }
-
- /**
- * Handle incoming message by creating an appropriate Sip Communicator
- * <tt>Message</tt> and firing a <tt>MessageReceivedEvent</tt>
- * to interested listeners.
- *
- * @param ev The original <tt>SessionEvent</tt> which noticed us
- * of an incoming message.
- */
- private void handleNewMessage(SessionEvent ev)
- {
- if (logger.isDebugEnabled())
- logger.debug("Message received : " + ev);
-
- // to keep things simple, we can decodeToText()
- //String formattedMessage = processLinks(
- // messageDecoder.decodeToText(ev.getMessage()));
-
- String formattedMessage = ev.getMessage();
- if (logger.isDebugEnabled())
- logger.debug("original message received : " + formattedMessage);
-
- formattedMessage = decodeMessage(formattedMessage);
-
- if (logger.isDebugEnabled())
- logger.debug("formatted Message : " + formattedMessage);
- // As no indications in the protocol is it html or not. No harm
- // to set all messages html - doesn't affect the appearance of the
- // gui
- Message newMessage =
- createMessage(formattedMessage, HTML_MIME_TYPE,
- DEFAULT_MIME_ENCODING, null);
-
- Contact sourceContact = opSetPersPresence.
- findContactByID(ev.getFrom());
-
- if(sourceContact == null)
- {
- if (logger.isDebugEnabled())
- logger.debug("received a message from an unknown contact: "
- + ev.getFrom());
- //create the volatile contact
- sourceContact = opSetPersPresence
- .createVolatileContact(ev.getFrom());
- }
-
- MessageReceivedEvent msgReceivedEvt
- = new MessageReceivedEvent(
- newMessage, sourceContact , new Date());
-
- // msgReceivedEvt = messageReceivedTransform(msgReceivedEvt);
-
- if (msgReceivedEvt != null)
- fireMessageEvent(msgReceivedEvt);
- }
- }
-
- /**
- * Decode the received chat message.
- * If the message contains \u001b the following text is decoded by
- * the MessageDecoder of yahoo api
- * Then make http links clickable and fix the font size of html code
- *
- * @param message the chat message
- * @return a decoded message.
- */
- String decodeMessage(String message)
- {
- message = messageDecoder.decodeToHTML(message);
- message = processLinks(message);
- message =
- FONT_SIZE_0_PATTERN.matcher(message)
- .replaceAll("$1 $2 size=\"10\">");
- message =
- FONT_SIZE_INT_PATTERN.matcher(message)
- .replaceAll("$1 $2 style=\"font-size: $3px;\">");
- return message;
- }
-
- /**
- * Format links in the given message. Skips all links, which are already in
- * HTML format and converts all other links.
- *
- * @param message The source message string.
- * @return The message string with properly formatted links.
- */
- public String processLinks(String message)
- {
- StringBuffer msgBuffer = new StringBuffer();
-
- // We match two groups of Strings. The first group is the group of any
- // String. The second group is a well formatted HTML link.
- Pattern p = Pattern.compile("(.*?)(<a[\\s][^<]*(/>|</a>))",
- Pattern.CASE_INSENSITIVE);
-
- Matcher m = p.matcher(message);
-
- int lastMatchIndex = 0;
- while (m.find())
- {
- lastMatchIndex = m.end();
-
- String matchGroup1 = m.group(1);
- String matchGroup2 = m.group(2);
-
- String formattedString = formatLinksToHTML(matchGroup1);
-
- m.appendReplacement(msgBuffer,
- replaceSpecialRegExpChars(formattedString) + matchGroup2);
- }
-
- String tailString = message.substring(lastMatchIndex);
-
- String formattedTailString = formatLinksToHTML(tailString);
-
- msgBuffer.append(formattedTailString);
-
- return msgBuffer.toString();
- }
-
- /**
- * Replaces some chars that are special in a regular expression.
- *
- * @param text The initial text.
- * @return the formatted text
- */
- private static String replaceSpecialRegExpChars(String text)
- {
- return MESSAGE_CHARS_ESCAPE.matcher(text).replaceAll("\\\\$1");
- }
-
- /**
- * Goes through the given text and converts all links to HTML links.
- * <p>
- * For example all occurrences of http://jitsi.org/ will be
- * replaced by <a href="http://jitsi.org/">
- * http://jitsi.org/</a>. The same is true for all strings
- * starting with "www".
- *
- * @param text the text on which the regular expression would be performed
- * @return the initial text containing only HTML links
- */
- private static String formatLinksToHTML(String text)
- {
- String wwwURL = "(www\\." + // Matches the "www" string.
- "[^/?#<\"'\\s]+" + // Matches at least one char of
- // any type except / ? # < " '
- // and space.
- "[\\.]" + // Matches the second point of the link.
- "[^?#<\"'\\s]+" + // Matches at least one char of
- // any type except ? # < " '
- // and space.
- "(\\?[^#<\"'\\s]*)?" +
- "(#.*)?)";
-
- String protocolURL
- = "([^\"'<>:/?#\\s]+" + // Matches at least one char of
- // any type except " ' < > : / ? #
- // and space.
- "://" + // Matches the :// delimiter in links
- "[^/?#<\"'\\s]*" + // Matches any number of times any char
- // except / ? # < " ' and space.
- "[^?#<\"'\\s]*" + // Matches any number of times any char
- // except ? # < " ' and space.
- "(\\?[^#<\"'\\s]*)?" +
- "(#.*)?)";
-
- String url = '(' + wwwURL + '|' + protocolURL + ')';
-
- Pattern p = Pattern.compile(url, Pattern.CASE_INSENSITIVE);
-
- Matcher m = p.matcher(text);
-
- StringBuffer linkBuffer = new StringBuffer();
-
- while (m.find())
- {
- String linkGroup = m.group();
-
- String replacement;
- if (linkGroup.startsWith("www"))
- {
- replacement = "<A href=\"" + "http://"
- + linkGroup + "\">" + linkGroup + "</A>";
- }
- else
- {
- replacement = "<A href=\"" + linkGroup
- + "\">" + linkGroup + "</A>";
- }
-
- m.appendReplacement(linkBuffer,
- replaceSpecialRegExpChars(replacement));
- }
-
- m.appendTail(linkBuffer);
-
- return linkBuffer.toString();
- }
-
- /**
- * Registers an <tt>EventFilter</tt> with this operation set so that
- * events, that do not need processing, are filtered out.
- *
- * @param filter the <tt>EventFilter</tt> to register.
- */
- public void addEventFilter(EventFilter filter)
- {
- synchronized(eventFilters)
- {
- if(!eventFilters.contains(filter))
- {
- eventFilters.add(filter);
- }
- }
- }
-
- /**
- * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
- * if an event should be filtered out.
- *
- * @param filter the <tt>EventFilter</tt> to unregister.
- */
- public void removeEventFilter(EventFilter filter)
- {
- synchronized(eventFilters)
- {
- eventFilters.remove(filter);
- }
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetFileTransferYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetFileTransferYahooImpl.java
deleted file mode 100644
index 85f25b1..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetFileTransferYahooImpl.java
+++ /dev/null
@@ -1,466 +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.yahoo;
-
-import java.io.*;
-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 ymsg.network.event.*;
-
-/**
- * The Yahoo protocol filetransfer OperationSet.
- *
- * @author Damian Minkov
- */
-public class OperationSetFileTransferYahooImpl
- implements OperationSetFileTransfer,
- SessionFileTransferListener
-{
- /**
- * The logger for this class.
- */
- private static final Logger logger =
- Logger.getLogger(OperationSetFileTransferYahooImpl.class);
-
- /**
- * The provider that created us.
- */
- private final ProtocolProviderServiceYahooImpl yahooProvider;
-
- /**
- * A list of listeners registered for file transfer events.
- */
- private ArrayList<FileTransferListener> fileTransferListeners
- = new ArrayList<FileTransferListener>();
-
- /**
- * A list of active fileTransfers.
- */
- private Hashtable<String, Object> activeFileTransfers
- = new Hashtable<String, Object>();
-
- /**
- * Constructor
- * @param provider is the provider that created us
- */
- public OperationSetFileTransferYahooImpl(
- ProtocolProviderServiceYahooImpl provider)
- {
- this.yahooProvider = provider;
-
- provider.addRegistrationStateChangeListener(
- new RegistrationStateListener());
- }
-
- /**
- * 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
- {
- try
- {
- assertConnected();
-
- if(file.length() > getMaximumFileLength())
- throw new IllegalArgumentException(
- "File length exceeds the allowed one for this protocol");
-
- ArrayList<String> filesToSend = new ArrayList<String>();
- filesToSend.add(file.getCanonicalPath());
- Date sentDate = new Date();
- String id = yahooProvider.getYahooSession().sendFiles(
- filesToSend, toContact.getAddress());
-
- FileTransferImpl ft =
- new FileTransferImpl(yahooProvider,
- id, toContact, file, FileTransfer.OUT);
-
- // Notify all interested listeners that a file transfer has been
- // created.
- FileTransferCreatedEvent event
- = new FileTransferCreatedEvent(ft, sentDate);
-
- fireFileTransferCreated(event);
-
- ft.fireStatusChangeEvent(FileTransferStatusChangeEvent.PREPARING);
-
- return ft;
- }
- catch(IOException e)
- {
- logger.error("Cannot send fileTransfer", e);
- return null;
- }
- }
-
- /**
- * 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 fromContact the contact sending the file
- * @param remotePath the remote file path
- * @param localPath the local file path
- *
- * @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,
- Contact fromContact,
- String remotePath,
- String localPath)
- throws IllegalStateException,
- IllegalArgumentException
- {
- return this.sendFile(toContact, new File(localPath));
- }
-
- /**
- * Adds the given <tt>FileTransferListener</tt> that would listen for
- * file transfer requests and created file transfers.
- *
- * @param listener the <tt>FileTransferListener</tt> to add
- */
- public void addFileTransferListener(
- FileTransferListener listener)
- {
- synchronized(fileTransferListeners)
- {
- if(!fileTransferListeners.contains(listener))
- {
- this.fileTransferListeners.add(listener);
- }
- }
- }
-
- /**
- * Removes the given <tt>FileTransferListener</tt> that listens for
- * file transfer requests and created file transfers.
- *
- * @param listener the <tt>FileTransferListener</tt> to remove
- */
- public void removeFileTransferListener(
- FileTransferListener listener)
- {
- synchronized(fileTransferListeners)
- {
- this.fileTransferListeners.remove(listener);
- }
- }
-
- /**
- * Utility method throwing an exception if the stack is not properly
- * initialized.
- * @throws java.lang.IllegalStateException if the underlying stack is
- * not registered and initialized.
- */
- private void assertConnected()
- throws IllegalStateException
- {
- if (yahooProvider == null)
- throw new IllegalStateException(
- "The provider must be non-null and signed on the "
- +"service before being able to send a file.");
- else if (!yahooProvider.isRegistered())
- throw new IllegalStateException(
- "The provider must be signed on the service before "
- +"being able to send a file.");
- }
-
- /**
- * Delivers the file transfer to all registered listeners.
- *
- * @param event the <tt>FileTransferEvent</tt> that we'd like delivered to
- * all registered file transfer listeners.
- */
- void fireFileTransferCreated(FileTransferCreatedEvent event)
- {
- activeFileTransfers.put(
- event.getFileTransfer().getID(), event.getFileTransfer());
-
- Iterator<FileTransferListener> listeners = null;
- synchronized (fileTransferListeners)
- {
- listeners = new ArrayList<FileTransferListener>
- (fileTransferListeners).iterator();
- }
-
- while (listeners.hasNext())
- {
- FileTransferListener listener = listeners.next();
- listener.fileTransferCreated(event);
- }
- }
-
- /**
- * Delivers the specified event to all registered file transfer listeners.
- *
- * @param event the <tt>EventObject</tt> that we'd like delivered to all
- * registered file transfer listeners.
- */
- void fireFileTransferRequestRejected(FileTransferRequestEvent event)
- {
- Iterator<FileTransferListener> listeners = null;
- synchronized (fileTransferListeners)
- {
- listeners = new ArrayList<FileTransferListener>
- (fileTransferListeners).iterator();
- }
-
- while (listeners.hasNext())
- {
- FileTransferListener listener = listeners.next();
-
- listener.fileTransferRequestRejected(event);
- }
- }
-
- /**
- * Delivers the specified event to all registered file transfer listeners.
- *
- * @param event the <tt>EventObject</tt> that we'd like delivered to all
- * registered file transfer listeners.
- */
- private void fireFileTransferRequest(FileTransferRequestEvent event)
- {
- Iterator<FileTransferListener> listeners = null;
- synchronized (fileTransferListeners)
- {
- listeners = new ArrayList<FileTransferListener>
- (fileTransferListeners).iterator();
- }
-
- while (listeners.hasNext())
- {
- FileTransferListener listener = listeners.next();
-
- listener.fileTransferRequestReceived(event);
- }
- }
-
- /**
- * Delivers the specified event to all registered file transfer listeners.
- *
- * @param event the <tt>EventObject</tt> that we'd like delivered to all
- * registered file transfer listeners.
- */
- void fireFileTransferRequestCanceled(FileTransferRequestEvent event)
- {
- Iterator<FileTransferListener> listeners = null;
- synchronized (fileTransferListeners)
- {
- listeners = new ArrayList<FileTransferListener>
- (fileTransferListeners).iterator();
- }
-
- while (listeners.hasNext())
- {
- FileTransferListener listener = listeners.next();
-
- listener.fileTransferRequestCanceled(event);
- }
- }
-
- private int getStateMapping(int s)
- {
- switch(s)
- {
- case SessionFileTransferEvent.REFUSED :
- return FileTransferStatusChangeEvent.REFUSED;
- case SessionFileTransferEvent.CANCEL :
- return FileTransferStatusChangeEvent.CANCELED;
- case SessionFileTransferEvent.FAILED :
- return FileTransferStatusChangeEvent.FAILED;
- case SessionFileTransferEvent.IN_PROGRESS :
- return FileTransferStatusChangeEvent.IN_PROGRESS;
- case SessionFileTransferEvent.RECEIVED :
- return FileTransferStatusChangeEvent.COMPLETED;
- case SessionFileTransferEvent.SENT :
- return FileTransferStatusChangeEvent.COMPLETED;
- default: return FileTransferStatusChangeEvent.WAITING;
- }
- }
-
- /**
- * Starting point for incoming filetransfer.
- * @param ev
- */
- public void fileTransferRequestReceived(SessionFileTransferEvent ev)
- {
- OperationSetPersistentPresenceYahooImpl opSetPersPresence
- = (OperationSetPersistentPresenceYahooImpl)
- yahooProvider.getOperationSet(
- OperationSetPersistentPresence.class);
-
- Contact sender = opSetPersPresence.findContactByID(ev.getFrom());
-
- if(sender == null)
- return;
-
- Date recvDate = new Date();
-
- for(int i = 0; i < ev.getFileNames().size(); i++)
- {
- String fileName = ev.getFileNames().get(i);
- String fileSize = ev.getFileSizes().get(i);
-
- IncomingFileTransferRequest req =
- new IncomingFileTransferRequestYahooImpl(
- yahooProvider, this, sender, recvDate,
- fileName, fileSize,
- ev.getId());
-
- activeFileTransfers.put(ev.getId(), req);
- fireFileTransferRequest(
- new FileTransferRequestEvent(this, req, recvDate));
- }
- }
-
- /**
- * Status changed for filetransfer.
- * @param ev
- */
- public void statusChanged(SessionFileTransferEvent ev)
- {
- if(ev.getId() == null)
- return;
-
- Object ftObj = activeFileTransfers.get(ev.getId());
-
- if(ftObj == null)
- {
- logger.warn("File Transfer or request not found. " + ev.getId() + "/ " + ev.getState());
- return;
- }
-
- int newState = ev.getState();
-
- if(newState == SessionFileTransferEvent.CANCEL
- || newState == SessionFileTransferEvent.FAILED
- || newState == SessionFileTransferEvent.RECEIVED
- || newState == SessionFileTransferEvent.REFUSED
- || newState == SessionFileTransferEvent.SENT)
- {
- // this is an final state so remove it from active filetransfers
- activeFileTransfers.remove(ev.getId());
- }
-
- if(ftObj instanceof IncomingFileTransferRequest)
- {
- if(newState == SessionFileTransferEvent.REFUSED)
- {
- IncomingFileTransferRequestYahooImpl req =
- (IncomingFileTransferRequestYahooImpl)ftObj;
- fireFileTransferRequestCanceled(
- new FileTransferRequestEvent(this, req, req.getDate()));
- return;
- }
- }
-
- if(!(ftObj instanceof FileTransferImpl))
- {
- logger.warn("File Transfer not found." + ftObj);
- return;
- }
-
- FileTransferImpl ft = (FileTransferImpl)ftObj;
-
- if( newState == SessionFileTransferEvent.IN_PROGRESS)
- {
- // if we start sending progress fire that we are in progress
- if(ev.getProgress() == 0)
- ft.fireStatusChangeEvent(
- FileTransferStatusChangeEvent.IN_PROGRESS);
-
- ft.setTransferedBytes(ev.getProgress());
- ft.fireProgressChangeEvent(
- System.currentTimeMillis(), ev.getProgress());
- }
- else
- ft.fireStatusChangeEvent(getStateMapping(newState));
- }
-
- /**
- * Returns the maximum file length supported by the protocol in bytes.
- * Supports up to 256MB.
- *
- * @return the file length that is supported.
- */
- public long getMaximumFileLength()
- {
- return 268435456l;// = 256*1024*1024;
- }
-
- /**
- * Our listener that will tell us when we're registered to
- */
- private class RegistrationStateListener
- implements RegistrationStateChangeListener
- {
- /**
- * The method is called by a ProtocolProvider implementation whenever
- * a change in the registration state of the corresponding provider had
- * occurred.
- * @param evt ProviderStatusChangeEvent the event describing the status
- * change.
- */
- public void registrationStateChanged(RegistrationStateChangeEvent evt)
- {
- if (logger.isDebugEnabled())
- logger.debug("The provider changed state from: "
- + evt.getOldState()
- + " to: " + evt.getNewState());
-
- if (evt.getNewState() == RegistrationState.REGISTERED)
- {
- yahooProvider.getYahooSession().addSessionFileListener(
- OperationSetFileTransferYahooImpl.this);
- }
- else if (evt.getNewState() == RegistrationState.UNREGISTERED)
- {
- YahooSession ys = yahooProvider.getYahooSession();
- if(ys != null)
- ys.removeSessionFileListener(
- OperationSetFileTransferYahooImpl.this);
- }
- }
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java
deleted file mode 100644
index a99bdb7..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetPersistentPresenceYahooImpl.java
+++ /dev/null
@@ -1,954 +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.yahoo;
-
-import java.io.*;
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.service.protocol.yahooconstants.*;
-import net.java.sip.communicator.util.*;
-import ymsg.network.*;
-import ymsg.network.event.*;
-
-/**
- * The Yahoo implementation of a Persistent Presence Operation set. This class
- * manages our own presence status as well as subscriptions for the presence
- * status of our buddies. It also offers methods for retrieving and modifying
- * the buddy contact list and adding listeners for changes in its layout.
- *
- * @author Damian Minkov
- */
-public class OperationSetPersistentPresenceYahooImpl
- extends AbstractOperationSetPersistentPresence<ProtocolProviderServiceYahooImpl>
-{
- private static final Logger logger =
- Logger.getLogger(OperationSetPersistentPresenceYahooImpl.class);
-
- /**
- * Contains our current status message. Note that this field would only
- * be changed once the server has confirmed the new status message and
- * not immediately upon setting a new one..
- */
- private String currentStatusMessage = "";
-
- /**
- * The presence status that we were last notified of entering.
- * The initial one is OFFLINE
- */
- private PresenceStatus currentStatus = YahooStatusEnum.OFFLINE;
-
- /**
- * Sometimes status changes are received before the contact list is inited
- * here we store such events so we can show them correctly
- */
-// private Hashtable earlyStatusChange = new Hashtable();
-
- /**
- * The array list we use when returning from the getSupportedStatusSet()
- * method.
- */
- private static final List<PresenceStatus> supportedPresenceStatusSet = new ArrayList<PresenceStatus>();
- static{
- supportedPresenceStatusSet.add(YahooStatusEnum.AVAILABLE);
- supportedPresenceStatusSet.add(YahooStatusEnum.BE_RIGHT_BACK);
- supportedPresenceStatusSet.add(YahooStatusEnum.BUSY);
- supportedPresenceStatusSet.add(YahooStatusEnum.IDLE);
- supportedPresenceStatusSet.add(YahooStatusEnum.INVISIBLE);
- supportedPresenceStatusSet.add(YahooStatusEnum.NOT_AT_DESK);
- supportedPresenceStatusSet.add(YahooStatusEnum.NOT_AT_HOME);
- supportedPresenceStatusSet.add(YahooStatusEnum.NOT_IN_OFFICE);
- supportedPresenceStatusSet.add(YahooStatusEnum.OFFLINE);
- supportedPresenceStatusSet.add(YahooStatusEnum.ON_THE_PHONE);
- supportedPresenceStatusSet.add(YahooStatusEnum.ON_VACATION);
- supportedPresenceStatusSet.add(YahooStatusEnum.OUT_TO_LUNCH);
- supportedPresenceStatusSet.add(YahooStatusEnum.STEPPED_OUT);
- }
-
- /**
- * A map containing bindings between SIP Communicator's yahoo presence status
- * instances and Yahoo status codes
- */
- private static final Map<PresenceStatus, Long> scToYahooModesMappings
- = new Hashtable<PresenceStatus, Long>();
- static{
- scToYahooModesMappings.put(YahooStatusEnum.AVAILABLE,
- StatusConstants.STATUS_AVAILABLE);
- scToYahooModesMappings.put(YahooStatusEnum.BE_RIGHT_BACK,
- StatusConstants.STATUS_BRB);
- scToYahooModesMappings.put(YahooStatusEnum.BUSY,
- StatusConstants.STATUS_BUSY);
- scToYahooModesMappings.put(YahooStatusEnum.IDLE,
- StatusConstants.STATUS_IDLE);
- scToYahooModesMappings.put(YahooStatusEnum.INVISIBLE,
- StatusConstants.STATUS_INVISIBLE);
- scToYahooModesMappings.put(YahooStatusEnum.NOT_AT_DESK,
- StatusConstants.STATUS_NOTATDESK);
- scToYahooModesMappings.put(YahooStatusEnum.NOT_AT_HOME,
- StatusConstants.STATUS_NOTATHOME);
- scToYahooModesMappings.put(YahooStatusEnum.NOT_IN_OFFICE,
- StatusConstants.STATUS_NOTINOFFICE);
- scToYahooModesMappings.put(YahooStatusEnum.OFFLINE,
- StatusConstants.STATUS_OFFLINE);
- scToYahooModesMappings.put(YahooStatusEnum.ON_THE_PHONE,
- StatusConstants.STATUS_ONPHONE);
- scToYahooModesMappings.put(YahooStatusEnum.ON_VACATION,
- StatusConstants.STATUS_ONVACATION);
- scToYahooModesMappings.put(YahooStatusEnum.OUT_TO_LUNCH,
- StatusConstants.STATUS_OUTTOLUNCH);
- scToYahooModesMappings.put(YahooStatusEnum.STEPPED_OUT,
- StatusConstants.STATUS_STEPPEDOUT);
- }
-
- /**
- * The server stored contact list that will be encapsulating smack's
- * buddy list.
- */
- private ServerStoredContactListYahooImpl ssContactList = null;
-
- /**
- * Listens for events that are fired while registering to server.
- * After we are registered instance is cleared and never used.
- */
- private EarlyEventListener earlyEventListener = null;
-
- /**
- * Status events are received before subscription one.
- * And when subscription is received we deliver
- * and the status events.
- */
- private StatusUpdater statusUpdater = new StatusUpdater();
-
- public OperationSetPersistentPresenceYahooImpl(
- ProtocolProviderServiceYahooImpl provider)
- {
- super(provider);
-
- ssContactList = new ServerStoredContactListYahooImpl( this , provider);
-
- parentProvider.addRegistrationStateChangeListener(
- new RegistrationStateListener());
- }
-
- /**
- * Registers a listener that would receive events upong changes in server
- * stored groups.
- *
- * @param listener a ServerStoredGroupChangeListener impl that would
- * receive events upong group changes.
- */
- @Override
- public void addServerStoredGroupChangeListener(ServerStoredGroupListener
- listener)
- {
- ssContactList.addGroupListener(listener);
- }
-
- /**
- * 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.
- * @throws OperationFailedException if such group already exists
- */
- public void createServerStoredContactGroup(ContactGroup parent,
- String groupName)
- throws OperationFailedException
- {
- assertConnected();
-
- if (!parent.canContainSubgroups())
- throw new IllegalArgumentException(
- "The specified contact group cannot contain child groups. Group:"
- + parent );
-
- ssContactList.createGroup(groupName);
- }
-
- /**
- * 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. The volatile contact would
- * remain in the list until it is really added to the contact list or
- * until the application is terminated.
- * @param id the address of the contact to create.
- * @return the newly created volatile <tt>ContactImpl</tt>
- */
- public ContactYahooImpl createVolatileContact(String id)
- {
- return ssContactList.createVolatileContact(id);
- }
-
- /**
- * Creates and returns a unresolved contact from the specified
- * <tt>address</tt> and <tt>persistentData</tt>.
- *
- * @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 parentGroup 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 parentGroup)
- {
- if(! (parentGroup instanceof ContactGroupYahooImpl ||
- parentGroup instanceof RootContactGroupYahooImpl) )
- throw new IllegalArgumentException(
- "Argument is not an yahoo contact group (group="
- + parentGroup + ")");
-
- ContactYahooImpl contact =
- ssContactList.createUnresolvedContact(parentGroup, address);
-
- contact.setPersistentData(persistentData);
-
- return contact;
- }
-
- /**
- * Creates and returns a unresolved contact from the specified
- * <tt>address</tt> and <tt>persistentData</tt>.
- *
- * @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 group from the specified
- * <tt>address</tt> and <tt>persistentData</tt>.
- *
- * @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)
- {
- return ssContactList.createUnresolvedContactGroup(groupUID);
- }
-
- /**
- * 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 ssContactList.findContactById(contactID);
- }
-
- /**
- * Returns the status message that was confirmed by the serfver
- *
- * @return the last status message that we have requested and the aim
- * server has confirmed.
- */
- public String getCurrentStatusMessage()
- {
- return currentStatusMessage;
- }
-
- /**
- * Returns the protocol specific contact instance representing the local
- * user.
- *
- * @return the Contact (address, phone number, or uin) that the Provider
- * implementation is communicating on behalf of.
- */
- public Contact getLocalContact()
- {
- return null;
- }
-
- /**
- * Returns a PresenceStatus instance representing the state this provider
- * is currently in.
- *
- * @return the PresenceStatus last published by this provider.
- */
- public PresenceStatus getPresenceStatus()
- {
- return currentStatus;
- }
-
- /**
- * 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 ssContactList.getRootGroup();
- }
-
- /**
- * 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 supportedPresenceStatusSet.iterator();
- }
-
- /**
- * 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)
- {
- assertConnected();
-
- if( !(contactToMove instanceof ContactYahooImpl) )
- throw new IllegalArgumentException(
- "The specified contact is not an yahoo contact." + contactToMove);
- if( !(newParent instanceof ContactGroupYahooImpl) )
- throw new IllegalArgumentException(
- "The specified group is not an yahoo contact group."
- + newParent);
-
- ssContactList.moveContact((ContactYahooImpl)contactToMove,
- (ContactGroupYahooImpl)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
- {
- assertConnected();
-
- if (!(status instanceof YahooStatusEnum))
- throw new IllegalArgumentException(
- status + " is not a valid Yahoo status");
-
- if(status.equals(YahooStatusEnum.OFFLINE))
- {
- parentProvider.unregister();
- return;
- }
-
- try
- {
- if(statusMessage != null && statusMessage.length() != 0)
- {
- boolean isAvailable = false;
-
- if(status.equals(YahooStatusEnum.AVAILABLE))
- isAvailable = true;
-
- // false - away
- // true - available
- parentProvider.getYahooSession().
- setStatus(statusMessage, isAvailable);
- }
-
- parentProvider.getYahooSession().setStatus(
- scToYahooModesMappings.get(status).longValue());
-
- fireProviderStatusChangeEvent(currentStatus, status);
- }
- catch(IOException ex)
- {
- throw new OperationFailedException("Failed to set Status",
- OperationFailedException.NETWORK_FAILURE);
- }
- }
-
- /**
- * 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
- {
-
- ContactYahooImpl contact = ssContactList.findContactById(contactIdentifier);
- if(contact == null)
- {
- if (logger.isInfoEnabled())
- logger.info("Contact not found id :" + contactIdentifier);
- return null;
- }
- else
- return yahooStatusToPresenceStatus(contact.getSourceContact().getStatus());
- }
-
- /**
- * Removes the specified group from the server stored contact list.
- *
- * @param group the group to remove.
- */
- public void removeServerStoredContactGroup(ContactGroup group)
- {
- assertConnected();
-
- if( !(group instanceof ContactGroupYahooImpl) )
- throw new IllegalArgumentException(
- "The specified group is not an yahoo contact group: " + group);
-
- ssContactList.removeGroup(((ContactGroupYahooImpl)group));
- }
-
- /**
- * Removes the specified group change listener so that it won't receive
- * any further events.
- *
- * @param listener the ServerStoredGroupChangeListener to remove
- */
- @Override
- public void removeServerStoredGroupChangeListener(ServerStoredGroupListener
- listener)
- {
- ssContactList.removeGroupListener(listener);
- }
-
- /**
- * 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)
- {
- assertConnected();
-
- if( !(group instanceof ContactGroupYahooImpl) )
- throw new IllegalArgumentException(
- "The specified group is not an yahoo contact group: " + group);
-
- throw new UnsupportedOperationException("Renaming group not supported!");
- //ssContactList.renameGroup((ContactGroupYahooImpl)group, newName);
- }
-
- /**
- * 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)
- {
- ssContactList.setAuthorizationHandler(handler);
-
- // we got a handler. Lets process if something has came
- // during login process
- if(earlyEventListener != null)
- {
- earlyEventListener.processEarlyAuthorizations();
- earlyEventListener = null;
- }
- }
-
- /**
- * 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
- {
- assertConnected();
-
- if(! (parent instanceof ContactGroupYahooImpl) )
- throw new IllegalArgumentException(
- "Argument is not an yahoo contact group (group=" + parent + ")");
-
- ssContactList.addContact((ContactGroupYahooImpl)parent, contactIdentifier);
- }
-
- /**
- * 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
- {
- assertConnected();
-
- ssContactList.addContact(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
- {
- assertConnected();
-
- if(! (contact instanceof ContactYahooImpl) )
- throw new IllegalArgumentException(
- "Argument is not an yahoo contact (contact=" + contact + ")");
-
- ssContactList.removeContact((ContactYahooImpl)contact);
- }
-
- /**
- * Converts the specified yahoo status to one of the status fields of the
- * YahooStatusEnum class.
- *
- * @param status the yahoo Status
- * @return a PresenceStatus instance representation of the yahoo Status
- * parameter. The returned result is one of the YahooStatusEnum fields.
- */
- YahooStatusEnum yahooStatusToPresenceStatus(long status)
- {
- if(status == StatusConstants.STATUS_AVAILABLE)
- return YahooStatusEnum.AVAILABLE;
- else if(status == StatusConstants.STATUS_BRB)
- return YahooStatusEnum.BE_RIGHT_BACK;
- else if(status == StatusConstants.STATUS_BUSY)
- return YahooStatusEnum.BUSY;
- else if(status == StatusConstants.STATUS_NOTATHOME)
- return YahooStatusEnum.NOT_AT_HOME;
- else if(status == StatusConstants.STATUS_NOTATDESK)
- return YahooStatusEnum.NOT_AT_DESK;
- else if(status == StatusConstants.STATUS_NOTINOFFICE)
- return YahooStatusEnum.NOT_IN_OFFICE;
- else if(status == StatusConstants.STATUS_ONPHONE)
- return YahooStatusEnum.ON_THE_PHONE;
- else if(status == StatusConstants.STATUS_ONVACATION)
- return YahooStatusEnum.ON_VACATION;
- else if(status == StatusConstants.STATUS_OUTTOLUNCH)
- return YahooStatusEnum.OUT_TO_LUNCH;
- else if(status == StatusConstants.STATUS_STEPPEDOUT)
- return YahooStatusEnum.STEPPED_OUT;
- else if(status == StatusConstants.STATUS_INVISIBLE)
- return YahooStatusEnum.INVISIBLE;
- else if(status == StatusConstants.STATUS_IDLE)
- return YahooStatusEnum.IDLE;
- else if(status == StatusConstants.STATUS_OFFLINE)
- return YahooStatusEnum.OFFLINE;
- // Yahoo supports custom statuses so if such is set just return available
- else
- return YahooStatusEnum.AVAILABLE;
- }
-
- /**
- * Utility method throwing an exception if the stack is not properly
- * initialized.
- * @throws java.lang.IllegalStateException if the underlying stack is
- * not registered and initialized.
- */
- private void assertConnected() throws IllegalStateException
- {
- if (parentProvider == null)
- throw new IllegalStateException(
- "The provider must be non-null and signed on the yahoo "
- +"service before being able to communicate.");
- if (!parentProvider.isRegistered())
- throw new IllegalStateException(
- "The provider must be signed on the yahoo service before "
- +"being able to communicate.");
- }
-
- /**
- * Notify all provider presence listeners of the corresponding event change
- *
- * @param oldStatus
- * the status our stack had so far
- * @param newStatus
- * the status we have from now on
- */
- @Override
- protected void fireProviderStatusChangeEvent(
- PresenceStatus oldStatus,
- PresenceStatus newStatus)
- {
- if (!oldStatus.equals(newStatus))
- {
- currentStatus = newStatus;
-
- super.fireProviderStatusChangeEvent(oldStatus, newStatus);
- }
- }
-
- /**
- * Statuses have been received durring login process
- * so we will init them once we are logged in
- */
- private void initContactStatuses()
- {
- YahooGroup[] groups = parentProvider.getYahooSession().getGroups();
-
- for (YahooGroup item : groups)
- {
- @SuppressWarnings("unchecked")
- Iterable<YahooUser> members = item.getMembers();
-
- for (YahooUser user : members)
- {
- ContactYahooImpl sourceContact =
- ssContactList.findContactById(user.getId());
-
- if(sourceContact != null)
- handleContactStatusChange(sourceContact, user);
- }
- }
- }
-
- /**
- * Our listener that will tell us when we're registered to server
- * and is ready to accept us as a listener.
- */
- private class RegistrationStateListener
- implements RegistrationStateChangeListener
- {
- /**
- * The method is called by a ProtocolProvider implementation whenever
- * a change in the registration state of the corresponding provider had
- * occurred.
- * @param evt ProviderStatusChangeEvent the event describing the status
- * change.
- */
- public void registrationStateChanged(RegistrationStateChangeEvent evt)
- {
- if (logger.isDebugEnabled())
- logger.debug("The yahoo provider changed state from: "
- + evt.getOldState()
- + " to: " + evt.getNewState());
-
- if(evt.getNewState() == RegistrationState.REGISTERING)
- {
- // add new listener waiting for events during login process
- earlyEventListener
- = new EarlyEventListener(parentProvider.getYahooSession());
- }
- else if(evt.getNewState() == RegistrationState.REGISTERED)
- {
- parentProvider.getYahooSession().
- addSessionListener(new StatusChangedListener());
-
- ssContactList.setYahooSession(parentProvider.getYahooSession());
-
- initContactStatuses();
-
- addSubscriptionListener(statusUpdater);
-
- if(earlyEventListener != null)
- {
- earlyEventListener.dispose();
- earlyEventListener = null;
- }
- }
- else if(evt.getNewState() == RegistrationState.UNREGISTERED
- || evt.getNewState() == RegistrationState.AUTHENTICATION_FAILED
- || evt.getNewState() == RegistrationState.CONNECTION_FAILED)
- {
- //since we are disconnected, we won't receive any further status
- //updates so we need to change by ourselves our own status as
- //well as set to offline all contacts in our contact list that
- //were online
- PresenceStatus oldStatus = currentStatus;
- currentStatus = YahooStatusEnum.OFFLINE;
-
- fireProviderStatusChangeEvent(oldStatus, currentStatus);
-
- removeSubscriptionListener(statusUpdater);
-
- //send event notifications saying that all our buddies are
- //offline. The 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())
- {
- ContactYahooImpl contact
- = (ContactYahooImpl)contactsIter.next();
-
- PresenceStatus oldContactStatus
- = contact.getPresenceStatus();
-
- if(!oldContactStatus.isOnline())
- continue;
-
- contact.updatePresenceStatus(YahooStatusEnum.OFFLINE);
-
- fireContactPresenceStatusChangeEvent(
- contact
- , contact.getParentContactGroup()
- , oldContactStatus, YahooStatusEnum.OFFLINE);
- }
- }
-
- // clear listener
- if(earlyEventListener != null)
- {
- earlyEventListener.dispose();
- earlyEventListener = null;
- }
- }
- }
- }
-
- private void handleContactStatusChange(YahooUser yFriend)
- {
- ContactYahooImpl sourceContact =
- ssContactList.findContactById(yFriend.getId());
-
- if(sourceContact == null)
- {
- if(parentProvider.getAccountID().getUserID().
- equals(yFriend.getId()))
- {
- // thats my own status
- if (logger.isTraceEnabled())
- logger.trace("Own status changed to " + yFriend.getStatus());
- PresenceStatus oldStatus = currentStatus;
- currentStatus =
- yahooStatusToPresenceStatus(yFriend.getStatus());
- fireProviderStatusChangeEvent(oldStatus, currentStatus);
-
- return;
- }
- // strange
- else
- return;
- }
-
- handleContactStatusChange(sourceContact, yFriend);
- }
-
- void handleContactStatusChange(ContactYahooImpl sourceContact, YahooUser yFriend)
- {
- PresenceStatus oldStatus
- = sourceContact.getPresenceStatus();
-
- PresenceStatus newStatus = yahooStatusToPresenceStatus(yFriend.getStatus());
-
- // statuses maybe the same and only change in status message
- sourceContact.setStatusMessage(yFriend.getCustomStatusMessage());
-
- // when old and new status are the same do nothing - no change
- if(oldStatus.equals(newStatus))
- {
- if (logger.isDebugEnabled())
- logger.debug("old(" + oldStatus + ") and new("+ newStatus + ") statuses are the same!");
- return;
- }
-
- sourceContact.updatePresenceStatus(newStatus);
-
- ContactGroup parent
- = ssContactList.findContactGroup(sourceContact);
-
- if (logger.isDebugEnabled())
- logger.debug("Will Dispatch the contact status event.");
- fireContactPresenceStatusChangeEvent(sourceContact, parent,
- oldStatus, newStatus);
- }
-
- private class StatusChangedListener
- extends SessionAdapter
- {
- @Override
- public void friendsUpdateReceived(SessionFriendEvent evt)
- {
- if (logger.isDebugEnabled())
- logger.debug("Received a status update for contact " + evt);
-
- if(evt.getFriend() != null)
- {
- handleContactStatusChange(evt.getFriend());
- }
- else if(evt.getFriends() != null)
- {
- YahooUser[] yfs = evt.getFriends();
- for (int i = 0; i < yfs.length; i++)
- handleContactStatusChange(yfs[i]);
- }
- }
- }
-
- /**
- * Updates the statuses of newly created persistent contacts
- */
- private class StatusUpdater
- extends SubscriptionAdapter
- {
- @Override
- public void subscriptionCreated(SubscriptionEvent evt)
- {
- ContactYahooImpl contact =
- (ContactYahooImpl)evt.getSourceContact();
-
- if(!contact.isPersistent() || !contact.isResolved())
- return;
-
- handleContactStatusChange(contact, contact.getSourceContact());
- }
- }
-
- private class EarlyEventListener
- extends SessionAdapter
- {
- private final List<SessionAuthorizationEvent> receivedAuthorizations
- = new Vector<SessionAuthorizationEvent>();
-
- /**
- * The <code>YahooSession</code> this instance is listening to because
- * the <code>YahooSession</code> isn't available in
- * <code>parentProvider</code> after
- * {@link RegistrationState#UNREGISTERED} and then this listener cannot
- * be removed.
- */
- private final YahooSession yahooSession;
-
- public EarlyEventListener(YahooSession yahooSession)
- {
- this.yahooSession = yahooSession;
- this.yahooSession.addSessionListener(this);
- }
-
- @Override
- public void authorizationReceived(SessionAuthorizationEvent ev)
- {
- if(ev.isAuthorizationRequest())
- {
- if (logger.isTraceEnabled())
- logger.trace("authorizationRequestReceived from " +
- ev.getFrom());
- receivedAuthorizations.add(ev);
- }
- }
-
- public void dispose()
- {
- yahooSession.removeSessionListener(this);
- }
-
- public void processEarlyAuthorizations()
- {
- for (SessionAuthorizationEvent e : receivedAuthorizations)
- {
- ssContactList.processAuthorizationRequest(e);
- }
- }
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java
deleted file mode 100644
index 81e266a..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetTypingNotificationsYahooImpl.java
+++ /dev/null
@@ -1,153 +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.yahoo;
-
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-import ymsg.network.event.*;
-
-/**
- * Maps SIP Communicator typing notifications to those going and coming from
- * smack lib.
- *
- * @author Damian Minkov
- */
-public class OperationSetTypingNotificationsYahooImpl
- extends AbstractOperationSetTypingNotifications<ProtocolProviderServiceYahooImpl>
-{
- private static final Logger logger =
- Logger.getLogger(OperationSetTypingNotificationsYahooImpl.class);
-
- /**
- * An active instance of the opSetPersPresence operation set. We're using
- * it to map incoming events to contacts in our contact list.
- */
- private OperationSetPersistentPresenceYahooImpl opSetPersPresence = null;
-
- /**
- * @param provider a ref to the <tt>ProtocolProviderServiceImpl</tt>
- * that created us and that we'll use for retrieving the underlying aim
- * connection.
- */
- OperationSetTypingNotificationsYahooImpl(
- ProtocolProviderServiceYahooImpl provider)
- {
- super(provider);
-
- provider.addRegistrationStateChangeListener(new ProviderRegListener());
- }
-
- /**
- * 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
- {
- assertConnected();
-
- if( !(notifiedContact instanceof ContactYahooImpl) )
- throw new IllegalArgumentException(
- "The specified contact is not an yahoo contact."
- + notifiedContact);
-
- if(typingState == OperationSetTypingNotifications.STATE_TYPING)
- {
-
- parentProvider.getYahooSession().
- keyTyped(notifiedContact.getAddress(),
- parentProvider.getAccountID().getUserID());
- }
- else
- if(typingState == OperationSetTypingNotifications.STATE_STOPPED ||
- typingState == OperationSetTypingNotifications.STATE_PAUSED)
- {
- parentProvider.getYahooSession().
- stopTyping(notifiedContact.getAddress(),
- parentProvider.getAccountID().getUserID());
- }
- }
-
- private class TypingListener
- extends SessionAdapter
- {
- @Override
- public void notifyReceived(SessionNotifyEvent evt)
- {
- if(evt.isTyping())
- {
- String typingUserID = evt.getFrom();
-
- if(typingUserID != null)
- {
- Contact sourceContact =
- opSetPersPresence.findContactByID(typingUserID);
-
- if(sourceContact == null)
- return;
-
- // typing on
- fireTypingNotificationsEvent(
- sourceContact,
- (evt.getMode() == 1) ? STATE_TYPING : STATE_STOPPED);
- }
- }
- }
- }
-
- /**
- * Our listener that will tell us when we're registered and
- * ready to accept us as a listener.
- */
- private class ProviderRegListener
- implements RegistrationStateChangeListener
- {
- /**
- * The method is called by a ProtocolProvider implementation whenever
- * a change in the registration state of the corresponding provider had
- * occurred.
- * @param evt ProviderStatusChangeEvent the event describing the status
- * change.
- */
- public void registrationStateChanged(RegistrationStateChangeEvent evt)
- {
- if (logger.isDebugEnabled())
- logger.debug("The provider changed state from: "
- + evt.getOldState()
- + " to: " + evt.getNewState());
- if (evt.getNewState() == RegistrationState.REGISTERED)
- {
- opSetPersPresence =
- (OperationSetPersistentPresenceYahooImpl) parentProvider
- .getOperationSet(OperationSetPersistentPresence.class);
-
- parentProvider
- .getYahooSession().addSessionListener(new TypingListener());
- }
- }
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolIconYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolIconYahooImpl.java
deleted file mode 100644
index 34befc9..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolIconYahooImpl.java
+++ /dev/null
@@ -1,173 +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.yahoo;
-
-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 Yahoo protocol icon. Implements the <tt>ProtocolIcon</tt>
- * interface in order to provide an Yahoo icon image in two different sizes.
- *
- * @author Yana Stamcheva
- */
-public class ProtocolIconYahooImpl
- implements ProtocolIcon
-{
- private static Logger logger = Logger.getLogger(ProtocolIconYahooImpl.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.yahoo.YAHOO_16x16"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_32x32,
- getImageInBytes("service.protocol.yahoo.YAHOO_32x32"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_48x48,
- getImageInBytes("service.protocol.yahoo.YAHOO_48x48"));
-
- iconsTable.put(ProtocolIcon.ICON_SIZE_64x64,
- getImageInBytes("service.protocol.yahoo.YAHOO_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.yahoo.YAHOO_16x16"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_32x32,
- getResources().getImagePath("service.protocol.yahoo.YAHOO_32x32"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_48x48,
- getResources().getImagePath("service.protocol.yahoo.YAHOO_48x48"));
-
- iconPathsTable.put(ProtocolIcon.ICON_SIZE_64x64,
- getResources().getImagePath("service.protocol.yahoo.YAHOO_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.
- */
- 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
- */
- 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("yahooConnectingIcon");
- }
-
- /**
- * 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;
- }
-
- public static ResourceManagementService getResources()
- {
- if (resourcesService == null)
- {
- ServiceReference serviceReference = YahooActivator.getBundleContext()
- .getServiceReference(ResourceManagementService.class.getName());
-
- if(serviceReference == null)
- return null;
-
- resourcesService = (ResourceManagementService)
- YahooActivator.getBundleContext().getService(serviceReference);
- }
-
- return resourcesService;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java
deleted file mode 100644
index 358286c..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderFactoryYahooImpl.java
+++ /dev/null
@@ -1,172 +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.yahoo;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-import org.osgi.framework.*;
-
-/**
- * The Yahoo implementation of the ProtocolProviderFactory.
- * @author Damian Minkov
- */
-public class ProtocolProviderFactoryYahooImpl
- extends ProtocolProviderFactory
-{
-
- /**
- * Creates an instance of the ProtocolProviderFactoryYahooImpl.
- */
- protected ProtocolProviderFactoryYahooImpl()
- {
- super(YahooActivator.getBundleContext(), ProtocolNames.YAHOO);
- }
-
- /**
- * Initializes and creates an account corresponding to the specified
- * accountProperties and registers the resulting ProtocolProvider in the
- * <tt>context</tt> BundleContext parameter. This method has a persistent
- * effect. Once created the resulting account will remain installed until
- * removed through the uninstall account method.
- *
- * @param userIDStr the user identifier for the new account
- * @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
- = YahooActivator.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 YahooAccountID(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 register() method and it needs to access the configuration service
- //and check for a password.
- this.storeAccount(accountID, false);
-
- accountID = loadAccount(accountProperties);
-
- return accountID;
- }
-
- @Override
- protected AccountID createAccountID(String userID, Map<String, String> accountProperties)
- {
- return new YahooAccountID(userID, accountProperties);
- }
-
- @Override
- protected ProtocolProviderService createService(String userID,
- AccountID accountID)
- {
- ProtocolProviderServiceYahooImpl service =
- new ProtocolProviderServiceYahooImpl();
-
- service.initialize(userID, accountID);
- return service;
- }
-
- @Override
- public void modifyAccount( ProtocolProviderService protocolProvider,
- Map<String, String> accountProperties)
- throws NullPointerException
- {
- BundleContext context
- = YahooActivator.getBundleContext();
-
- if (context == null)
- throw new NullPointerException(
- "The specified BundleContext was null");
-
- if (protocolProvider == null)
- throw new NullPointerException(
- "The specified Protocol Provider was null");
-
- YahooAccountID accountID
- = (YahooAccountID) protocolProvider.getAccountID();
-
- // If the given accountID doesn't correspond to an existing account
- // we return.
- if(!registeredAccounts.containsKey(accountID))
- return;
-
- ServiceRegistration registration = registeredAccounts.get(accountID);
-
- // kill the service
- if (registration != null)
- registration.unregister();
-
- if (accountProperties == null)
- throw new NullPointerException(
- "The specified property map was null");
-
- accountProperties.put(USER_ID, accountID.getUserID());
-
- if (!accountProperties.containsKey(PROTOCOL))
- accountProperties.put(PROTOCOL, ProtocolNames.YAHOO);
-
- accountID.setAccountProperties(accountProperties);
-
- // First store the account and only then load it as the load generates
- // an osgi event, the osgi event triggers (trhgough the UI) a call to
- // the register() method and it needs to acces the configuration service
- // and check for a password.
- this.storeAccount(accountID);
-
- Hashtable<String, String> properties = new Hashtable<String, String>();
- properties.put(PROTOCOL, ProtocolNames.YAHOO);
- properties.put(USER_ID, accountID.getUserID());
-
- ((ProtocolProviderServiceYahooImpl)protocolProvider)
- .initialize(accountID.getUserID(), accountID);
-
- // We store again the account in order to store all properties added
- // during the protocol provider initialization.
- this.storeAccount(accountID);
-
- registration
- = context.registerService(
- ProtocolProviderService.class.getName(),
- protocolProvider,
- properties);
-
- registeredAccounts.put(accountID, registration);
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java
deleted file mode 100644
index 264aaab..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/ProtocolProviderServiceYahooImpl.java
+++ /dev/null
@@ -1,574 +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.yahoo;
-
-import java.io.*;
-
-import net.java.sip.communicator.service.dns.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-import ymsg.network.*;
-import ymsg.network.event.*;
-
-/**
- * An implementation of the protocol provider service over the Yahoo protocol
- *
- * @author Damian Minkov
- */
-public class ProtocolProviderServiceYahooImpl
- extends AbstractProtocolProviderService
-{
- /**
- * This class logger.
- */
- private static final Logger logger =
- Logger.getLogger(ProtocolProviderServiceYahooImpl.class);
-
- /**
- * The current yahoo session.
- */
- private YahooSession yahooSession = null;
-
- /**
- * indicates whether or not the provider is initialized and ready for use.
- */
- private boolean isInitialized = false;
-
- /**
- * We use this to lock access to initialization.
- */
- private final Object initializationLock = new Object();
-
- /**
- * The identifier of the account that this provider represents.
- */
- private AccountID accountID = null;
-
- /**
- * Used when we need to re-register
- */
- private SecurityAuthority authority = null;
-
- /**
- * The persistent presence operations set.
- */
- private OperationSetPersistentPresenceYahooImpl persistentPresence = null;
-
- /**
- * Typing notifications operations set.
- */
- private OperationSetTypingNotificationsYahooImpl typingNotifications = null;
-
- /**
- * The logo corresponding to the msn protocol.
- */
- private ProtocolIconYahooImpl yahooIcon
- = new ProtocolIconYahooImpl();
-
- /**
- * The connection listener.
- */
- private YahooConnectionListener connectionListener = null;
-
- /**
- * Returns the state of the registration of this protocol provider
- * @return the <tt>RegistrationState</tt> that this provider is
- * currently in or null in case it is in a unknown state.
- */
- public RegistrationState getRegistrationState()
- {
- if(yahooSession != null &&
- yahooSession.getSessionStatus() == StatusConstants.MESSAGING)
- return RegistrationState.REGISTERED;
- else
- return RegistrationState.UNREGISTERED;
- }
-
- /**
- * Starts the registration process. Connection details such as
- * registration server, user name/number are provided through the
- * configuration service through implementation specific properties.
- *
- * @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(final SecurityAuthority authority)
- throws OperationFailedException
- {
- if(authority == null)
- throw new IllegalArgumentException(
- "The register method needs a valid non-null authority impl "
- + " in order to be able and retrieve passwords.");
-
- this.authority = authority;
-
- connectAndLogin(authority, SecurityAuthority.AUTHENTICATION_REQUIRED);
- }
-
- /**
- * Connects and logins to the server
- * @param authority SecurityAuthority
- * @param authReasonCode the authentication reason code, which should
- * indicate why are making an authentication request
- * @throws OperationFailedException if login parameters
- * as server port are not correct
- */
- private void connectAndLogin( SecurityAuthority authority,
- int authReasonCode)
- throws OperationFailedException
- {
- synchronized(initializationLock)
- {
- //verify whether a password has already been stored for this account
- String password = YahooActivator.
- getProtocolProviderFactory().loadPassword(getAccountID());
-
- // If the password hasn't been saved or the reason is one of those
- // listed below we need to ask the user for credentials again.
- if (password == null
- || authReasonCode == SecurityAuthority.WRONG_PASSWORD
- || authReasonCode == SecurityAuthority.WRONG_USERNAME)
- {
- //create a default credentials object
- UserCredentials credentials = new UserCredentials();
- credentials.setUserName(getAccountID().getUserID());
-
- //request a password from the user
- credentials = authority.obtainCredentials(
- getAccountID().getDisplayName(),
- credentials,
- authReasonCode);
-
- // in case user has canceled the login window
- if(credentials == null)
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.UNREGISTERED,
- RegistrationStateChangeEvent.REASON_USER_REQUEST, "");
- return;
- }
-
- //extract the password the user passed us.
- char[] pass = credentials.getPassword();
-
- // the user didn't provide us a password (canceled the operation)
- if(pass == null)
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.UNREGISTERED,
- RegistrationStateChangeEvent.REASON_USER_REQUEST, "");
- return;
- }
- password = new String(pass);
-
- if (credentials.isPasswordPersistent())
- {
- YahooActivator.getProtocolProviderFactory()
- .storePassword(getAccountID(), password);
- }
- }
-
- yahooSession = new YahooSession();
- connectionListener = new YahooConnectionListener();
- yahooSession.addSessionListener(connectionListener);
-
- try
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.REGISTERING,
- RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
-
- yahooSession.login(getAccountID().getUserID(), password);
-
- if(yahooSession.getSessionStatus()==StatusConstants.MESSAGING)
- {
- persistentPresence.fireProviderStatusChangeEvent(
- persistentPresence.getPresenceStatus(),
- persistentPresence.yahooStatusToPresenceStatus(
- yahooSession.getStatus()));
-
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.REGISTERED,
- RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
- }
- else
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.UNREGISTERED,
- RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
- }
- }
- catch (LoginRefusedException ex)
- {
- if(ex.getStatus() == StatusConstants.STATUS_BADUSERNAME)
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.AUTHENTICATION_FAILED,
- RegistrationStateChangeEvent.REASON_NON_EXISTING_USER_ID,
- null);
-
- reregister(SecurityAuthority.WRONG_USERNAME);
- }
- else if(ex.getStatus() == StatusConstants.STATUS_BAD)
- {
- YahooActivator.getProtocolProviderFactory()
- .storePassword(getAccountID(), null);
-
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.AUTHENTICATION_FAILED,
- RegistrationStateChangeEvent.REASON_AUTHENTICATION_FAILED,
- null);
-
- // Try to re-register and ask the user to retype the password.
- reregister(SecurityAuthority.WRONG_PASSWORD);
- }
- else if(ex.getStatus() == StatusConstants.STATUS_LOCKED)
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.AUTHENTICATION_FAILED,
- RegistrationStateChangeEvent.REASON_RECONNECTION_RATE_LIMIT_EXCEEDED,
- null);
- }
- }
- catch (IOException ex)
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.CONNECTION_FAILED,
- RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
- }
- catch (DnssecRuntimeException ex)
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.UNREGISTERED,
- RegistrationStateChangeEvent.REASON_USER_REQUEST, null);
- }
- }
- }
-
- /**
- * Reconnects if fails fire connection failed.
- * @param reasonCode the appropriate <tt>SecurityAuthority</tt> reasonCode,
- * which would specify the reason for which we're re-calling the login.
- */
- void reregister(int reasonCode)
- {
- try
- {
- connectAndLogin(authority, reasonCode);
- }
- catch (OperationFailedException ex)
- {
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.CONNECTION_FAILED,
- RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
- }
- }
-
- /**
- * Ends the registration of this protocol provider with the service.
- */
- public void unregister()
- {
- unregisterInternal(true);
- }
-
- /**
- * Unregister and fire the event if requested
- * @param fireEvent boolean
- */
- void unregisterInternal(boolean fireEvent)
- {
- RegistrationState currRegState = getRegistrationState();
-
- if(fireEvent)
- fireRegistrationStateChanged(
- currRegState,
- RegistrationState.UNREGISTERING,
- RegistrationStateChangeEvent.REASON_USER_REQUEST,
- null);
-
- try
- {
- if(connectionListener != null && yahooSession != null)
- {
- yahooSession.removeSessionListener(connectionListener);
- connectionListener = null;
- }
-
- if((yahooSession != null)
- && (yahooSession.getSessionStatus() == StatusConstants.MESSAGING))
- yahooSession.logout();
- }
- catch(Exception ex)
- {
- logger.error("Cannot logout! ", ex);
- }
-
- yahooSession = null;
-
- if(fireEvent)
- fireRegistrationStateChanged(
- currRegState,
- RegistrationState.UNREGISTERED,
- 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 short name of the protocol that the implementation of this
- * provider is based upon (like SIP, Msn, ICQ/AIM, or others for
- * example).
- *
- * @return a String containing the short name of the protocol this
- * service is taking care of.
- */
- public String getProtocolName()
- {
- return ProtocolNames.YAHOO;
- }
-
- /**
- * Initialized 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 screenname the account id/uin/screenname of the account that
- * we're about to create
- * @param accountID the identifier of the account that this protocol
- * provider represents.
- *
- * @see net.java.sip.communicator.service.protocol.AccountID
- */
- protected void initialize(String screenname,
- AccountID accountID)
- {
- synchronized(initializationLock)
- {
- this.accountID = accountID;
-
- addSupportedOperationSet(
- OperationSetInstantMessageTransform.class,
- new OperationSetInstantMessageTransformImpl());
-
- //initialize the presence operationset
- persistentPresence
- = new OperationSetPersistentPresenceYahooImpl(this);
- addSupportedOperationSet(
- OperationSetPersistentPresence.class,
- persistentPresence);
- //register it once again for those that simply need presence
- addSupportedOperationSet(
- OperationSetPresence.class,
- persistentPresence);
-
- //initialize the IM operation set
- addSupportedOperationSet(
- OperationSetBasicInstantMessaging.class,
- new OperationSetBasicInstantMessagingYahooImpl(this));
-
- //initialize the multi user chat operation set
- addSupportedOperationSet(
- OperationSetAdHocMultiUserChat.class,
- new OperationSetAdHocMultiUserChatYahooImpl(this));
-
- //initialize the typing notifications operation set
- typingNotifications
- = new OperationSetTypingNotificationsYahooImpl(this);
- addSupportedOperationSet(
- OperationSetTypingNotifications.class,
- typingNotifications);
-
- addSupportedOperationSet(
- OperationSetFileTransfer.class,
- new OperationSetFileTransferYahooImpl(this));
-
- isInitialized = true;
- }
- }
-
- /**
- * 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()
- {
- synchronized(initializationLock){
- unregisterInternal(false);
- yahooSession = null;
- isInitialized = false;
- }
- }
-
- /**
- * Returns true if the provider service implementation is initialized and
- * ready for use by other services, and false otherwise.
- *
- * @return true if the provider is initialized and ready for use and false
- * otherwise
- */
- public boolean isInitialized()
- {
- return isInitialized;
- }
-
- /**
- * 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 Yahoo<tt>Session</tt>opened by this provider
- * @return a reference to the <tt>Session</tt> last opened by this
- * provider.
- */
- YahooSession getYahooSession()
- {
- return yahooSession;
- }
-
- /**
- * Creates a RegistrationStateChange event corresponding to the specified
- * old and new states and notifies all currently registered listeners.
- *
- * @param oldState the state that the provider had before the change
- * occurred
- * @param newState the state that the provider is currently in.
- * @param reasonCode a value corresponding to one of the REASON_XXX fields
- * of the RegistrationStateChangeEvent class, indicating the reason for
- * this state transition.
- * @param reason a String further explaining the reason code or null if
- * no such explanation is necessary.
- */
- @Override
- public void fireRegistrationStateChanged( RegistrationState oldState,
- RegistrationState newState,
- int reasonCode,
- String reason)
- {
- if(newState.equals(RegistrationState.UNREGISTERED))
- {
- unregisterInternal(false);
- yahooSession = null;
- }
-
- super.fireRegistrationStateChanged(oldState, newState, reasonCode, reason);
- }
-
- /**
- * Listens when we are logged in the server
- * or incoming exception in the lib impl.
- */
- private class YahooConnectionListener
- extends SessionAdapter
- {
- /**
- * Yahoo has logged us off the system, or the connection was lost
- *
- * @param ev the event
- */
- @Override
- public void connectionClosed(SessionEvent ev)
- {
- if(isRegistered())
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.CONNECTION_FAILED,
- RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
- }
-
- /**
- * Some exception has occurred in stack.
- * @param ev
- */
- @Override
- public void inputExceptionThrown(SessionExceptionEvent ev)
- {
- if(ev.getException() instanceof YMSG9BadFormatException)
- {
- logger.error("Yahoo protocol exception occured exception",
- ev.getException());
- logger.error("Yahoo protocol exception occured exception cause",
- ev.getException().getCause());
- }
- else
- logger.error(
- "Yahoo protocol exception occured", ev.getException());
-
- unregisterInternal(false);
- if(isRegistered())
- fireRegistrationStateChanged(
- getRegistrationState(),
- RegistrationState.UNREGISTERED,
- RegistrationStateChangeEvent.REASON_INTERNAL_ERROR, null);
- }
- }
-
- /**
- * Returns the yahoo protocol icon.
- * @return the yahoo protocol icon
- */
- public ProtocolIcon getProtocolIcon()
- {
- return yahooIcon;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java
deleted file mode 100644
index 856bb30..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/RootContactGroupYahooImpl.java
+++ /dev/null
@@ -1,280 +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.yahoo;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * A dummy ContactGroup implementation representing the ContactList root for
- * Yahoo contact lists.
- * @author Damian Minkov
- */
-public class RootContactGroupYahooImpl
- extends AbstractContactGroupYahooImpl
-{
- private String ROOT_CONTACT_GROUP_NAME = "ContactListRoot";
- private List<ContactGroup> subGroups = new LinkedList<ContactGroup>();
- private boolean isResolved = false;
-
- /**
- * An empty list that we use when returning an iterator.
- */
- private List<Contact> dummyContacts = new LinkedList<Contact>();
-
- private final ProtocolProviderServiceYahooImpl protocolProvider;
-
- /**
- * Creates a ContactGroup instance.
- */
- RootContactGroupYahooImpl(ProtocolProviderServiceYahooImpl protocolProvider)
- {
- this.protocolProvider = protocolProvider;
- }
-
- /**
- * The ContactListRoot is the only group that can contain subgroups.
- *
- * @return true (always)
- */
- public boolean canContainSubgroups()
- {
- return true;
- }
-
- /**
- * Returns the name of this group which is always
- * <tt>ROOT_CONTACT_GROUP_NAME</tt>.
- *
- * @return a String containing the name of this group.
- */
- public String getGroupName()
- {
- return ROOT_CONTACT_GROUP_NAME;
- }
-
- /**
- * Adds the specified group to the end of the list of sub groups.
- * @param group the group to add.
- */
- void addSubGroup(ContactGroupYahooImpl group)
- {
- subGroups.add(group);
- }
-
- /**
- * Removes the specified from the list of sub groups
- * @param group the group to remove.
- */
- void removeSubGroup(ContactGroupYahooImpl group)
- {
- removeSubGroup(subGroups.indexOf(group));
- }
-
- /**
- * Removes the sub group with the specified index.
- * @param index the index of the group to remove
- */
- void removeSubGroup(int index)
- {
- subGroups.remove(index);
- }
-
- /**
- * Returns the number of subgroups contained by this
- * <tt>RootContactGroupImpl</tt>.
- *
- * @return an int indicating the number of subgroups that this
- * ContactGroup contains.
- */
- public int countSubgroups()
- {
- return subGroups.size();
- }
-
- /**
- * Returns null as this is the root contact group.
- * @return null as this is the root contact group.
- */
- public ContactGroup getParentContactGroup()
- {
- 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> subgroups = subgroups();
- while (subgroups.hasNext())
- {
- ContactGroup grp = subgroups.next();
-
- if (grp.getGroupName().equals(groupName))
- return grp;
- }
-
- return null;
- }
-
- /**
- * 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();
- }
-
- /**
- * Returns the number, which is always 0, 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 dummyContacts.size();
- }
-
- /**
- * 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 dummyContacts.iterator();
- }
-
- /**
- * 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)
- {
- //no contacts in the root group for this yahoo impl.
- return null;
- }
-
-
- /**
- * Returns a string representation of the root contact group that contains
- * all subgroups and subcontacts of this group.
- *
- * @return a string representation of this root contact group.
- */
- @Override
- public String toString()
- {
- StringBuffer buff = new StringBuffer(getGroupName());
- buff.append(".subGroups=" + countSubgroups() + ":\n");
-
- Iterator<ContactGroup> subGroups = subgroups();
- while (subGroups.hasNext())
- {
- ContactGroup group = subGroups.next();
- buff.append(group.toString());
- if (subGroups.hasNext())
- buff.append("\n");
- }
- return buff.toString();
- }
-
- /**
- * 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 protocolProvider;
- }
-
- /**
- * 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 true;
- }
-
- /**
- * Returns null as no persistent data is required and the group name 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 group has been resolved against the
- * server. Unresolved groups 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 groups to their on-line buddies.
- * @return true if the group has been resolved (mapped against a buddy)
- * and false otherwise.
- */
- public boolean isResolved()
- {
- return isResolved;
- }
-
- /**
- * Returns a <tt>String</tt> that uniquely represnets the group. In this we
- * use the name of the group as an identifier. This may cause problems
- * though, in clase the name is changed by some other application between
- * consecutive runs of the sip-communicator.
- *
- * @return a String representing this group in a unique and persistent
- * way.
- */
- public String getUID()
- {
- return getGroupName();
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java
deleted file mode 100644
index e732b84..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/ServerStoredContactListYahooImpl.java
+++ /dev/null
@@ -1,1274 +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.yahoo;
-
-import java.io.*;
-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 ymsg.network.*;
-import ymsg.network.event.*;
-
-/**
- * This class encapsulates the Roster class. Once created, it will
- * register itself as a listener to the encapsulated Roster and modify it's
- * local copy of Contacts and ContactGroups every time an event is generated
- * by the underlying framework. The class would also generate
- * corresponding sip-communicator events to all events coming from smack.
- *
- * @author Damian Minkov
- * @author Emil Ivov
- */
-public class ServerStoredContactListYahooImpl
-{
- private static final Logger logger =
- Logger.getLogger(ServerStoredContactListYahooImpl.class);
-
- /**
- * If there is no group and we add contact with no parent
- * a default group is created with name : DEFAULT_GROUP_NAME
- */
- private static final String DEFAULT_GROUP_NAME = "General";
-
- /**
- * The root contagroup. The container for all yahoo buddies and groups.
- */
- private final RootContactGroupYahooImpl rootGroup;
-
- /**
- * The operation set that created us and that we could use when dispatching
- * subscription events.
- */
- private final OperationSetPersistentPresenceYahooImpl parentOperationSet;
-
- /**
- * The provider that is on top of us.
- */
- private final ProtocolProviderServiceYahooImpl yahooProvider;
-
- private YahooSession yahooSession = null;
-
- /**
- * Listeners that would receive event notifications for changes in group
- * names or other properties, removal or creation of groups.
- */
- private Vector<ServerStoredGroupListener> serverStoredGroupListeners
- = new Vector<ServerStoredGroupListener>();
-
- private ContactListModListenerImpl contactListModListenerImpl
- = new ContactListModListenerImpl();
-
- /**
- * Handler for incoming authorization requests.
- */
- private AuthorizationHandler handler = null;
-
- private Hashtable<String, String> addedCustomYahooIds
- = new Hashtable<String, String>();
-
- /**
- * Creates a ServerStoredContactList wrapper for the specified BuddyList.
- *
- * @param parentOperationSet the operation set that created us and that
- * we could use for dispatching subscription events
- * @param provider the provider that has instantiated us.
- */
- ServerStoredContactListYahooImpl(
- OperationSetPersistentPresenceYahooImpl parentOperationSet,
- ProtocolProviderServiceYahooImpl provider)
- {
- //We need to init these as early as possible to ensure that the provider
- //and the operationsset would not be null in the incoming events.
- this.parentOperationSet = parentOperationSet;
-
- this.yahooProvider = provider;
- this.rootGroup = new RootContactGroupYahooImpl(this.yahooProvider);
- }
-
- /**
- * 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.handler = handler;
- }
-
- /**
- * Returns the root group of the contact list.
- *
- * @return the root ContactGroup for the ContactList
- */
- public ContactGroup getRootGroup()
- {
- return rootGroup;
- }
-
- /**
- * Registers the specified group listener so that it would receive events
- * on group modification/creation/destruction.
- * @param listener the ServerStoredGroupListener to register for group
- * events
- */
- void addGroupListener(ServerStoredGroupListener listener)
- {
- synchronized(serverStoredGroupListeners)
- {
- if(!serverStoredGroupListeners.contains(listener))
- serverStoredGroupListeners.add(listener);
- }
- }
-
- /**
- * Removes the specified group listener so that it won't receive further
- * events on group modification/creation/destruction.
- * @param listener the ServerStoredGroupListener to unregister
- */
- void removeGroupListener(ServerStoredGroupListener listener)
- {
- synchronized(serverStoredGroupListeners)
- {
- this.serverStoredGroupListeners.remove(listener);
- }
- }
-
- /**
- * Creates the corresponding event and notifies all
- * <tt>ServerStoredGroupListener</tt>s that the source group has been
- * removed, changed, renamed or whatever happened to it.
- * @param group the ContactGroup that has been created/modified/removed
- * @param eventID the id of the event to generate.
- */
- private void fireGroupEvent(ContactGroupYahooImpl group, int eventID)
- {
- //bail out if no one's listening
- if(parentOperationSet == null){
- if (logger.isDebugEnabled())
- logger.debug("No presence op. set available. Bailing out.");
- return;
- }
-
- ServerStoredGroupEvent evt = new ServerStoredGroupEvent(
- group
- , eventID
- , parentOperationSet.getServerStoredContactListRoot()
- , yahooProvider
- , parentOperationSet);
-
- if (logger.isTraceEnabled())
- logger.trace("Will dispatch the following grp event: " + evt);
-
- Iterable<ServerStoredGroupListener> listeners;
- synchronized (serverStoredGroupListeners)
- {
- listeners
- = new ArrayList<ServerStoredGroupListener>(
- serverStoredGroupListeners);
- }
-
- for (ServerStoredGroupListener listener : listeners)
- {
- try{
- if (eventID == ServerStoredGroupEvent.GROUP_REMOVED_EVENT)
- listener.groupRemoved(evt);
- else if (eventID == ServerStoredGroupEvent.GROUP_RENAMED_EVENT)
- listener.groupNameChanged(evt);
- else if (eventID == ServerStoredGroupEvent.GROUP_CREATED_EVENT)
- listener.groupCreated(evt);
- else if (eventID == ServerStoredGroupEvent.GROUP_RESOLVED_EVENT)
- listener.groupResolved(evt);
- }catch(Exception ex){
- logger.warn("Unhandled Exception! ", ex);
- }
- }
- }
-
- /**
- * Make the parent persistent presence operation set dispatch a contact
- * removed event.
- * @param parentGroup the group where that the removed contact belonged to.
- * @param contact the contact that was removed.
- */
- private void fireContactRemoved( ContactGroup parentGroup,
- ContactYahooImpl contact)
- {
- //bail out if no one's listening
- if(parentOperationSet == null){
- if (logger.isDebugEnabled())
- logger.debug("No presence op. set available. Bailing out.");
- return;
- }
-
- //dispatch
- parentOperationSet.fireSubscriptionEvent(
- contact, parentGroup, SubscriptionEvent.SUBSCRIPTION_REMOVED);
- }
-
- /**
- * Make the parent persistent presence operation set dispatch a subscription
- * moved event.
- * @param oldParentGroup the group where the source contact was located
- * before being moved
- * @param newParentGroup the group that the source contact is currently in.
- * @param contact the contact that was added
- */
- private void fireContactMoved( ContactGroup oldParentGroup,
- ContactGroupYahooImpl newParentGroup,
- ContactYahooImpl contact)
- {
- //bail out if no one's listening
- if(parentOperationSet == null){
- if (logger.isDebugEnabled())
- logger.debug("No presence op. set available. Bailing out.");
- return;
- }
-
- //dispatch
- parentOperationSet.fireSubscriptionMovedEvent(
- contact, oldParentGroup, newParentGroup);
- }
-
- /**
- * Returns a reference to the provider that created us.
- * @return a reference to a ProtocolProviderServiceImpl instance.
- */
- ProtocolProviderServiceYahooImpl getParentProvider()
- {
- return yahooProvider;
- }
-
- /**
- * Returns the ConntactGroup with the specified name or null if no such
- * group was found.
- * <p>
- * @param name the name of the group we're looking for.
- * @return a reference to the ContactGroupYahooImpl instance we're looking
- * for or null if no such group was found.
- */
- public ContactGroupYahooImpl findContactGroup(String name)
- {
- String nameToLookFor = replaceIllegalChars(name);
- Iterator<ContactGroup> contactGroups = rootGroup.subgroups();
-
- while(contactGroups.hasNext())
- {
- ContactGroupYahooImpl contactGroup
- = (ContactGroupYahooImpl) contactGroups.next();
-
- if (contactGroup.getGroupName().equals(nameToLookFor))
- return contactGroup;
- }
-
- return null;
- }
-
- /**
- * Returns the Contact with the specified id or null if
- * no such id was found.
- *
- * @param id the id of the contact to find.
- * @return the <tt>Contact</tt> carrying the specified
- * <tt>screenName</tt> or <tt>null</tt> if no such contact exits.
- */
- public ContactYahooImpl findContactById(String id)
- {
- Iterator<ContactGroup> contactGroups = rootGroup.subgroups();
- ContactYahooImpl result = null;
-
- while(contactGroups.hasNext())
- {
- ContactGroupYahooImpl contactGroup
- = (ContactGroupYahooImpl) contactGroups.next();
-
- result = contactGroup.findContact(id);
-
- if (result != null)
- return result;
- }
-
- return null;
- }
-
- /**
- * Returns the Contact corresponding to the specified <tt>YahooUser</tt>
- * or null if no such id was found.
- *
- * @param yahooUser the YahooUser of the contact to find.
- * @return the <tt>Contact</tt> carrying the specified
- * <tt>screenName</tt> or <tt>null</tt> if no such contact exits.
- */
- public ContactYahooImpl findContactByYahooUser(YahooUser yahooUser)
- {
- return findContactById(yahooUser.getId().toLowerCase());
- }
-
- /**
- * Returns the ContactGroup containing the specified contact or null
- * if no such group or contact exist.
- *
- * @param child the contact whose parent group we're looking for.
- * @return the <tt>ContactGroup</tt> containing the specified
- * <tt>contact</tt> or <tt>null</tt> if no such groupo or contact
- * exist.
- */
- public ContactGroup findContactGroup(ContactYahooImpl child)
- {
- Iterator<ContactGroup> contactGroups = rootGroup.subgroups();
- String contactAddress = child.getAddress();
-
- while(contactGroups.hasNext())
- {
- ContactGroupYahooImpl contactGroup
- = (ContactGroupYahooImpl) contactGroups.next();
-
- if( contactGroup.findContact(contactAddress)!= null)
- return contactGroup;
- }
-
- return null;
- }
-
- /**
- * Adds a new contact with the specified screenname to the list under a
- * default location.
- * @param id the id of the contact to add.
- * @throws OperationFailedException
- */
- public void addContact(String id)
- throws OperationFailedException
- {
- ContactGroupYahooImpl parent = getFirstPersistentGroup();
-
- if(parent == null)
- {
- // if there is no group create it
- parent = createUnresolvedContactGroup(DEFAULT_GROUP_NAME);
- }
-
- addContact(parent, id);
- }
-
- /**
- * Adds a new contact with the specified screenname to the list under the
- * specified group.
- * @param id the id of the contact to add.
- * @param parent the group under which we want the new contact placed.
- * @throws OperationFailedException if the contact already exist
- */
- public void addContact(final ContactGroupYahooImpl parent, String id)
- throws OperationFailedException
- {
- if (logger.isTraceEnabled())
- logger.trace("Adding contact " + id + " to parent=" + parent);
-
- //if the contact is already in the contact list and is not volatile,
- //then only broadcast an event
- ContactYahooImpl existingContact = findContactById(id);
-
- if( existingContact != null
- && existingContact.isPersistent() )
- {
- if (logger.isDebugEnabled())
- logger.debug("Contact " + id + " already exists.");
- throw new OperationFailedException(
- "Contact " + id + " already exists.",
- OperationFailedException.SUBSCRIPTION_ALREADY_EXISTS);
- }
-
- if(id.indexOf("@") > -1 )
- addedCustomYahooIds.put(YahooSession.getYahooUserID(id), id);
-
- try
- {
- yahooSession.addFriend(YahooSession.getYahooUserID(id),
- parent.getGroupName());
- }
- catch(IOException ex)
- {
- throw new OperationFailedException(
- "Contact cannot be added " + id,
- OperationFailedException.NETWORK_FAILURE);
- }
- }
-
- /**
- * 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.
- * @param id the address of the contact to create.
- * @return the newly created volatile <tt>ContactImpl</tt>
- */
- ContactYahooImpl createVolatileContact(String id)
- {
- if (logger.isTraceEnabled())
- logger.trace("Creating volatile contact " + id);
- ContactYahooImpl newVolatileContact =
- new ContactYahooImpl(id, this, false, false, true);
-
- //Check whether a volatile group already exists and if not create one
- ContactGroupYahooImpl theVolatileGroup = getNonPersistentGroup();
-
- //if the parent group is null then create it
- if (theVolatileGroup == null)
- {
- theVolatileGroup = new VolatileContactGroupYahooImpl(
- YahooActivator.getResources().getI18NString(
- "service.gui.NOT_IN_CONTACT_LIST_GROUP_NAME"),
- this);
-
- theVolatileGroup.addContact(newVolatileContact);
-
- this.rootGroup.addSubGroup(theVolatileGroup);
-
- fireGroupEvent(theVolatileGroup
- , ServerStoredGroupEvent.GROUP_CREATED_EVENT);
- }
- else
- {
- theVolatileGroup.addContact(newVolatileContact);
-
- fireContactAdded(theVolatileGroup, newVolatileContact);
- }
-
- return newVolatileContact;
- }
-
-
- /**
- * Creates a non resolved contact for the specified address and inside the
- * specified group. The newly created contact would be added to the local
- * contact list as a standard contact but when an event is received from the
- * server concerning this contact, then it will be reused and only its
- * isResolved field would be updated instead of creating the whole contact
- * again.
- *
- * @param parentGroup the group where the unersolved contact is to be
- * created
- * @param id the Address of the contact to create.
- * @return the newly created unresolved <tt>ContactImpl</tt>
- */
- ContactYahooImpl createUnresolvedContact(ContactGroup parentGroup,
- String id)
- {
- if (logger.isTraceEnabled())
- logger.trace("Creating unresolved contact " + id
- + " to parent=" + parentGroup);
-
- ContactYahooImpl existingContact = findContactById(id);
-
- if( existingContact != null)
- {
- return existingContact;
- }
-
- ContactYahooImpl newUnresolvedContact
- = new ContactYahooImpl(id, this, false, true, false);
-
- if(parentGroup instanceof ContactGroupYahooImpl)
- ((ContactGroupYahooImpl)parentGroup).
- addContact(newUnresolvedContact);
-
- fireContactAdded(parentGroup, newUnresolvedContact);
-
- return newUnresolvedContact;
- }
-
- /**
- * Creates a non resolved contact group for the specified name. The newly
- * created group would be added to the local contact list as any other group
- * but when an event is received from the server concerning this group, then
- * it will be reused and only its isResolved field would be updated instead
- * of creating the whole group again.
- * <p>
- * @param groupName the name of the group to create.
- * @return the newly created unresolved <tt>ContactGroupImpl</tt>
- */
- ContactGroupYahooImpl createUnresolvedContactGroup(String groupName)
- {
- ContactGroupYahooImpl existingGroup = findContactGroup(groupName);
-
- if( existingGroup != null )
- {
- if (logger.isDebugEnabled())
- logger.debug("ContactGroup " + groupName + " already exists.");
- return existingGroup;
- }
-
- ContactGroupYahooImpl newUnresolvedGroup =
- new ContactGroupYahooImpl(groupName, this);
-
- this.rootGroup.addSubGroup(newUnresolvedGroup);
-
- fireGroupEvent(newUnresolvedGroup
- , ServerStoredGroupEvent.GROUP_CREATED_EVENT);
-
- return newUnresolvedGroup;
- }
-
- /**
- * Creates the specified group on the server stored contact list.
- * @param groupName a String containing the name of the new group.
- * @throws OperationFailedException with code CONTACT_GROUP_ALREADY_EXISTS
- * if the group we're trying to create is already in our contact list.
- */
- public void createGroup(String groupName)
- throws OperationFailedException
- {
- if (logger.isTraceEnabled())
- logger.trace("Creating group: " + groupName);
-
- ContactGroupYahooImpl existingGroup = findContactGroup(groupName);
-
- if( existingGroup != null && existingGroup.isPersistent() )
- {
- if (logger.isDebugEnabled())
- logger.debug("ContactGroup " + groupName + " already exists.");
- throw new OperationFailedException(
- "ContactGroup " + groupName + " already exists.",
- OperationFailedException.CONTACT_GROUP_ALREADY_EXISTS);
- }
-
- // create unresolved group if friend is added - group will be resolved
- createUnresolvedContactGroup(groupName);
- }
-
- /**
- * Removes the specified group from the buddy list.
- * @param groupToRemove the group that we'd like removed.
- */
- @SuppressWarnings("unchecked") //jymsg legacy code
- public void removeGroup(ContactGroupYahooImpl groupToRemove)
- {
- // to remove group just remove all the contacts in it
-
- if (logger.isTraceEnabled())
- logger.trace("removing group " + groupToRemove);
-
- // if its not persistent group just remove it
- if(!groupToRemove.isPersistent() || !groupToRemove.isResolved())
- {
- rootGroup.removeSubGroup(groupToRemove);
- fireGroupEvent(groupToRemove,
- ServerStoredGroupEvent.GROUP_REMOVED_EVENT);
- return;
- }
-
- Vector<YahooUser> contacts
- = groupToRemove.getSourceGroup().getMembers();
-
- if(contacts.size() == 0)
- {
- // the group is empty just remove it
- rootGroup.removeSubGroup(groupToRemove);
- fireGroupEvent(groupToRemove,
- ServerStoredGroupEvent.GROUP_REMOVED_EVENT);
- return;
- }
-
- /*
- * ContactGroupYahooImpl#getGroupName() isn't a plain getter so
- * performance-wise we're better off not calling it multiple times in
- * the following loop.
- */
- String groupToRemoveName = groupToRemove.getGroupName();
-
- for (YahooUser item : contacts)
- {
- try
- {
- yahooSession.removeFriend(item.getId(), groupToRemoveName);
- }
- catch(IOException ex)
- {
- if (logger.isInfoEnabled())
- logger.info("Cannot Remove contact " + item.getId());
- }
- }
- }
-
- /**
- * Removes a contact from the serverside list
- * Event will come for successful operation
- * @param contactToRemove ContactYahooImpl
- */
- void removeContact(ContactYahooImpl contactToRemove)
- {
- if (logger.isTraceEnabled())
- logger.trace("Removing yahoo contact "
- + contactToRemove.getSourceContact());
-
- if(contactToRemove.isVolatile())
- {
- ContactGroupYahooImpl parent =
- (ContactGroupYahooImpl)contactToRemove.getParentContactGroup();
-
- parent.removeContact(contactToRemove);
- fireContactRemoved(parent, contactToRemove);
- return;
- }
-
- try
- {
- yahooSession.removeFriend(
- contactToRemove.getSourceContact().getId(),
- contactToRemove.getParentContactGroup().getGroupName());
- }
- catch(IOException ex)
- {
- if (logger.isInfoEnabled())
- logger.info("Cannot Remove contact " + contactToRemove);
- }
- }
-
- /**
- * Renames the specified group according to the specified new name..
- * @param groupToRename the group that we'd like removed.
- * @param newName the new name of the group
- */
- public void renameGroup(ContactGroupYahooImpl groupToRename, String newName)
- {
- // not working
- /*
- try
- {
- yahooSession.renameGroup(groupToRename.getGroupName(), newName);
- }
- catch(IOException ex)
- {
- if (logger.isInfoEnabled())
- logger.info("Cannot rename group " + groupToRename);
- }
-
- fireGroupEvent(groupToRename,
- ServerStoredGroupEvent.GROUP_RENAMED_EVENT);
- */
- }
-
- /**
- * Moves the specified <tt>contact</tt> to the group indicated by
- * <tt>newParent</tt>.
- * @param contact the contact that we'd like moved under the new group.
- * @param newParent the group where we'd like the parent placed.
- */
- public void moveContact(ContactYahooImpl contact,
- ContactGroupYahooImpl newParent)
- {
- String userID = contact.getID();
- try
- {
- contactListModListenerImpl.
- waitForMove(userID,
- contact.getParentContactGroup().getGroupName());
-
- yahooSession.addFriend(
- userID,
- newParent.getGroupName());
- }
- catch(IOException ex)
- {
- contactListModListenerImpl.removeWaitForMove(userID);
- logger.error("Contact cannot be added " + ex.getMessage());
- }
- }
-
- /**
- * Returns the volatile group
- *
- * @return ContactGroupYahooImpl
- */
- private ContactGroupYahooImpl getNonPersistentGroup()
- {
- for (int i = 0; i < getRootGroup().countSubgroups(); i++)
- {
- ContactGroupYahooImpl gr =
- (ContactGroupYahooImpl)getRootGroup().getGroup(i);
-
- if(!gr.isPersistent())
- return gr;
- }
-
- return null;
- }
-
- /**
- * Returns the first persistent group
- *
- * @return ContactGroupIcqImpl
- */
- private ContactGroupYahooImpl getFirstPersistentGroup()
- {
- for (int i = 0; i < getRootGroup().countSubgroups(); i++)
- {
- ContactGroupYahooImpl gr =
- (ContactGroupYahooImpl)getRootGroup().getGroup(i);
-
- if(gr.isPersistent())
- return gr;
- }
-
- return null;
- }
-
- /**
- * Make the parent persistent presence operation set dispatch a contact
- * added event.
- * @param parentGroup the group where the new contact was added
- * @param contact the contact that was added
- */
- void fireContactAdded( ContactGroup parentGroup,
- ContactYahooImpl contact)
- {
- //bail out if no one's listening
- if(parentOperationSet == null){
- if (logger.isDebugEnabled())
- logger.debug("No presence op. set available. Bailing out.");
- return;
- }
-
- //dispatch
- parentOperationSet.fireSubscriptionEvent(
- contact, parentGroup, SubscriptionEvent.SUBSCRIPTION_CREATED);
- }
-
- /**
- * Make the parent persistent presence operation set dispatch a contact
- * resolved event.
- * @param parentGroup the group that the resolved contact belongs to.
- * @param contact the contact that was resolved
- */
- void fireContactResolved( ContactGroup parentGroup,
- ContactYahooImpl contact)
- {
- //bail out if no one's listening
- if(parentOperationSet == null){
- if (logger.isDebugEnabled())
- logger.debug("No presence op. set available. Bailing out.");
- return;
- }
-
- //dispatch
- parentOperationSet.fireSubscriptionEvent(
- contact, parentGroup, SubscriptionEvent.SUBSCRIPTION_RESOLVED);
- }
-
- /**
- * When the protocol is online this method is used to fill or resolve
- * the current contact list
- */
- @SuppressWarnings("unchecked") //jymsg legacy code
- private void initList()
- {
- if (logger.isTraceEnabled())
- logger.trace("Start init list of "
- + yahooProvider.getAccountID().getUserID());
-
- for (YahooGroup item : yahooSession.getGroups())
- {
- ContactGroupYahooImpl group = findContactGroup(item.getName());
-
- if(group == null)
- {
- // create the group as it doesn't exist
- group = new ContactGroupYahooImpl(
- item, item.getMembers(), this, true);
-
- rootGroup.addSubGroup(group);
-
- //tell listeners about the added group
- fireGroupEvent(group,
- ServerStoredGroupEvent.GROUP_CREATED_EVENT);
- }
- else
- {
- // the group exist so just resolved. The group will check and
- // create or resolve its entries
- group.setResolved(item);
-
- //fire an event saying that the group has been resolved
- fireGroupEvent(group
- , ServerStoredGroupEvent.GROUP_RESOLVED_EVENT);
-
- /** @todo if something to delete . delete it */
- }
-
- if (logger.isTraceEnabled())
- logger.trace("Init of group done! : " + group);
- }
- }
-
- /**
- * @param name Name of the group to search
- * @return The yahoo group with given name
- */
- private YahooGroup findGroup(String name)
- {
- for (YahooGroup elem : yahooSession.getGroups())
- {
- if(elem.getName().equals(name))
- return elem;
- }
- return null;
- }
-
- /**
- * Process incoming authorization requests.
- * @param ev the event to process.
- */
- void processAuthorizationRequest(SessionAuthorizationEvent ev)
- {
- if(handler == null)
- return;
-
- Contact srcContact = findContactById(ev.getFrom());
-
- // if there is no such contact we create it as
- // volatile so we can fire notification
- // and then if accepted add it in the protocol
- // so we can receive its states
- boolean isCurrentlyCreated = false;
- if(srcContact == null)
- {
- srcContact = createVolatileContact(ev.getFrom());
- isCurrentlyCreated = true;
- }
-
- AuthorizationRequest authRequest = new AuthorizationRequest();
- authRequest.setReason(ev.getMessage());
-
- AuthorizationResponse authResponse =
- handler.processAuthorisationRequest(
- authRequest, srcContact);
-
- if (authResponse.getResponseCode() == AuthorizationResponse.IGNORE)
- {
- return;
- }
- else if (authResponse.getResponseCode() == AuthorizationResponse.REJECT)
- {
- removeContact((ContactYahooImpl)srcContact);
- try
- {
- yahooSession.rejectFriendAuthorization(
- ev, ev.getFrom(), authResponse.getReason());
- }
- catch(IOException ex)
- {
- logger.error("cannot send auth deny", ex);
- }
-
- return;
- }
-
- // else we accepted it
- try
- {
- yahooSession.acceptFriendAuthorization(ev, ev.getFrom());
- }
- catch(IOException ex)
- {
- logger.error("cannot send auth deny", ex);
- }
-
- if(isCurrentlyCreated)
- try
- {
- addContact(ev.getFrom());
- }
- catch (OperationFailedException ex)
- {
- logger.error("Cannot add friend", ex);
- }
- }
-
- /**
- * Imulates firing adding contact in group and moving contact to group.
- * When moving contact it is first adding to the new group then
- * it is removed from the old one.
- */
- private class ContactListModListenerImpl
- extends SessionAdapter
- {
- private final Hashtable<String, Object> waitMove
- = new Hashtable<String, Object>();
-
- public void waitForMove(String id, String oldParent)
- {
- waitMove.put(id, oldParent);
- }
-
- public void removeWaitForMove(String id)
- {
- waitMove.remove(id);
- }
-
- /**
- * Successfully added a friend
- * friend - YahooUser of friend
- * group - name of group added to
- * @param ev fired event
- */
- @Override
- public void friendAddedReceived(SessionFriendEvent ev)
- {
- if (logger.isTraceEnabled())
- logger.trace("Receive event for adding a friend : " + ev);
-
- ContactGroupYahooImpl group =
- findContactGroup(ev.getGroup());
-
- if(group == null){
- if (logger.isTraceEnabled())
- logger.trace("Group not found!" + ev.getGroup());
- return;
- }
-
- String contactID = ev.getFriend().getId();
- ContactYahooImpl contactToAdd = findContactById(contactID);
-
- // if group is note resolved resolve it
- // this means newly created group
- if(!group.isResolved())
- {
- // if the contact is volatile me must remove it
- // as new one will be created
- if(contactToAdd != null && contactToAdd.isVolatile())
- {
- ContactGroupYahooImpl parent
- = (ContactGroupYahooImpl)contactToAdd
- .getParentContactGroup();
-
- parent.removeContact(contactToAdd);
- fireContactRemoved(parent, contactToAdd);
- }
-
- YahooGroup gr = findGroup(ev.getGroup());
-
- if(gr != null)
- group.setResolved(gr);
-
- // contact will be added when resolving the group
-
- return;
- }
-
-
- boolean isVolatile = false;
-
- if(contactToAdd == null)
- {
- if(addedCustomYahooIds.containsKey(contactID))
- {
- String expectedContactID =
- addedCustomYahooIds.remove(contactID);
-
- contactToAdd =
- new ContactYahooImpl(expectedContactID, ev.getFriend(),
- ServerStoredContactListYahooImpl.this, true, true);
- }
- else
- {
- contactToAdd =
- new ContactYahooImpl(ev.getFriend(),
- ServerStoredContactListYahooImpl.this, true, true);
- }
- }
- else
- {
- isVolatile = contactToAdd.isVolatile();
- }
-
- //first check is contact is moving from a group
- Object isWaitingForMove = waitMove.get(contactID);
-
- if(isWaitingForMove != null && isWaitingForMove instanceof String)
- {
- // waits for move into group
- // will remove it from old group and will wait for event remove
- // from group, then will fire moved to group event
- String oldParent = (String)isWaitingForMove;
-
- group.addContact(contactToAdd);
- waitMove.put(contactID, group.getSourceGroup());
- try
- {
- yahooSession.removeFriend(contactID, oldParent);
- }
- catch(IOException ex)
- {
- if (logger.isInfoEnabled())
- logger.info("Cannot Remove(till moving) contact :" +
- contactToAdd + " from group " + oldParent);
- }
- return;
- }
-
- if(isVolatile)
- {
- // we must remove the volatile buddy as we will add
- // the persistent one.
- // Volatile buddy is moving from the volatile group
- // to the new one
- ContactGroupYahooImpl parent =
- (ContactGroupYahooImpl)contactToAdd.getParentContactGroup();
-
- parent.removeContact(contactToAdd);
- fireContactRemoved(parent, contactToAdd);
-
- contactToAdd.setPersistent(true);
- contactToAdd.setResolved(ev.getFriend());
-
- group.addContact(contactToAdd);
-
- fireContactAdded(group, contactToAdd);
- waitMove.remove(contactID);
-
- return;
- }
-
- group.addContact(contactToAdd);
- fireContactAdded(group, contactToAdd);
- }
-
- /**
- * Successfully removed a friend
- * friend - YahooUser of friend
- * group - name of group removed from
- * @param ev fired event
- */
- @Override
- public void friendRemovedReceived(SessionFriendEvent ev)
- {
- if (logger.isTraceEnabled())
- logger.trace("Receive event for removing a friend : " + ev);
-
- String contactID = ev.getFriend().getId();
-
- // first check is this part of move action
- Object waitForMoveObj = waitMove.get(contactID);
- if(waitForMoveObj != null && waitForMoveObj instanceof YahooGroup)
- {
- // first get the group - oldParent
- ContactGroupYahooImpl oldParent
- = findContactGroup(ev.getGroup());
- ContactYahooImpl contactToRemove
- = oldParent.findContact(contactID);
-
- oldParent.removeContact(contactToRemove);
- waitMove.remove(contactID);
-
- ContactGroupYahooImpl newParent =
- findContactGroup(((YahooGroup)waitForMoveObj).getName());
-
- fireContactMoved(oldParent, newParent, contactToRemove);
- return;
- }
-
- ContactYahooImpl contactToRemove = findContactById(contactID);
-
- // strange we cannot find the contact to be removed
- if(contactToRemove == null)
- return;
-
- ContactGroupYahooImpl parentGroup =
- (ContactGroupYahooImpl)contactToRemove.
- getParentContactGroup();
- parentGroup.removeContact(contactToRemove);
- fireContactRemoved(parentGroup, contactToRemove);
-
- // check if the group is deleted. If the contact is the last one in
- // the group. The group is also deleted
- if(findGroup(ev.getGroup()) == null)
- {
- rootGroup.removeSubGroup(parentGroup);
- fireGroupEvent(parentGroup,
- ServerStoredGroupEvent.GROUP_REMOVED_EVENT);
- }
- }
-
- /**
- * Someone wants to add us to their friends list
- * to - the target (us!)
- * from - the user who wants to add us
- * message - the request message text
- * @param ev fired event
- */
- @Override
- public void contactRequestReceived(SessionEvent ev)
- {
- if (logger.isInfoEnabled())
- logger.info("contactRequestReceived : " + ev);
-
- if(handler == null || ev.getFrom() == null)
- return;
-
- ContactYahooImpl contact = findContactById(ev.getFrom());
-
- if(contact == null)
- contact = createVolatileContact(ev.getFrom());
-
- AuthorizationRequest request = new AuthorizationRequest();
- request.setReason(ev.getMessage());
-
- AuthorizationResponse resp =
- handler.processAuthorisationRequest(request, contact);
-
- if (resp.getResponseCode() == AuthorizationResponse.REJECT)
- {
- try{
- yahooSession.rejectContact(ev, resp.getReason());
- }catch(IOException ex){
- logger.error("Cannot send reject : " + ex.getMessage());
- }
- }
- }
-
- /**
- * Someone has rejected our attempts to add them to our friends list
- * from - the user who rejected us
- * message - rejection message text
- * @param ev fired event
- */
- @Override
- public void contactRejectionReceived(SessionEvent ev)
- {
- if (logger.isInfoEnabled())
- logger.info("contactRejectionReceived : " + ev);
-
- if(handler == null)
- return;
-
- ContactYahooImpl contact = findContactById(ev.getFrom());
-
- AuthorizationResponse resp =
- new AuthorizationResponse(AuthorizationResponse.REJECT,
- ev.getMessage());
- handler.processAuthorizationResponse(resp, contact);
- }
-
- /**
- * Invoked on picture received.
- * @param ev fired event
- */
- @Override
- public void pictureReceived(SessionPictureEvent ev)
- {
- ContactYahooImpl contact = findContactById(ev.getFrom());
-
- if(contact == null)
- return;
-
- contact.setImage(ev.getPictureData());
-
- parentOperationSet.fireContactPropertyChangeEvent(
- ContactPropertyChangeEvent.PROPERTY_IMAGE,
- contact, null, ev.getPictureData());
- }
-
- /**
- * Process Authorization responses
- * @param ev the event to process
- */
- @Override
- public void authorizationReceived(SessionAuthorizationEvent ev)
- {
- if(ev.isAuthorizationAccepted())
- {
- if (logger.isTraceEnabled())
- logger.trace("authorizationAccepted from " + ev.getFrom());
- Contact srcContact = findContactById(ev.getFrom());
-
- if(srcContact == null)
- if (logger.isTraceEnabled())
- logger.trace("No contact found");
- else
- handler.processAuthorizationResponse(
- new AuthorizationResponse(
- AuthorizationResponse.ACCEPT,
- ev.getMessage()),
- srcContact);
- }
- else if(ev.isAuthorizationDenied())
- {
- if (logger.isTraceEnabled())
- logger.trace("authorizationDenied from " + ev.getFrom());
- Contact srcContact = findContactById(ev.getFrom());
-
- if(srcContact == null)
- if (logger.isTraceEnabled())
- logger.trace("No contact found");
- else
- {
- handler.processAuthorizationResponse(
- new AuthorizationResponse(
- AuthorizationResponse.REJECT,
- ev.getMessage()),
- srcContact);
- try
- {
- removeContact((ContactYahooImpl)srcContact);
- } catch (Exception ex)
- {
- logger.error("cannot remove denied contact : " +
- srcContact, ex);
- }
- }
- }
- else if(ev.isAuthorizationRequest())
- {
- if (logger.isTraceEnabled())
- logger.trace("authorizationRequestReceived from "
- + ev.getFrom());
- processAuthorizationRequest(ev);
- }
- }
- }
-
- /**
- * Sets the yahoo session instance of the lib
- * which comunicates with the server
- * @param session YahooSession
- */
- void setYahooSession(YahooSession session)
- {
- this.yahooSession = session;
- session.addSessionListener(contactListModListenerImpl);
- initList();
- }
-
- /**
- * It seems that ymsg (or the Yahoo! service itself as the problem also
- * appears with libpurple) would return illegal chars for names that were
- * entered in cyrillic. We use this method to translate their names into
- * something that we could actually display and store here.
- *
- * @param ymsgString the <tt>String</tt> containing illegal chars.
- *
- * @return a String where all illegal chars are converted into human
- * readable ones
- */
- static String replaceIllegalChars(String ymsgString)
- {
- return ymsgString.replace((char)26, '?');
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java b/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java
deleted file mode 100644
index e2c589a..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/VolatileContactGroupYahooImpl.java
+++ /dev/null
@@ -1,97 +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.yahoo;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * The Yahoo implementation of the Volatile ContactGroup interface.
- *
- * @author Damian Minkov
- */
-public class VolatileContactGroupYahooImpl
- extends ContactGroupYahooImpl
-{
- /**
- * This contact group name
- */
- private String contactGroupName = null;
-
- /**
- * Creates an Yahoo group using the specified group name
- * @param groupName String groupname
- * @param ssclCallback a callback to the server stored contact list
- * we're creating.
- */
- VolatileContactGroupYahooImpl(
- String groupName,
- ServerStoredContactListYahooImpl ssclCallback)
- {
- super(groupName, ssclCallback);
- this.contactGroupName = groupName;
- }
-
- /**
- * Returns the name of this group.
- * @return a String containing the name of this group.
- */
- @Override
- public String getGroupName()
- {
- return contactGroupName;
- }
-
- /**
- * Returns a string representation of this group, in the form
- * YahooGroup.GroupName[size]{ buddy1.toString(), buddy2.toString(), ...}.
- * @return a String representation of the object.
- */
- @Override
- public String toString()
- {
- StringBuffer buff = new StringBuffer("VolatileYahooGroup.");
- buff.append(getGroupName());
- buff.append(", childContacts="+countContacts()+":[");
-
- Iterator<Contact> contacts = contacts();
-
- while (contacts.hasNext())
- {
- Contact contact = contacts.next();
-
- buff.append(contact.toString());
- if(contacts.hasNext())
- buff.append(", ");
- }
- return buff.append("]").toString();
- }
-
- /**
- * 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.
- */
- @Override
- public boolean isPersistent()
- {
- return false;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java
deleted file mode 100644
index 0eb0e86..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooAccountID.java
+++ /dev/null
@@ -1,42 +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.yahoo;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * The Yahoo implementation of a sip-communicator AccountID
- *
- * @author Damian Minkov
- */
-public class YahooAccountID
- extends AccountID
-{
- /**
- * Creates an account id from the specified id and account properties.
- * @param id the id identifying this account
- * @param accountProperties any other properties necessary for the account.
- */
- YahooAccountID(String id, Map<String, String> accountProperties )
- {
- super(YahooSession.getYahooUserID(id),
- accountProperties, ProtocolNames.YAHOO, "yahoo.com");
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java
deleted file mode 100644
index 1e7ca01..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooActivator.java
+++ /dev/null
@@ -1,145 +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.yahoo;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.protocol.*;
-
-import org.jitsi.service.configuration.*;
-import org.jitsi.service.resources.*;
-import org.osgi.framework.*;
-
-/**
- * Loads the Yahoo provider factory and registers it with service in the OSGI
- * bundle context.
- *
- * @author Damian Minkov
- */
-public class YahooActivator
- implements BundleActivator
-{
- private ServiceRegistration yahooPpFactoryServReg = null;
- private static BundleContext bundleContext = null;
- private static ConfigurationService configurationService = null;
-
- private static ProtocolProviderFactoryYahooImpl yahooProviderFactory = null;
-
- private static ResourceManagementService resourcesService;
-
- /**
- * Called when this bundle is started so the Framework can perform the
- * bundle-specific activities necessary to start this bundle.
- *
- * @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
- {
- bundleContext = context;
-
- Hashtable<String, String> hashtable = new Hashtable<String, String>();
- hashtable.put(ProtocolProviderFactory.PROTOCOL, ProtocolNames.YAHOO);
-
- yahooProviderFactory = new ProtocolProviderFactoryYahooImpl();
-
- //reg the yahoo account man.
- yahooPpFactoryServReg = context.registerService(
- ProtocolProviderFactory.class.getName(),
- yahooProviderFactory,
- hashtable);
- }
-
- /**
- * Returns a reference to a ConfigurationService implementation currently
- * registered in the bundle context or null if no such implementation was
- * found.
- *
- * @return ConfigurationService a currently valid implementation of the
- * configuration service.
- */
- public static ConfigurationService getConfigurationService()
- {
- if(configurationService == null)
- {
- ServiceReference confReference
- = bundleContext.getServiceReference(
- ConfigurationService.class.getName());
- configurationService
- = (ConfigurationService) bundleContext.getService(confReference);
- }
- return configurationService;
- }
-
- /**
- * Returns a reference to 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>ProtocolProviderFactoryYahooImpl</tt>
- * instance that we have registered from this package.
- */
- static ProtocolProviderFactoryYahooImpl getProtocolProviderFactory()
- {
- return yahooProviderFactory;
- }
-
- /**
- * 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
- {
- yahooProviderFactory.stop();
- yahooPpFactoryServReg.unregister();
- }
-
- public static ResourceManagementService getResources()
- {
- if (resourcesService == null)
- {
- ServiceReference serviceReference = bundleContext
- .getServiceReference(ResourceManagementService.class.getName());
-
- if(serviceReference == null)
- return null;
-
- resourcesService = (ResourceManagementService) bundleContext
- .getService(serviceReference);
- }
-
- return resourcesService;
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java b/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java
deleted file mode 100644
index f24e50c..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/YahooSession.java
+++ /dev/null
@@ -1,76 +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.yahoo;
-
-import java.io.*;
-
-import ymsg.network.*;
-
-/**
- * Extends The Yahoo session to have access to some
- * protected functionality
- * Not working for now.
- *
- * @author Damian Minkov
- */
-public class YahooSession
- extends Session
-{
- /**
- * Renames a group. Not working for now
- */
- public void renameGroup(String oldName, String newName)
- throws IOException
- {
- transmitGroupRename(oldName, newName);
- }
-
- /**
- * Removes the server part from the given id
- */
- public static String getYahooUserID(String id)
- {
- return (id.indexOf("@") > -1 )
- ? id.substring(0, id.indexOf("@"))
- : id;
- }
-
- /**
- * Sending typing notifications
- * @param to user we are notifing
- * @param from our user id
- */
- void keyTyped(String to, String from)
- {
- try {
- transmitNotify(to, from, true, " ", NOTIFY_TYPING);
- }catch(IOException e){}
- }
-
- /**
- * Sending stop typing notifications
- * @param to user we are notifing
- * @param from our user id
- */
- void stopTyping(String to, String from)
- {
- try {
- transmitNotify(to, from, false, " ", NOTIFY_TYPING);
- }catch(IOException e){}
- }
-}
diff --git a/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf
deleted file mode 100644
index 39e23bf..0000000
--- a/src/net/java/sip/communicator/impl/protocol/yahoo/yahoo.provider.manifest.mf
+++ /dev/null
@@ -1,22 +0,0 @@
-Bundle-Activator: net.java.sip.communicator.impl.protocol.yahoo.YahooActivator
-Bundle-Name: Yahoo Protocol Provider Implementation
-Bundle-Description: An Yahoo implementation of the Protocol Provider Service.
-Bundle-Vendor: jitsi.org
-Bundle-Version: 0.0.1
-Bundle-SymbolicName: net.java.sip.communicator.protocol.yahoo
-Import-Package: org.osgi.framework,
- javax.net.ssl,
- javax.swing,
- javax.swing.text,
- javax.xml.parsers,
- javax.naming,
- javax.naming.directory,
- org.xml.sax,
- sun.security.action,
- org.jitsi.service.configuration,
- org.jitsi.service.resources, net.java.sip.communicator.service.resources,
- net.java.sip.communicator.util,
- net.java.sip.communicator.service.dns,
- net.java.sip.communicator.service.protocol,
- net.java.sip.communicator.service.protocol.yahooconstants,
- net.java.sip.communicator.service.protocol.event
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 &lt; PROBING_2 &lt; PROBING_3 &lt; ANNOUNCING_1 &lt;
- * ANNOUNCING_2 &lt; RESPONDING &lt; ANNOUNCED &lt; 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
diff --git a/src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java b/src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java
index 48bb362..3bdaaa4 100644
--- a/src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java
+++ b/src/net/java/sip/communicator/impl/replacement/directimage/ReplacementServiceDirectImageImpl.java
@@ -45,7 +45,7 @@ public class ReplacementServiceDirectImageImpl
* The regex used to match the link in the message.
*/
public static final String URL_PATTERN =
- "https?\\:\\/\\/(www\\.)*.*\\.(?:jpg|png|gif)";
+ "https?\\:\\/\\/.*\\.(?:jpg|png|gif)";
/**
* Configuration label shown in the config form.
diff --git a/src/net/java/sip/communicator/impl/resources/ResourceManagementActivator.java b/src/net/java/sip/communicator/impl/resources/ResourceManagementActivator.java
index 6c76906..1e7e7e4 100644
--- a/src/net/java/sip/communicator/impl/resources/ResourceManagementActivator.java
+++ b/src/net/java/sip/communicator/impl/resources/ResourceManagementActivator.java
@@ -19,6 +19,7 @@ package net.java.sip.communicator.impl.resources;
import net.java.sip.communicator.util.*;
+import org.jitsi.service.configuration.*;
import org.jitsi.service.resources.*;
import org.osgi.framework.*;
@@ -31,6 +32,7 @@ public class ResourceManagementActivator
extends SimpleServiceActivator<ResourceManagementServiceImpl>
{
static BundleContext bundleContext;
+ private static ConfigurationService configService;
/**
* Creates new instance of <tt>ResourceManagementActivator</tt>
@@ -68,4 +70,22 @@ public class ResourceManagementActivator
{
return new ResourceManagementServiceImpl();
}
+
+ /**
+ * Returns the <tt>ConfigurationService</tt> obtained from the bundle
+ * context.
+ * @return the <tt>ConfigurationService</tt> obtained from the bundle
+ * context
+ */
+ public static ConfigurationService getConfigService()
+ {
+ if(configService == null)
+ {
+ configService
+ = ServiceUtils.getService(
+ bundleContext,
+ ConfigurationService.class);
+ }
+ return configService;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java b/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java
index d116fea..1cc2dcf 100644
--- a/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/resources/ResourceManagementServiceImpl.java
@@ -344,4 +344,24 @@ public class ResourceManagementServiceImpl
{
return SkinJarBuilder.createBundleFromZip(zipFile, getImagePack());
}
+
+ /**
+ * Gets the specified setting from the config service if present, otherwise
+ * from the embedded resources (resources/config/defaults.properties).
+ *
+ * @param key The setting to lookup.
+ * @return The setting for the key or {@code null} if not found.
+ */
+ @Override
+ public String getSettingsString(String key)
+ {
+ Object configValue = ResourceManagementActivator
+ .getConfigService().getProperty(key);
+ if (configValue == null)
+ {
+ configValue = super.getSettingsString(key);
+ }
+
+ return configValue == null ? null : configValue.toString();
+ }
}
diff --git a/src/net/java/sip/communicator/impl/sysactivity/DBusNetworkManager.java b/src/net/java/sip/communicator/impl/sysactivity/DBusNetworkManager.java
index 4c148df..c75e941 100644
--- a/src/net/java/sip/communicator/impl/sysactivity/DBusNetworkManager.java
+++ b/src/net/java/sip/communicator/impl/sysactivity/DBusNetworkManager.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,133 +15,133 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.sysactivity;
-
-import org.freedesktop.dbus.*;
-import org.freedesktop.dbus.exceptions.*;
-
-/**
- * NetworkManager D-Bus Interface
- *
- * @author Damian Minkov
- * @author Ingo Bauersachs
- */
-@DBusInterfaceName("org.freedesktop.NetworkManager")
-public interface DBusNetworkManager
- extends DBusInterface
-{
- /*
- * Types of NetworkManager states for versions < 0.9
- */
- public static final int NM_STATE_UNKNOWN = 0;
- public static final int NM_STATE_ASLEEP = 1;
- public static final int NM_STATE_CONNECTING = 2;
- public static final int NM_STATE_CONNECTED = 3;
- public static final int NM_STATE_DISCONNECTED = 4;
-
- /*
- * Types of NetworkManager states for versions >= 0.9
- */
- public static final int NM9_STATE_UNKNOWN = 0;
- public static final int NM9_STATE_ASLEEP = 10;
- public static final int NM9_STATE_DISCONNECTED = 20;
- public static final int NM9_STATE_DISCONNECTING = 30;
- public static final int NM9_STATE_CONNECTING = 40;
- public static final int NM9_STATE_CONNECTED_LOCAL = 50;
- public static final int NM9_STATE_CONNECTED_SITE = 60;
- public static final int NM9_STATE_CONNECTED_GLOBAL = 70;
-
- /**
- * State change signal.
- */
- public class StateChange extends DBusSignal
- {
- /**
- * The name of the signal.
- */
- public final String name;
-
- /**
- * The current status it holds.
- */
- public final UInt32 status;
-
- /**
- * Creates status change.
- * @param path the path
- * @param status the status
- * @throws DBusException
- */
- public StateChange(String path, UInt32 status)
- throws DBusException
- {
- super(path, status);
- name = path;
- this.status = status;
- }
-
- /**
- * The current status.
- * @return the current status
- */
- public int getStatus()
- {
- return status.intValue();
- }
-
- /**
- * Returns status description
- * @return the status description
- */
- public String getStatusName()
- {
- switch(status.intValue())
- {
- case NM_STATE_ASLEEP : return "Asleep";
- case NM_STATE_CONNECTING : return "Connecting";
- case NM_STATE_CONNECTED : return "Connected";
- case NM_STATE_DISCONNECTED : return "Disconnected";
- default : return "Unknown";
- }
- }
- }
-
- /**
- * State changed signal.
- */
- public static class StateChanged extends StateChange
- {
- /**
- * Creates status changed.
- * @param path the path
- * @param status the status
- * @throws DBusException
- */
- public StateChanged(String path, UInt32 status)
- throws DBusException
- {
- super(path, status);
- }
-
- /**
- * Returns status description
- * @return the status name
- */
- @Override
- public String getStatusName()
- {
- switch(status.intValue())
- {
- case NM9_STATE_UNKNOWN: return "Unknown";
- case NM9_STATE_ASLEEP: return "Asleep";
- case NM9_STATE_DISCONNECTED: return "Disconnected";
- case NM9_STATE_DISCONNECTING: return "Disconnecting";
- case NM9_STATE_CONNECTING: return "Connecting";
- case NM9_STATE_CONNECTED_LOCAL: return "LocalConnectivity";
- case NM9_STATE_CONNECTED_SITE: return "SiteConnectivity";
- case NM9_STATE_CONNECTED_GLOBAL: return "GlobalConnectivity";
- default : return "Unknown";
- }
- }
- }
-}
+package net.java.sip.communicator.impl.sysactivity;
+
+import org.freedesktop.dbus.*;
+import org.freedesktop.dbus.exceptions.*;
+
+/**
+ * NetworkManager D-Bus Interface
+ *
+ * @author Damian Minkov
+ * @author Ingo Bauersachs
+ */
+@DBusInterfaceName("org.freedesktop.NetworkManager")
+public interface DBusNetworkManager
+ extends DBusInterface
+{
+ /*
+ * Types of NetworkManager states for versions < 0.9
+ */
+ public static final int NM_STATE_UNKNOWN = 0;
+ public static final int NM_STATE_ASLEEP = 1;
+ public static final int NM_STATE_CONNECTING = 2;
+ public static final int NM_STATE_CONNECTED = 3;
+ public static final int NM_STATE_DISCONNECTED = 4;
+
+ /*
+ * Types of NetworkManager states for versions >= 0.9
+ */
+ public static final int NM9_STATE_UNKNOWN = 0;
+ public static final int NM9_STATE_ASLEEP = 10;
+ public static final int NM9_STATE_DISCONNECTED = 20;
+ public static final int NM9_STATE_DISCONNECTING = 30;
+ public static final int NM9_STATE_CONNECTING = 40;
+ public static final int NM9_STATE_CONNECTED_LOCAL = 50;
+ public static final int NM9_STATE_CONNECTED_SITE = 60;
+ public static final int NM9_STATE_CONNECTED_GLOBAL = 70;
+
+ /**
+ * State change signal.
+ */
+ public class StateChange extends DBusSignal
+ {
+ /**
+ * The name of the signal.
+ */
+ public final String name;
+
+ /**
+ * The current status it holds.
+ */
+ public final UInt32 status;
+
+ /**
+ * Creates status change.
+ * @param path the path
+ * @param status the status
+ * @throws DBusException
+ */
+ public StateChange(String path, UInt32 status)
+ throws DBusException
+ {
+ super(path, status);
+ name = path;
+ this.status = status;
+ }
+
+ /**
+ * The current status.
+ * @return the current status
+ */
+ public int getStatus()
+ {
+ return status.intValue();
+ }
+
+ /**
+ * Returns status description
+ * @return the status description
+ */
+ public String getStatusName()
+ {
+ switch(status.intValue())
+ {
+ case NM_STATE_ASLEEP : return "Asleep";
+ case NM_STATE_CONNECTING : return "Connecting";
+ case NM_STATE_CONNECTED : return "Connected";
+ case NM_STATE_DISCONNECTED : return "Disconnected";
+ default : return "Unknown";
+ }
+ }
+ }
+
+ /**
+ * State changed signal.
+ */
+ public static class StateChanged extends StateChange
+ {
+ /**
+ * Creates status changed.
+ * @param path the path
+ * @param status the status
+ * @throws DBusException
+ */
+ public StateChanged(String path, UInt32 status)
+ throws DBusException
+ {
+ super(path, status);
+ }
+
+ /**
+ * Returns status description
+ * @return the status name
+ */
+ @Override
+ public String getStatusName()
+ {
+ switch(status.intValue())
+ {
+ case NM9_STATE_UNKNOWN: return "Unknown";
+ case NM9_STATE_ASLEEP: return "Asleep";
+ case NM9_STATE_DISCONNECTED: return "Disconnected";
+ case NM9_STATE_DISCONNECTING: return "Disconnecting";
+ case NM9_STATE_CONNECTING: return "Connecting";
+ case NM9_STATE_CONNECTED_LOCAL: return "LocalConnectivity";
+ case NM9_STATE_CONNECTED_SITE: return "SiteConnectivity";
+ case NM9_STATE_CONNECTED_GLOBAL: return "GlobalConnectivity";
+ default : return "Unknown";
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/sysactivity/NetworkManagerListenerImpl.java b/src/net/java/sip/communicator/impl/sysactivity/NetworkManagerListenerImpl.java
index 952645a..94f8748 100644
--- a/src/net/java/sip/communicator/impl/sysactivity/NetworkManagerListenerImpl.java
+++ b/src/net/java/sip/communicator/impl/sysactivity/NetworkManagerListenerImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,164 +15,164 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.sysactivity;
-
-import net.java.sip.communicator.service.sysactivity.event.*;
-import net.java.sip.communicator.util.*;
-
-import org.freedesktop.*;
-import org.freedesktop.dbus.*;
-import org.freedesktop.dbus.exceptions.*;
-
-/**
- * Responsible for subscribe and dispatch signals from NetworkManager.
- * Uses dbus to connect.
- *
- * @author Damian Minkov
- */
-@SuppressWarnings("rawtypes")
-public class NetworkManagerListenerImpl
- implements DBusSigHandler,
- SystemActivityManager
-{
- /**
- * The logger.
- */
- private Logger logger = Logger.getLogger(
- NetworkManagerListenerImpl.class.getName());
-
- /**
- * Dbus connection we use.
- */
- private DBusConnection dbusConn;
-
- /**
- * Make only one instance.
- */
- public NetworkManagerListenerImpl()
- {
- try
- {
- dbusConn = DBusConnection.getConnection(DBusConnection.SYSTEM);
- }
- catch(DBusException e)
- {
- logger.error("Cannot obtain dbus connection", e);
- }
- }
-
- /**
- * Starts
- */
- @SuppressWarnings("unchecked")
- public void start()
- {
- // on error connecting to dbus do nothing
- if(dbusConn == null)
- return;
-
- try
- {
- dbusConn.addSigHandler(DBus.NameOwnerChanged.class, this);
- dbusConn.addSigHandler(DBusNetworkManager.StateChange.class, this);
- dbusConn.addSigHandler(DBusNetworkManager.StateChanged.class, this);
- }
- catch(DBusException e)
- {
- logger.error("Error adding dbus signal handlers", e);
- }
- }
-
- /**
- * Stops.
- */
- @SuppressWarnings("unchecked")
- public void stop()
- {
- // on error connecting to dbus do nothing
- if(dbusConn == null)
- return;
-
- try
- {
- dbusConn.removeSigHandler(DBus.NameOwnerChanged.class, this);
- dbusConn.removeSigHandler(
- DBusNetworkManager.StateChange.class, this);
- dbusConn.removeSigHandler(
- DBusNetworkManager.StateChanged.class, this);
- }
- catch(DBusException e)
- {
- logger.error("Error removing dbus signal handlers", e);
- }
- }
-
- /**
- * Receives signals and dispatch them.
- * @param dBusSignal signal to handle.
- */
- public void handle(DBusSignal dBusSignal)
- {
- if(dBusSignal instanceof DBus.NameOwnerChanged)
- {
- DBus.NameOwnerChanged nameOwnerChanged =
- (DBus.NameOwnerChanged)dBusSignal;
-
- if(nameOwnerChanged.name.equals("org.freedesktop.NetworkManager"))
- {
- boolean b1 = nameOwnerChanged.old_owner != null
- && nameOwnerChanged.old_owner.length() > 0;
- boolean b2 = nameOwnerChanged.new_owner != null
- && nameOwnerChanged.new_owner.length() > 0;
-
- if(b1 && !b2)
- {
- SystemActivityEvent evt = new SystemActivityEvent(
- SysActivityActivator.getSystemActivityService(),
- SystemActivityEvent.EVENT_NETWORK_CHANGE);
- SysActivityActivator.getSystemActivityService()
- .fireSystemActivityEvent(evt);
- }
- }
- }
- else if(dBusSignal instanceof DBusNetworkManager.StateChange)
- {
- DBusNetworkManager.StateChange stateChange =
- (DBusNetworkManager.StateChange)dBusSignal;
-
- SystemActivityEvent evt = null;
- switch(stateChange.getStatus())
- {
- case DBusNetworkManager.NM_STATE_CONNECTED:
- case DBusNetworkManager.NM_STATE_DISCONNECTED:
- case DBusNetworkManager.NM9_STATE_DISCONNECTED:
- case DBusNetworkManager.NM9_STATE_CONNECTED_LOCAL:
- case DBusNetworkManager.NM9_STATE_CONNECTED_SITE:
- case DBusNetworkManager.NM9_STATE_CONNECTED_GLOBAL:
- evt = new SystemActivityEvent(
- SysActivityActivator.getSystemActivityService(),
- SystemActivityEvent.EVENT_NETWORK_CHANGE);
- break;
- case DBusNetworkManager.NM_STATE_ASLEEP:
- case DBusNetworkManager.NM9_STATE_ASLEEP:
- evt = new SystemActivityEvent(
- SysActivityActivator.getSystemActivityService(),
- SystemActivityEvent.EVENT_SLEEP);
- break;
- }
-
- if(evt != null)
- SysActivityActivator.getSystemActivityService()
- .fireSystemActivityEvent(evt);
- }
- }
-
- /**
- * Whether we are connected to the network manager through dbus.
- * @return whether we are connected to the network manager.
- */
- public boolean isConnected()
- {
- return dbusConn != null;
- }
-}
+package net.java.sip.communicator.impl.sysactivity;
+
+import net.java.sip.communicator.service.sysactivity.event.*;
+import net.java.sip.communicator.util.*;
+
+import org.freedesktop.*;
+import org.freedesktop.dbus.*;
+import org.freedesktop.dbus.exceptions.*;
+
+/**
+ * Responsible for subscribe and dispatch signals from NetworkManager.
+ * Uses dbus to connect.
+ *
+ * @author Damian Minkov
+ */
+@SuppressWarnings("rawtypes")
+public class NetworkManagerListenerImpl
+ implements DBusSigHandler,
+ SystemActivityManager
+{
+ /**
+ * The logger.
+ */
+ private Logger logger = Logger.getLogger(
+ NetworkManagerListenerImpl.class.getName());
+
+ /**
+ * Dbus connection we use.
+ */
+ private DBusConnection dbusConn;
+
+ /**
+ * Make only one instance.
+ */
+ public NetworkManagerListenerImpl()
+ {
+ try
+ {
+ dbusConn = DBusConnection.getConnection(DBusConnection.SYSTEM);
+ }
+ catch(DBusException e)
+ {
+ logger.error("Cannot obtain dbus connection", e);
+ }
+ }
+
+ /**
+ * Starts
+ */
+ @SuppressWarnings("unchecked")
+ public void start()
+ {
+ // on error connecting to dbus do nothing
+ if(dbusConn == null)
+ return;
+
+ try
+ {
+ dbusConn.addSigHandler(DBus.NameOwnerChanged.class, this);
+ dbusConn.addSigHandler(DBusNetworkManager.StateChange.class, this);
+ dbusConn.addSigHandler(DBusNetworkManager.StateChanged.class, this);
+ }
+ catch(DBusException e)
+ {
+ logger.error("Error adding dbus signal handlers", e);
+ }
+ }
+
+ /**
+ * Stops.
+ */
+ @SuppressWarnings("unchecked")
+ public void stop()
+ {
+ // on error connecting to dbus do nothing
+ if(dbusConn == null)
+ return;
+
+ try
+ {
+ dbusConn.removeSigHandler(DBus.NameOwnerChanged.class, this);
+ dbusConn.removeSigHandler(
+ DBusNetworkManager.StateChange.class, this);
+ dbusConn.removeSigHandler(
+ DBusNetworkManager.StateChanged.class, this);
+ }
+ catch(DBusException e)
+ {
+ logger.error("Error removing dbus signal handlers", e);
+ }
+ }
+
+ /**
+ * Receives signals and dispatch them.
+ * @param dBusSignal signal to handle.
+ */
+ public void handle(DBusSignal dBusSignal)
+ {
+ if(dBusSignal instanceof DBus.NameOwnerChanged)
+ {
+ DBus.NameOwnerChanged nameOwnerChanged =
+ (DBus.NameOwnerChanged)dBusSignal;
+
+ if(nameOwnerChanged.name.equals("org.freedesktop.NetworkManager"))
+ {
+ boolean b1 = nameOwnerChanged.old_owner != null
+ && nameOwnerChanged.old_owner.length() > 0;
+ boolean b2 = nameOwnerChanged.new_owner != null
+ && nameOwnerChanged.new_owner.length() > 0;
+
+ if(b1 && !b2)
+ {
+ SystemActivityEvent evt = new SystemActivityEvent(
+ SysActivityActivator.getSystemActivityService(),
+ SystemActivityEvent.EVENT_NETWORK_CHANGE);
+ SysActivityActivator.getSystemActivityService()
+ .fireSystemActivityEvent(evt);
+ }
+ }
+ }
+ else if(dBusSignal instanceof DBusNetworkManager.StateChange)
+ {
+ DBusNetworkManager.StateChange stateChange =
+ (DBusNetworkManager.StateChange)dBusSignal;
+
+ SystemActivityEvent evt = null;
+ switch(stateChange.getStatus())
+ {
+ case DBusNetworkManager.NM_STATE_CONNECTED:
+ case DBusNetworkManager.NM_STATE_DISCONNECTED:
+ case DBusNetworkManager.NM9_STATE_DISCONNECTED:
+ case DBusNetworkManager.NM9_STATE_CONNECTED_LOCAL:
+ case DBusNetworkManager.NM9_STATE_CONNECTED_SITE:
+ case DBusNetworkManager.NM9_STATE_CONNECTED_GLOBAL:
+ evt = new SystemActivityEvent(
+ SysActivityActivator.getSystemActivityService(),
+ SystemActivityEvent.EVENT_NETWORK_CHANGE);
+ break;
+ case DBusNetworkManager.NM_STATE_ASLEEP:
+ case DBusNetworkManager.NM9_STATE_ASLEEP:
+ evt = new SystemActivityEvent(
+ SysActivityActivator.getSystemActivityService(),
+ SystemActivityEvent.EVENT_SLEEP);
+ break;
+ }
+
+ if(evt != null)
+ SysActivityActivator.getSystemActivityService()
+ .fireSystemActivityEvent(evt);
+ }
+ }
+
+ /**
+ * Whether we are connected to the network manager through dbus.
+ * @return whether we are connected to the network manager.
+ */
+ public boolean isConnected()
+ {
+ return dbusConn != null;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/sysactivity/SysActivityActivator.java b/src/net/java/sip/communicator/impl/sysactivity/SysActivityActivator.java
index 6a46e18..96cea29 100644
--- a/src/net/java/sip/communicator/impl/sysactivity/SysActivityActivator.java
+++ b/src/net/java/sip/communicator/impl/sysactivity/SysActivityActivator.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,101 +15,101 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.sysactivity;
-
-import net.java.sip.communicator.service.sysactivity.*;
-import net.java.sip.communicator.util.*;
-
-import org.osgi.framework.*;
-
-/**
- * Listens for system activity changes like sleep, network change, inactivity
- * and informs all its listeners.
- *
- * @author Damian Minkov
- */
-public class SysActivityActivator
- implements BundleActivator
-{
- /**
- * The <tt>Logger</tt> used by this <tt>SysActivityActivator</tt> for
- * logging output.
- */
- private final Logger logger = Logger.getLogger(SysActivityActivator.class);
-
- /**
- * The OSGi <tt>BundleContext</tt>.
- */
- private static BundleContext bundleContext = null;
-
- /**
- * The system activity service impl.
- */
- private static SystemActivityNotificationsServiceImpl
- sysActivitiesServiceImpl;
-
- /**
- * Called when this bundle is started so the Framework can perform the
- * bundle-specific activities necessary to start this bundle.
- *
- * @param bundleContext 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 bundleContext)
- throws Exception
- {
- SysActivityActivator.bundleContext = bundleContext;
-
- if (logger.isDebugEnabled())
- logger.debug("Started.");
-
- sysActivitiesServiceImpl = new SystemActivityNotificationsServiceImpl();
- sysActivitiesServiceImpl.start();
-
- bundleContext.registerService(
- SystemActivityNotificationsService.class.getName(),
- sysActivitiesServiceImpl,
- null);
- }
-
- /**
- * Returns a reference to the bundle context that we were started with.
- * @return a reference to the BundleContext instance that we were started
- * with.
- */
- public static SystemActivityNotificationsServiceImpl
- getSystemActivityService()
- {
- return sysActivitiesServiceImpl;
- }
-
- /**
- * Called when this bundle is stopped so the Framework can perform the
- * bundle-specific activities necessary to stop the bundle.
- *
- * @param bundleContext 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 bundleContext)
- throws Exception
- {
- if (sysActivitiesServiceImpl != null)
- sysActivitiesServiceImpl.stop();
- }
-
- /**
- * Returns a reference to the bundle context that we were started with.
- * @return a reference to the BundleContext instance that we were started
- * with.
- */
- public static BundleContext getBundleContext()
- {
- return bundleContext;
- }
-}
+package net.java.sip.communicator.impl.sysactivity;
+
+import net.java.sip.communicator.service.sysactivity.*;
+import net.java.sip.communicator.util.*;
+
+import org.osgi.framework.*;
+
+/**
+ * Listens for system activity changes like sleep, network change, inactivity
+ * and informs all its listeners.
+ *
+ * @author Damian Minkov
+ */
+public class SysActivityActivator
+ implements BundleActivator
+{
+ /**
+ * The <tt>Logger</tt> used by this <tt>SysActivityActivator</tt> for
+ * logging output.
+ */
+ private final Logger logger = Logger.getLogger(SysActivityActivator.class);
+
+ /**
+ * The OSGi <tt>BundleContext</tt>.
+ */
+ private static BundleContext bundleContext = null;
+
+ /**
+ * The system activity service impl.
+ */
+ private static SystemActivityNotificationsServiceImpl
+ sysActivitiesServiceImpl;
+
+ /**
+ * Called when this bundle is started so the Framework can perform the
+ * bundle-specific activities necessary to start this bundle.
+ *
+ * @param bundleContext 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 bundleContext)
+ throws Exception
+ {
+ SysActivityActivator.bundleContext = bundleContext;
+
+ if (logger.isDebugEnabled())
+ logger.debug("Started.");
+
+ sysActivitiesServiceImpl = new SystemActivityNotificationsServiceImpl();
+ sysActivitiesServiceImpl.start();
+
+ bundleContext.registerService(
+ SystemActivityNotificationsService.class.getName(),
+ sysActivitiesServiceImpl,
+ null);
+ }
+
+ /**
+ * Returns a reference to the bundle context that we were started with.
+ * @return a reference to the BundleContext instance that we were started
+ * with.
+ */
+ public static SystemActivityNotificationsServiceImpl
+ getSystemActivityService()
+ {
+ return sysActivitiesServiceImpl;
+ }
+
+ /**
+ * Called when this bundle is stopped so the Framework can perform the
+ * bundle-specific activities necessary to stop the bundle.
+ *
+ * @param bundleContext 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 bundleContext)
+ throws Exception
+ {
+ if (sysActivitiesServiceImpl != null)
+ sysActivitiesServiceImpl.stop();
+ }
+
+ /**
+ * Returns a reference to the bundle context that we were started with.
+ * @return a reference to the BundleContext instance that we were started
+ * with.
+ */
+ public static BundleContext getBundleContext()
+ {
+ return bundleContext;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java
index a6ff936..e2fb17a 100644
--- a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java
+++ b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,235 +15,237 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.sysactivity;
-
-import net.java.sip.communicator.util.*;
-
-/**
- * @author Damian Minkov
- */
-public class SystemActivityNotifications
-{
- /**
- * The <tt>Logger</tt> used by the <tt>SystemActivityNotifications</tt>
- * class to log debugging information.
- */
- private static final Logger logger
- = Logger.getLogger(SystemActivityNotifications.class);
-
- /**
- * Computer display has stand by.
- */
- public static final int NOTIFY_DISPLAY_SLEEP = 2;
-
- /**
- * Computer display wakes up after stand by.
- */
- public static final int NOTIFY_DISPLAY_WAKE = 3;
-
- /**
- * A change in dns configuration has occurred.
- */
- public static final int NOTIFY_DNS_CHANGE = 10;
-
- /**
- * All processes have been informed about ending session, now notify for
- * the actual end session.
- */
- public static final int NOTIFY_ENDSESSION = 12;
-
- /**
- * A change in network configuration has occurred.
- */
- public static final int NOTIFY_NETWORK_CHANGE = 9;
-
- /**
- * Notifies for start of process of ending desktop session,
- * logoff or shutdown.
- */
- public static final int NOTIFY_QUERY_ENDSESSION = 11;
-
- /**
- * Screen has been locked.
- */
- public static final int NOTIFY_SCREEN_LOCKED = 7;
-
- /**
- * Screen has been unlocked.
- */
- public static final int NOTIFY_SCREEN_UNLOCKED = 8;
-
- /**
- * Screensaver has been started.
- */
- public static final int NOTIFY_SCREENSAVER_START = 4;
-
- /**
- * Screensaver has been stopped.
- */
- public static final int NOTIFY_SCREENSAVER_STOP = 6;
-
- /**
- * Screensaver will stop.
- */
- public static final int NOTIFY_SCREENSAVER_WILL_STOP = 5;
-
- /**
- * Notify that computers is going to sleep.
- */
- public static final int NOTIFY_SLEEP = 0;
-
- /**
- * Notify that computer is wakeing up after stand by.
- */
- public static final int NOTIFY_WAKE = 1;
-
- /**
- * The native instance.
- */
- private static long ptr;
-
- /**
- * Init native library.
- */
- static
- {
- try
- {
- // Don't load native library on Android to prevent the exception
- if(!org.jitsi.util.OSUtils.IS_ANDROID)
- {
- System.loadLibrary("sysactivitynotifications");
-
- ptr = allocAndInit();
- if (ptr == -1)
- ptr = 0;
- }
- }
- catch (Throwable t)
- {
- if (t instanceof ThreadDeath)
- throw (ThreadDeath) t;
- else
- logger.warn("Failed to initialize native counterpart", t);
- }
- }
-
- /**
- * Allocate native resources and gets a pointer.
- *
- * @return
- */
- private static native long allocAndInit();
-
- /**
- * Returns the when was last input in milliseconds. The time when there was
- * any activity on the computer.
- *
- * @return the last input in milliseconds
- */
- public static native long getLastInput();
-
- /**
- * Whether native library is loaded.
- *
- * @return whether native library is loaded.
- */
- public static boolean isLoaded()
- {
- return (ptr != 0);
- }
-
- /**
- * Release native resources.
- *
- * @param ptr
- */
- private static native void release(long ptr);
-
- /**
- * Sets notifier delegate.
- *
- * @param ptr
- * @param delegate
- */
- public static native void setDelegate(
- long ptr,
- NotificationsDelegate delegate);
-
- /**
- * Sets delegate.
- *
- * @param delegate
- */
- public static void setDelegate(NotificationsDelegate delegate)
- {
- if (ptr != 0)
- setDelegate(ptr, delegate);
- }
-
- /**
- * Start.
- */
- public static void start()
- {
- if (ptr != 0)
- start(ptr);
- }
-
- /**
- * Start processing.
- *
- * @param ptr
- */
- private static native void start(long ptr);
-
- /**
- * Stop.
- */
- public static void stop()
- {
- if (ptr != 0)
- {
- stop(ptr);
- release(ptr);
- ptr = 0;
- }
- }
-
- /**
- * Stop processing.
- *
- * @param ptr
- */
- private static native void stop(long ptr);
-
- /**
- * Delegate class to be notified about changes.
- */
- public interface NotificationsDelegate
- {
- /**
- * Callback method when receiving notifications.
- *
- * @param type
- */
- public void notify(int type);
-
- /**
- * Callback method when receiving special network notifications.
- *
- * @param family family of network change (ipv6, ipv4)
- * @param luidIndex unique index of interface
- * @param name name of the interface
- * @param type of the interface
- * @param connected whether interface is connected or not.
- */
- public void notifyNetworkChange(
- int family,
- long luidIndex,
- String name,
- long type,
- boolean connected);
- }
-}
+package net.java.sip.communicator.impl.sysactivity;
+
+import net.java.sip.communicator.util.Logger;
+import org.jitsi.util.*;
+
+/**
+ * @author Damian Minkov
+ */
+public class SystemActivityNotifications
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>SystemActivityNotifications</tt>
+ * class to log debugging information.
+ */
+ private static final Logger logger
+ = Logger.getLogger(SystemActivityNotifications.class);
+
+ /**
+ * Computer display has stand by.
+ */
+ public static final int NOTIFY_DISPLAY_SLEEP = 2;
+
+ /**
+ * Computer display wakes up after stand by.
+ */
+ public static final int NOTIFY_DISPLAY_WAKE = 3;
+
+ /**
+ * A change in dns configuration has occurred.
+ */
+ public static final int NOTIFY_DNS_CHANGE = 10;
+
+ /**
+ * All processes have been informed about ending session, now notify for
+ * the actual end session.
+ */
+ public static final int NOTIFY_ENDSESSION = 12;
+
+ /**
+ * A change in network configuration has occurred.
+ */
+ public static final int NOTIFY_NETWORK_CHANGE = 9;
+
+ /**
+ * Notifies for start of process of ending desktop session,
+ * logoff or shutdown.
+ */
+ public static final int NOTIFY_QUERY_ENDSESSION = 11;
+
+ /**
+ * Screen has been locked.
+ */
+ public static final int NOTIFY_SCREEN_LOCKED = 7;
+
+ /**
+ * Screen has been unlocked.
+ */
+ public static final int NOTIFY_SCREEN_UNLOCKED = 8;
+
+ /**
+ * Screensaver has been started.
+ */
+ public static final int NOTIFY_SCREENSAVER_START = 4;
+
+ /**
+ * Screensaver has been stopped.
+ */
+ public static final int NOTIFY_SCREENSAVER_STOP = 6;
+
+ /**
+ * Screensaver will stop.
+ */
+ public static final int NOTIFY_SCREENSAVER_WILL_STOP = 5;
+
+ /**
+ * Notify that computers is going to sleep.
+ */
+ public static final int NOTIFY_SLEEP = 0;
+
+ /**
+ * Notify that computer is wakeing up after stand by.
+ */
+ public static final int NOTIFY_WAKE = 1;
+
+ /**
+ * The native instance.
+ */
+ private static long ptr;
+
+ /**
+ * Init native library.
+ */
+ static
+ {
+ try
+ {
+ // Don't load native library on Android to prevent the exception
+ if(!org.jitsi.util.OSUtils.IS_ANDROID)
+ {
+ JNIUtils.loadLibrary("sysactivitynotifications",
+ SystemActivityNotifications.class);
+
+ ptr = allocAndInit();
+ if (ptr == -1)
+ ptr = 0;
+ }
+ }
+ catch (Throwable t)
+ {
+ if (t instanceof ThreadDeath)
+ throw (ThreadDeath) t;
+ else
+ logger.warn("Failed to initialize native counterpart", t);
+ }
+ }
+
+ /**
+ * Allocate native resources and gets a pointer.
+ *
+ * @return
+ */
+ private static native long allocAndInit();
+
+ /**
+ * Returns the when was last input in milliseconds. The time when there was
+ * any activity on the computer.
+ *
+ * @return the last input in milliseconds
+ */
+ public static native long getLastInput();
+
+ /**
+ * Whether native library is loaded.
+ *
+ * @return whether native library is loaded.
+ */
+ public static boolean isLoaded()
+ {
+ return (ptr != 0);
+ }
+
+ /**
+ * Release native resources.
+ *
+ * @param ptr
+ */
+ private static native void release(long ptr);
+
+ /**
+ * Sets notifier delegate.
+ *
+ * @param ptr
+ * @param delegate
+ */
+ public static native void setDelegate(
+ long ptr,
+ NotificationsDelegate delegate);
+
+ /**
+ * Sets delegate.
+ *
+ * @param delegate
+ */
+ public static void setDelegate(NotificationsDelegate delegate)
+ {
+ if (ptr != 0)
+ setDelegate(ptr, delegate);
+ }
+
+ /**
+ * Start.
+ */
+ public static void start()
+ {
+ if (ptr != 0)
+ start(ptr);
+ }
+
+ /**
+ * Start processing.
+ *
+ * @param ptr
+ */
+ private static native void start(long ptr);
+
+ /**
+ * Stop.
+ */
+ public static void stop()
+ {
+ if (ptr != 0)
+ {
+ stop(ptr);
+ release(ptr);
+ ptr = 0;
+ }
+ }
+
+ /**
+ * Stop processing.
+ *
+ * @param ptr
+ */
+ private static native void stop(long ptr);
+
+ /**
+ * Delegate class to be notified about changes.
+ */
+ public interface NotificationsDelegate
+ {
+ /**
+ * Callback method when receiving notifications.
+ *
+ * @param type
+ */
+ public void notify(int type);
+
+ /**
+ * Callback method when receiving special network notifications.
+ *
+ * @param family family of network change (ipv6, ipv4)
+ * @param luidIndex unique index of interface
+ * @param name name of the interface
+ * @param type of the interface
+ * @param connected whether interface is connected or not.
+ */
+ public void notifyNetworkChange(
+ int family,
+ long luidIndex,
+ String name,
+ long type,
+ boolean connected);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java
index c25504f..c58805b 100644
--- a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotificationsServiceImpl.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,639 +15,639 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.sysactivity;
-
-import java.util.*;
-
-import net.java.sip.communicator.service.sysactivity.*;
-import net.java.sip.communicator.service.sysactivity.event.*;
-import net.java.sip.communicator.util.Logger;
-
-import org.jitsi.util.*;
-
-/**
- * Service implementation listens for computer changes as sleeping, network
- * change, inactivity.
- *
- * @author Damian Minkov
- */
-public class SystemActivityNotificationsServiceImpl
- implements SystemActivityNotifications.NotificationsDelegate,
- SystemActivityNotificationsService,
- Runnable
-{
- /**
- * The <tt>Logger</tt> used by this
- * <tt>SystemActivityNotificationsServiceImpl</tt> for logging output.
- */
- private final Logger logger
- = Logger.getLogger(SystemActivityNotificationsServiceImpl.class);
-
- /**
- * The thread dispatcher of network change events.
- */
- private final SystemActivityEventDispatcher eventDispatcher
- = new SystemActivityEventDispatcher();
-
- /**
- * A list of listeners registered for idle events.
- */
- private final Map<SystemActivityChangeListener,Long> idleChangeListeners
- = new HashMap<SystemActivityChangeListener, Long>();
-
- /**
- * Listeners which are fired for idle state and which will be fired
- * with idle end when needed.
- */
- private final List<SystemActivityChangeListener> listenersInIdleState
- = new ArrayList<SystemActivityChangeListener>();
-
- /**
- * The interval between checks when not idle.
- */
- private static final int CHECK_FOR_IDLE_DEFAULT = 30 * 1000;
-
- /**
- * The interval between checks when idle. The interval is shorter
- * so we can react almost immediately when we are active again.
- */
- private static final int CHECK_FOR_IDLE_WHEN_IDLE = 1000;
-
- /**
- * The time in milliseconds between two checks for system idle.
- */
- private static int idleStateCheckDelay = CHECK_FOR_IDLE_DEFAULT;
-
- /**
- * Whether current service is started or stopped.
- */
- private boolean running = false;
-
- /**
- * The time when we received latest network change event.
- */
- private long lastNetworkChange = -1;
-
- /**
- * Sometimes (on windows) we got several network change events
- * this is the time after which latest event we will skip next events.
- */
- private static final long NETWORK_EVENT_SILENT_TIME = 10*1000;
-
- /**
- * Whether network is currently connected.
- */
- private Boolean networkIsConnected = null;
-
- /**
- * The linux impl class name.
- */
- private static final String SYSTEM_ACTIVITY_MANAGER_LINUX_CLASS
- = "net.java.sip.communicator.impl.sysactivity.NetworkManagerListenerImpl";
-
- /**
- * The android impl class name.
- */
- private static final String SYSTEM_ACTIVITY_MANAGER_ANDROID_CLASS
- = "net.java.sip.communicator.impl.sysactivity.ConnectivityManagerListenerImpl";
-
- /**
- * The currently instantiated and working manager.
- */
- private SystemActivityManager currentRunningManager = null;
-
- /**
- * Init and start notifications.
- */
- public void start()
- {
- running = true;
-
- // set the delegate and start notification in new thread
- // make sure we don't block startup process
- Thread notifystartThread
- = new Thread(
- new Runnable()
- {
- public void run()
- {
- SystemActivityNotifications.setDelegate(
- SystemActivityNotificationsServiceImpl.this);
- SystemActivityNotifications.start();
- }
- },
- "SystemActivityNotificationsServiceImpl");
- notifystartThread.setDaemon(true);
- notifystartThread.start();
-
- if(isSupported(SystemActivityEvent.EVENT_SYSTEM_IDLE))
- {
- // a thread periodically checks system idle state and if it pass the
- // idle time for a particular listener, will inform it.
- Thread idleNotifyThread = new Thread(
- this,
- "SystemActivityNotificationsServiceImpl.IdleNotifyThread");
- idleNotifyThread.setDaemon(true);
- idleNotifyThread.start();
- }
-
- if (getCurrentRunningManager() != null)
- getCurrentRunningManager().start();
- }
-
- /**
- * Stop notifications.
- */
- public void stop()
- {
- SystemActivityNotifications.stop();
-
- if (getCurrentRunningManager() != null)
- getCurrentRunningManager().stop();
-
- eventDispatcher.stop();
-
- running = false;
-
- synchronized(this)
- {
- this.notifyAll();
- }
- }
-
- /**
- * Registers a listener that would be notified of changes that have occurred
- * in the underlying system.
- *
- * @param listener the listener that we'd like to register for changes in
- * the underlying system.
- */
- public void addSystemActivityChangeListener(
- SystemActivityChangeListener listener)
- {
- eventDispatcher.addSystemActivityChangeListener(listener);
- }
-
- /**
- * Remove the specified listener so that it won't receive further
- * notifications of changes that occur in the underlying system
- *
- * @param listener the listener to remove.
- */
- public void removeSystemActivityChangeListener(
- SystemActivityChangeListener listener)
- {
- eventDispatcher.removeSystemActivityChangeListener(listener);
- }
-
- /**
- * Registers a listener that would be notified for idle of the system
- * for <tt>idleTime</tt>.
- *
- * @param idleTime the time in milliseconds after which we will consider
- * system to be idle. This doesn't count when system seems idle as
- * monitor is off or screensaver is on, or desktop is locked.
- * @param listener the listener that we'd like to register for changes in
- * the underlying system.
- */
- public void addIdleSystemChangeListener(
- long idleTime,
- SystemActivityChangeListener listener)
- {
- synchronized (idleChangeListeners)
- {
- if (idleTime > 0
- && !idleChangeListeners.containsKey(listener))
- idleChangeListeners.put(listener, idleTime);
- }
- }
-
- /**
- * Remove the specified listener so that it won't receive further
- * notifications for idle system.
- *
- * @param listener the listener to remove.
- */
- public void removeIdleSystemChangeListener(
- SystemActivityChangeListener listener)
- {
- synchronized (idleChangeListeners)
- {
- idleChangeListeners.remove(listener);
- }
- }
-
- /**
- * The time since last user input. The time the system has been idle.
- * @return time the system has been idle.
- */
- public long getTimeSinceLastInput()
- {
- if(SystemActivityNotifications.isLoaded())
- return SystemActivityNotifications.getLastInput();
- else
- return -1;
- }
-
- /**
- * Callback method when receiving notifications.
- *
- * @param type type of the notification.
- */
- public void notify(int type)
- {
- SystemActivityEvent evt = null;
- switch(type)
- {
- case SystemActivityNotifications.NOTIFY_SLEEP :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_SLEEP);
- break;
- case SystemActivityNotifications.NOTIFY_WAKE :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_WAKE);
- break;
- case SystemActivityNotifications.NOTIFY_DISPLAY_SLEEP :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_DISPLAY_SLEEP);
- break;
- case SystemActivityNotifications.NOTIFY_DISPLAY_WAKE :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_DISPLAY_WAKE);
- break;
- case SystemActivityNotifications.NOTIFY_SCREENSAVER_START :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_SCREENSAVER_START);
- break;
- case SystemActivityNotifications.NOTIFY_SCREENSAVER_WILL_STOP :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_SCREENSAVER_WILL_STOP);
- break;
- case SystemActivityNotifications.NOTIFY_SCREENSAVER_STOP :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_SCREENSAVER_STOP);
- break;
- case SystemActivityNotifications.NOTIFY_SCREEN_LOCKED :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_SCREEN_LOCKED);
- break;
- case SystemActivityNotifications.NOTIFY_SCREEN_UNLOCKED :
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_SCREEN_UNLOCKED);
- break;
- case SystemActivityNotifications.NOTIFY_NETWORK_CHANGE :
- {
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_NETWORK_CHANGE);
- break;
- }
- case SystemActivityNotifications.NOTIFY_DNS_CHANGE :
- {
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_DNS_CHANGE);
- break;
- }
- case SystemActivityNotifications.NOTIFY_QUERY_ENDSESSION :
- {
- // both events QUERY_ENDSESSION and ENDSESSION
- // depend on the result one after another
- // we don't put them in new thread in order to give control
- // in the bundles using this events.
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_QUERY_ENDSESSION);
- eventDispatcher.fireSystemActivityEventCurrentThread(evt);
-
- return;
- }
- case SystemActivityNotifications.NOTIFY_ENDSESSION :
- {
- // both events QUERY_ENDSESSION and ENDSESSION
- // depend on the result one after another
- // we don't put them in new thread in order to give control
- // in the bundles using this events.
- evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_ENDSESSION);
- eventDispatcher.fireSystemActivityEventCurrentThread(evt);
-
- return;
- }
- }
-
- if (evt != null)
- fireSystemActivityEvent(evt);
- }
-
- /**
- * Callback method when receiving special network notifications.
- *
- * @param family family of network change (ipv6, ipv4)
- * AF_UNSPEC = 0 (The address family is unspecified.)
- * AF_INET = 2 (The Internet Protocol version 4 (IPv4) address family)
- * AF_INET6 = 23 (The Internet Protocol version 6 (IPv6) address family)
- * @param luidIndex unique index of interface
- * @param name name of the interface
- * @param type of the interface
- * Possible values for the interface type are listed in the Ipifcons.h file.
- * common values:
- * IF_TYPE_OTHER = 1 (Some other type of network interface.)
- * IF_TYPE_ETHERNET_CSMACD = 6 (An Ethernet network interface.)
- * IF_TYPE_ISO88025_TOKENRING = 9 (A token ring network interface.)
- * IF_TYPE_PPP = 23 (A PPP network interface.)
- * IF_TYPE_SOFTWARE_LOOPBACK = 24 (A software loopback network interface.)
- * IF_TYPE_IEEE80211 = 71 (An IEEE 802.11 wireless network interface.)
- * IF_TYPE_TUNNEL = 131 (A tunnel type encapsulation network interface.)
- * IF_TYPE_IEEE1394 = 144 (An IEEE 1394 (Firewire) high performance
- * serial bus network interface.)
- * @param connected whether interface is connected or not.
- */
- public void notifyNetworkChange(
- int family,
- long luidIndex,
- String name,
- long type,
- boolean connected)
- {
- long current = System.currentTimeMillis();
- if(current - lastNetworkChange <= NETWORK_EVENT_SILENT_TIME
- && (networkIsConnected != null && networkIsConnected.equals(connected)))
- {
- networkIsConnected = connected;
- return;
- }
-
- lastNetworkChange = current;
- networkIsConnected = connected;
-
- SystemActivityEvent evt = new SystemActivityEvent(this,
- SystemActivityEvent.EVENT_NETWORK_CHANGE);
- fireSystemActivityEvent(evt);
- }
-
- /**
- * The thread run method that handles idle notifies.
- *
- * @see Thread#run()
- */
- public void run()
- {
- while(running)
- {
- try
- {
- long idleTime = 0;
- if(idleChangeListeners.size() > 0)
- {
- // check
- idleTime = SystemActivityNotifications.getLastInput();
-
- if((idleTime < idleStateCheckDelay)
- && (listenersInIdleState.size() > 0))
- {
- for(SystemActivityChangeListener l
- : listenersInIdleState)
- {
- fireSystemIdleEndEvent(l);
- }
- listenersInIdleState.clear();
- }
-
- for(Map.Entry<SystemActivityChangeListener, Long> entry
- : idleChangeListeners.entrySet())
- {
- SystemActivityChangeListener listener =
- entry.getKey();
-
- if(!listenersInIdleState.contains(listener)
- && (entry.getValue() <= idleTime))
- {
- fireSystemIdleEvent(listener);
-
- listenersInIdleState.add(listener);
- }
- }
- }
-
- // if the minimum check for idle is X minutes
- // we will wait before checking (X - Y + 1sec)
- // where Y is the last idle time returned by OS
- if(listenersInIdleState.size() > 0)
- {
- idleStateCheckDelay = CHECK_FOR_IDLE_WHEN_IDLE;
- }
- else if(idleTime != 0)
- {
- long minIdleSetting = CHECK_FOR_IDLE_DEFAULT;
-
- if(!idleChangeListeners.isEmpty())
- minIdleSetting =
- Collections.min(idleChangeListeners.values());
-
- int newSetting = (int)(minIdleSetting - idleTime) + 1000;
-
- if(newSetting > 0)
- idleStateCheckDelay = newSetting;
- else
- idleStateCheckDelay = CHECK_FOR_IDLE_DEFAULT;
- }
- else
- {
- idleStateCheckDelay = CHECK_FOR_IDLE_DEFAULT;
- }
-
- // wait for the specified time
- synchronized(this)
- {
- this.wait(idleStateCheckDelay);
- }
- }
- catch(UnsatisfiedLinkError t)
- {
- logger.error("Missing native impl", t);
- return;
- }
- catch(Throwable t)
- {
- logger.error("Error checking for idle", t);
- }
- }
- }
-
- /**
- * Delivers the specified event to all registered listeners.
- *
- * @param evt the <tt>SystemActivityEvent</tt> that we'd like delivered to
- * all registered message listeners.
- */
- protected void fireSystemActivityEvent(SystemActivityEvent evt)
- {
- int eventID = evt.getEventID();
-
- // Add network activity info to track wake up problems.
- if (logger.isInfoEnabled()
- && ((eventID == SystemActivityEvent.EVENT_NETWORK_CHANGE)
- || (eventID == SystemActivityEvent.EVENT_DNS_CHANGE)))
- {
- logger.info("Received system activity event: " + evt);
- }
-
- if (eventID == SystemActivityEvent.EVENT_NETWORK_CHANGE)
- {
- // Give time to Java to dispatch same event and populate its network
- // interfaces.
- eventDispatcher.fireSystemActivityEvent(evt, 500);
- }
- else
- eventDispatcher.fireSystemActivityEvent(evt);
- }
-
- /**
- * Delivers the specified event to all registered listeners.
- *
- * @param listener listener to inform
- */
- protected void fireSystemIdleEvent(SystemActivityChangeListener listener)
- {
- SystemActivityEvent evt
- = new SystemActivityEvent(
- this,
- SystemActivityEvent.EVENT_SYSTEM_IDLE);
-
- if (logger.isDebugEnabled())
- logger.debug("Dispatching SystemActivityEvent evt=" + evt);
-
- try
- {
- listener.activityChanged(evt);
- }
- catch (Throwable t)
- {
- if (t instanceof ThreadDeath)
- throw (ThreadDeath) t;
- else
- logger.error("Error delivering event", t);
- }
- }
-
- /**
- * Delivers the specified event to listener.
- *
- * @param listener listener to inform
- */
- protected void fireSystemIdleEndEvent(
- SystemActivityChangeListener listener)
- {
- SystemActivityEvent evt
- = new SystemActivityEvent(
- this,
- SystemActivityEvent.EVENT_SYSTEM_IDLE_END);
-
- if (logger.isDebugEnabled())
- logger.debug("Dispatching SystemActivityEvent evt=" + evt);
-
- try
- {
- listener.activityChanged(evt);
- }
- catch (Throwable t)
- {
- if (t instanceof ThreadDeath)
- throw (ThreadDeath) t;
- else
- logger.error("Error delivering event", t);
- }
- }
-
- /**
- * Can check whether an event id is supported on
- * current operation system.
- * Simple return what is implemented in native, and checks
- * are made when possible, for example linux cannot connect
- * to NM through dbus.
- * @param eventID the event to check.
- * @return whether the supplied event id is supported.
- */
- public boolean isSupported(int eventID)
- {
- if(OSUtils.IS_WINDOWS)
- {
- switch(eventID)
- {
- case SystemActivityEvent.EVENT_SLEEP:
- case SystemActivityEvent.EVENT_WAKE:
- case SystemActivityEvent.EVENT_NETWORK_CHANGE:
- case SystemActivityEvent.EVENT_SYSTEM_IDLE:
- case SystemActivityEvent.EVENT_SYSTEM_IDLE_END:
- return SystemActivityNotifications.isLoaded();
- default:
- return false;
- }
- }
- else if(OSUtils.IS_MAC)
- {
- return SystemActivityNotifications.isLoaded();
- }
- else if(OSUtils.IS_LINUX)
- {
- switch(eventID)
- {
- case SystemActivityEvent.EVENT_SLEEP:
- case SystemActivityEvent.EVENT_NETWORK_CHANGE:
- {
- SystemActivityManager currentRunningManager
- = getCurrentRunningManager();
-
- return
- (currentRunningManager == null)
- ? false
- : currentRunningManager.isConnected();
- }
- case SystemActivityEvent.EVENT_SYSTEM_IDLE:
- case SystemActivityEvent.EVENT_SYSTEM_IDLE_END:
- return SystemActivityNotifications.isLoaded();
- default:
- return false;
- }
- }
- else if(OSUtils.IS_ANDROID)
- {
- return (eventID == SystemActivityEvent.EVENT_NETWORK_CHANGE);
- }
- else
- {
- return false;
- }
- }
-
- /**
- * Returns or instantiate the manager.
- * @return
- */
- private SystemActivityManager getCurrentRunningManager()
- {
- if(currentRunningManager == null)
- {
- try
- {
- String className = null;
- if(OSUtils.IS_LINUX)
- {
- className = SYSTEM_ACTIVITY_MANAGER_LINUX_CLASS;
- }
- else if(OSUtils.IS_ANDROID)
- {
- className = SYSTEM_ACTIVITY_MANAGER_ANDROID_CLASS;
- }
-
- if(className != null)
- currentRunningManager = (SystemActivityManager)
- Class.forName(className).newInstance();
- }
- catch(Throwable t)
- {
- logger.error("Error creating manager", t);
- }
- }
-
- return currentRunningManager;
- }
-}
+package net.java.sip.communicator.impl.sysactivity;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.sysactivity.*;
+import net.java.sip.communicator.service.sysactivity.event.*;
+import net.java.sip.communicator.util.Logger;
+
+import org.jitsi.util.*;
+
+/**
+ * Service implementation listens for computer changes as sleeping, network
+ * change, inactivity.
+ *
+ * @author Damian Minkov
+ */
+public class SystemActivityNotificationsServiceImpl
+ implements SystemActivityNotifications.NotificationsDelegate,
+ SystemActivityNotificationsService,
+ Runnable
+{
+ /**
+ * The <tt>Logger</tt> used by this
+ * <tt>SystemActivityNotificationsServiceImpl</tt> for logging output.
+ */
+ private final Logger logger
+ = Logger.getLogger(SystemActivityNotificationsServiceImpl.class);
+
+ /**
+ * The thread dispatcher of network change events.
+ */
+ private final SystemActivityEventDispatcher eventDispatcher
+ = new SystemActivityEventDispatcher();
+
+ /**
+ * A list of listeners registered for idle events.
+ */
+ private final Map<SystemActivityChangeListener,Long> idleChangeListeners
+ = new HashMap<SystemActivityChangeListener, Long>();
+
+ /**
+ * Listeners which are fired for idle state and which will be fired
+ * with idle end when needed.
+ */
+ private final List<SystemActivityChangeListener> listenersInIdleState
+ = new ArrayList<SystemActivityChangeListener>();
+
+ /**
+ * The interval between checks when not idle.
+ */
+ private static final int CHECK_FOR_IDLE_DEFAULT = 30 * 1000;
+
+ /**
+ * The interval between checks when idle. The interval is shorter
+ * so we can react almost immediately when we are active again.
+ */
+ private static final int CHECK_FOR_IDLE_WHEN_IDLE = 1000;
+
+ /**
+ * The time in milliseconds between two checks for system idle.
+ */
+ private static int idleStateCheckDelay = CHECK_FOR_IDLE_DEFAULT;
+
+ /**
+ * Whether current service is started or stopped.
+ */
+ private boolean running = false;
+
+ /**
+ * The time when we received latest network change event.
+ */
+ private long lastNetworkChange = -1;
+
+ /**
+ * Sometimes (on windows) we got several network change events
+ * this is the time after which latest event we will skip next events.
+ */
+ private static final long NETWORK_EVENT_SILENT_TIME = 10*1000;
+
+ /**
+ * Whether network is currently connected.
+ */
+ private Boolean networkIsConnected = null;
+
+ /**
+ * The linux impl class name.
+ */
+ private static final String SYSTEM_ACTIVITY_MANAGER_LINUX_CLASS
+ = "net.java.sip.communicator.impl.sysactivity.NetworkManagerListenerImpl";
+
+ /**
+ * The android impl class name.
+ */
+ private static final String SYSTEM_ACTIVITY_MANAGER_ANDROID_CLASS
+ = "net.java.sip.communicator.impl.sysactivity.ConnectivityManagerListenerImpl";
+
+ /**
+ * The currently instantiated and working manager.
+ */
+ private SystemActivityManager currentRunningManager = null;
+
+ /**
+ * Init and start notifications.
+ */
+ public void start()
+ {
+ running = true;
+
+ // set the delegate and start notification in new thread
+ // make sure we don't block startup process
+ Thread notifystartThread
+ = new Thread(
+ new Runnable()
+ {
+ public void run()
+ {
+ SystemActivityNotifications.setDelegate(
+ SystemActivityNotificationsServiceImpl.this);
+ SystemActivityNotifications.start();
+ }
+ },
+ "SystemActivityNotificationsServiceImpl");
+ notifystartThread.setDaemon(true);
+ notifystartThread.start();
+
+ if(isSupported(SystemActivityEvent.EVENT_SYSTEM_IDLE))
+ {
+ // a thread periodically checks system idle state and if it pass the
+ // idle time for a particular listener, will inform it.
+ Thread idleNotifyThread = new Thread(
+ this,
+ "SystemActivityNotificationsServiceImpl.IdleNotifyThread");
+ idleNotifyThread.setDaemon(true);
+ idleNotifyThread.start();
+ }
+
+ if (getCurrentRunningManager() != null)
+ getCurrentRunningManager().start();
+ }
+
+ /**
+ * Stop notifications.
+ */
+ public void stop()
+ {
+ SystemActivityNotifications.stop();
+
+ if (getCurrentRunningManager() != null)
+ getCurrentRunningManager().stop();
+
+ eventDispatcher.stop();
+
+ running = false;
+
+ synchronized(this)
+ {
+ this.notifyAll();
+ }
+ }
+
+ /**
+ * Registers a listener that would be notified of changes that have occurred
+ * in the underlying system.
+ *
+ * @param listener the listener that we'd like to register for changes in
+ * the underlying system.
+ */
+ public void addSystemActivityChangeListener(
+ SystemActivityChangeListener listener)
+ {
+ eventDispatcher.addSystemActivityChangeListener(listener);
+ }
+
+ /**
+ * Remove the specified listener so that it won't receive further
+ * notifications of changes that occur in the underlying system
+ *
+ * @param listener the listener to remove.
+ */
+ public void removeSystemActivityChangeListener(
+ SystemActivityChangeListener listener)
+ {
+ eventDispatcher.removeSystemActivityChangeListener(listener);
+ }
+
+ /**
+ * Registers a listener that would be notified for idle of the system
+ * for <tt>idleTime</tt>.
+ *
+ * @param idleTime the time in milliseconds after which we will consider
+ * system to be idle. This doesn't count when system seems idle as
+ * monitor is off or screensaver is on, or desktop is locked.
+ * @param listener the listener that we'd like to register for changes in
+ * the underlying system.
+ */
+ public void addIdleSystemChangeListener(
+ long idleTime,
+ SystemActivityChangeListener listener)
+ {
+ synchronized (idleChangeListeners)
+ {
+ if (idleTime > 0
+ && !idleChangeListeners.containsKey(listener))
+ idleChangeListeners.put(listener, idleTime);
+ }
+ }
+
+ /**
+ * Remove the specified listener so that it won't receive further
+ * notifications for idle system.
+ *
+ * @param listener the listener to remove.
+ */
+ public void removeIdleSystemChangeListener(
+ SystemActivityChangeListener listener)
+ {
+ synchronized (idleChangeListeners)
+ {
+ idleChangeListeners.remove(listener);
+ }
+ }
+
+ /**
+ * The time since last user input. The time the system has been idle.
+ * @return time the system has been idle.
+ */
+ public long getTimeSinceLastInput()
+ {
+ if(SystemActivityNotifications.isLoaded())
+ return SystemActivityNotifications.getLastInput();
+ else
+ return -1;
+ }
+
+ /**
+ * Callback method when receiving notifications.
+ *
+ * @param type type of the notification.
+ */
+ public void notify(int type)
+ {
+ SystemActivityEvent evt = null;
+ switch(type)
+ {
+ case SystemActivityNotifications.NOTIFY_SLEEP :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_SLEEP);
+ break;
+ case SystemActivityNotifications.NOTIFY_WAKE :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_WAKE);
+ break;
+ case SystemActivityNotifications.NOTIFY_DISPLAY_SLEEP :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_DISPLAY_SLEEP);
+ break;
+ case SystemActivityNotifications.NOTIFY_DISPLAY_WAKE :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_DISPLAY_WAKE);
+ break;
+ case SystemActivityNotifications.NOTIFY_SCREENSAVER_START :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_SCREENSAVER_START);
+ break;
+ case SystemActivityNotifications.NOTIFY_SCREENSAVER_WILL_STOP :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_SCREENSAVER_WILL_STOP);
+ break;
+ case SystemActivityNotifications.NOTIFY_SCREENSAVER_STOP :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_SCREENSAVER_STOP);
+ break;
+ case SystemActivityNotifications.NOTIFY_SCREEN_LOCKED :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_SCREEN_LOCKED);
+ break;
+ case SystemActivityNotifications.NOTIFY_SCREEN_UNLOCKED :
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_SCREEN_UNLOCKED);
+ break;
+ case SystemActivityNotifications.NOTIFY_NETWORK_CHANGE :
+ {
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_NETWORK_CHANGE);
+ break;
+ }
+ case SystemActivityNotifications.NOTIFY_DNS_CHANGE :
+ {
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_DNS_CHANGE);
+ break;
+ }
+ case SystemActivityNotifications.NOTIFY_QUERY_ENDSESSION :
+ {
+ // both events QUERY_ENDSESSION and ENDSESSION
+ // depend on the result one after another
+ // we don't put them in new thread in order to give control
+ // in the bundles using this events.
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_QUERY_ENDSESSION);
+ eventDispatcher.fireSystemActivityEventCurrentThread(evt);
+
+ return;
+ }
+ case SystemActivityNotifications.NOTIFY_ENDSESSION :
+ {
+ // both events QUERY_ENDSESSION and ENDSESSION
+ // depend on the result one after another
+ // we don't put them in new thread in order to give control
+ // in the bundles using this events.
+ evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_ENDSESSION);
+ eventDispatcher.fireSystemActivityEventCurrentThread(evt);
+
+ return;
+ }
+ }
+
+ if (evt != null)
+ fireSystemActivityEvent(evt);
+ }
+
+ /**
+ * Callback method when receiving special network notifications.
+ *
+ * @param family family of network change (ipv6, ipv4)
+ * AF_UNSPEC = 0 (The address family is unspecified.)
+ * AF_INET = 2 (The Internet Protocol version 4 (IPv4) address family)
+ * AF_INET6 = 23 (The Internet Protocol version 6 (IPv6) address family)
+ * @param luidIndex unique index of interface
+ * @param name name of the interface
+ * @param type of the interface
+ * Possible values for the interface type are listed in the Ipifcons.h file.
+ * common values:
+ * IF_TYPE_OTHER = 1 (Some other type of network interface.)
+ * IF_TYPE_ETHERNET_CSMACD = 6 (An Ethernet network interface.)
+ * IF_TYPE_ISO88025_TOKENRING = 9 (A token ring network interface.)
+ * IF_TYPE_PPP = 23 (A PPP network interface.)
+ * IF_TYPE_SOFTWARE_LOOPBACK = 24 (A software loopback network interface.)
+ * IF_TYPE_IEEE80211 = 71 (An IEEE 802.11 wireless network interface.)
+ * IF_TYPE_TUNNEL = 131 (A tunnel type encapsulation network interface.)
+ * IF_TYPE_IEEE1394 = 144 (An IEEE 1394 (Firewire) high performance
+ * serial bus network interface.)
+ * @param connected whether interface is connected or not.
+ */
+ public void notifyNetworkChange(
+ int family,
+ long luidIndex,
+ String name,
+ long type,
+ boolean connected)
+ {
+ long current = System.currentTimeMillis();
+ if(current - lastNetworkChange <= NETWORK_EVENT_SILENT_TIME
+ && (networkIsConnected != null && networkIsConnected.equals(connected)))
+ {
+ networkIsConnected = connected;
+ return;
+ }
+
+ lastNetworkChange = current;
+ networkIsConnected = connected;
+
+ SystemActivityEvent evt = new SystemActivityEvent(this,
+ SystemActivityEvent.EVENT_NETWORK_CHANGE);
+ fireSystemActivityEvent(evt);
+ }
+
+ /**
+ * The thread run method that handles idle notifies.
+ *
+ * @see Thread#run()
+ */
+ public void run()
+ {
+ while(running)
+ {
+ try
+ {
+ long idleTime = 0;
+ if(idleChangeListeners.size() > 0)
+ {
+ // check
+ idleTime = SystemActivityNotifications.getLastInput();
+
+ if((idleTime < idleStateCheckDelay)
+ && (listenersInIdleState.size() > 0))
+ {
+ for(SystemActivityChangeListener l
+ : listenersInIdleState)
+ {
+ fireSystemIdleEndEvent(l);
+ }
+ listenersInIdleState.clear();
+ }
+
+ for(Map.Entry<SystemActivityChangeListener, Long> entry
+ : idleChangeListeners.entrySet())
+ {
+ SystemActivityChangeListener listener =
+ entry.getKey();
+
+ if(!listenersInIdleState.contains(listener)
+ && (entry.getValue() <= idleTime))
+ {
+ fireSystemIdleEvent(listener);
+
+ listenersInIdleState.add(listener);
+ }
+ }
+ }
+
+ // if the minimum check for idle is X minutes
+ // we will wait before checking (X - Y + 1sec)
+ // where Y is the last idle time returned by OS
+ if(listenersInIdleState.size() > 0)
+ {
+ idleStateCheckDelay = CHECK_FOR_IDLE_WHEN_IDLE;
+ }
+ else if(idleTime != 0)
+ {
+ long minIdleSetting = CHECK_FOR_IDLE_DEFAULT;
+
+ if(!idleChangeListeners.isEmpty())
+ minIdleSetting =
+ Collections.min(idleChangeListeners.values());
+
+ int newSetting = (int)(minIdleSetting - idleTime) + 1000;
+
+ if(newSetting > 0)
+ idleStateCheckDelay = newSetting;
+ else
+ idleStateCheckDelay = CHECK_FOR_IDLE_DEFAULT;
+ }
+ else
+ {
+ idleStateCheckDelay = CHECK_FOR_IDLE_DEFAULT;
+ }
+
+ // wait for the specified time
+ synchronized(this)
+ {
+ this.wait(idleStateCheckDelay);
+ }
+ }
+ catch(UnsatisfiedLinkError t)
+ {
+ logger.error("Missing native impl", t);
+ return;
+ }
+ catch(Throwable t)
+ {
+ logger.error("Error checking for idle", t);
+ }
+ }
+ }
+
+ /**
+ * Delivers the specified event to all registered listeners.
+ *
+ * @param evt the <tt>SystemActivityEvent</tt> that we'd like delivered to
+ * all registered message listeners.
+ */
+ protected void fireSystemActivityEvent(SystemActivityEvent evt)
+ {
+ int eventID = evt.getEventID();
+
+ // Add network activity info to track wake up problems.
+ if (logger.isInfoEnabled()
+ && ((eventID == SystemActivityEvent.EVENT_NETWORK_CHANGE)
+ || (eventID == SystemActivityEvent.EVENT_DNS_CHANGE)))
+ {
+ logger.info("Received system activity event: " + evt);
+ }
+
+ if (eventID == SystemActivityEvent.EVENT_NETWORK_CHANGE)
+ {
+ // Give time to Java to dispatch same event and populate its network
+ // interfaces.
+ eventDispatcher.fireSystemActivityEvent(evt, 500);
+ }
+ else
+ eventDispatcher.fireSystemActivityEvent(evt);
+ }
+
+ /**
+ * Delivers the specified event to all registered listeners.
+ *
+ * @param listener listener to inform
+ */
+ protected void fireSystemIdleEvent(SystemActivityChangeListener listener)
+ {
+ SystemActivityEvent evt
+ = new SystemActivityEvent(
+ this,
+ SystemActivityEvent.EVENT_SYSTEM_IDLE);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Dispatching SystemActivityEvent evt=" + evt);
+
+ try
+ {
+ listener.activityChanged(evt);
+ }
+ catch (Throwable t)
+ {
+ if (t instanceof ThreadDeath)
+ throw (ThreadDeath) t;
+ else
+ logger.error("Error delivering event", t);
+ }
+ }
+
+ /**
+ * Delivers the specified event to listener.
+ *
+ * @param listener listener to inform
+ */
+ protected void fireSystemIdleEndEvent(
+ SystemActivityChangeListener listener)
+ {
+ SystemActivityEvent evt
+ = new SystemActivityEvent(
+ this,
+ SystemActivityEvent.EVENT_SYSTEM_IDLE_END);
+
+ if (logger.isDebugEnabled())
+ logger.debug("Dispatching SystemActivityEvent evt=" + evt);
+
+ try
+ {
+ listener.activityChanged(evt);
+ }
+ catch (Throwable t)
+ {
+ if (t instanceof ThreadDeath)
+ throw (ThreadDeath) t;
+ else
+ logger.error("Error delivering event", t);
+ }
+ }
+
+ /**
+ * Can check whether an event id is supported on
+ * current operation system.
+ * Simple return what is implemented in native, and checks
+ * are made when possible, for example linux cannot connect
+ * to NM through dbus.
+ * @param eventID the event to check.
+ * @return whether the supplied event id is supported.
+ */
+ public boolean isSupported(int eventID)
+ {
+ if(OSUtils.IS_WINDOWS)
+ {
+ switch(eventID)
+ {
+ case SystemActivityEvent.EVENT_SLEEP:
+ case SystemActivityEvent.EVENT_WAKE:
+ case SystemActivityEvent.EVENT_NETWORK_CHANGE:
+ case SystemActivityEvent.EVENT_SYSTEM_IDLE:
+ case SystemActivityEvent.EVENT_SYSTEM_IDLE_END:
+ return SystemActivityNotifications.isLoaded();
+ default:
+ return false;
+ }
+ }
+ else if(OSUtils.IS_MAC)
+ {
+ return SystemActivityNotifications.isLoaded();
+ }
+ else if(OSUtils.IS_LINUX)
+ {
+ switch(eventID)
+ {
+ case SystemActivityEvent.EVENT_SLEEP:
+ case SystemActivityEvent.EVENT_NETWORK_CHANGE:
+ {
+ SystemActivityManager currentRunningManager
+ = getCurrentRunningManager();
+
+ return
+ (currentRunningManager == null)
+ ? false
+ : currentRunningManager.isConnected();
+ }
+ case SystemActivityEvent.EVENT_SYSTEM_IDLE:
+ case SystemActivityEvent.EVENT_SYSTEM_IDLE_END:
+ return SystemActivityNotifications.isLoaded();
+ default:
+ return false;
+ }
+ }
+ else if(OSUtils.IS_ANDROID)
+ {
+ return (eventID == SystemActivityEvent.EVENT_NETWORK_CHANGE);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Returns or instantiate the manager.
+ * @return
+ */
+ private SystemActivityManager getCurrentRunningManager()
+ {
+ if(currentRunningManager == null)
+ {
+ try
+ {
+ String className = null;
+ if(OSUtils.IS_LINUX)
+ {
+ className = SYSTEM_ACTIVITY_MANAGER_LINUX_CLASS;
+ }
+ else if(OSUtils.IS_ANDROID)
+ {
+ className = SYSTEM_ACTIVITY_MANAGER_ANDROID_CLASS;
+ }
+
+ if(className != null)
+ currentRunningManager = (SystemActivityManager)
+ Class.forName(className).newInstance();
+ }
+ catch(Throwable t)
+ {
+ logger.error("Error creating manager", t);
+ }
+ }
+
+ return currentRunningManager;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/version/VersionImpl.java b/src/net/java/sip/communicator/impl/version/VersionImpl.java
index 8bed4ec..74e199c 100644
--- a/src/net/java/sip/communicator/impl/version/VersionImpl.java
+++ b/src/net/java/sip/communicator/impl/version/VersionImpl.java
@@ -42,7 +42,7 @@ public class VersionImpl
* number changes when a relatively extensive set of new features and
* possibly rearchitecturing have been applied to the Jitsi.
*/
- public static final int VERSION_MINOR = 9;
+ public static final int VERSION_MINOR = 11;
/**
* Indicates whether this version represents a prerelease (i.e. a