/** * Checks whether the carbon is supported by the server or not. * * @return <tt>true</tt> if carbon is supported by the server and <tt>false</tt> if not. */ private boolean isCarbonSupported() { try { return jabberProvider .getDiscoveryManager() .discoverInfo(jabberProvider.getAccountID().getService()) .containsFeature(CarbonPacketExtension.NAMESPACE); } catch (XMPPException e) { logger.warn("Failed to retrieve carbon support." + e.getMessage()); } return false; }
/** * {@inheritDoc} * * <p>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); } }
/** * Subscribes this provider as interested in receiving notifications for new mail messages from * Google mail services such as Gmail or Google Apps. */ private void subscribeForGmailNotifications() { // first check support for the notification service String accountIDService = jabberProvider.getAccountID().getService(); boolean notificationsAreSupported = jabberProvider.isFeatureSupported(accountIDService, NewMailNotificationIQ.NAMESPACE); if (!notificationsAreSupported) { if (logger.isDebugEnabled()) logger.debug( accountIDService + " does not seem to provide a Gmail notification " + " service so we won't be trying to subscribe for it"); return; } if (logger.isDebugEnabled()) logger.debug( accountIDService + " seems to provide a Gmail notification " + " service so we will try to subscribe for it"); ProviderManager providerManager = ProviderManager.getInstance(); providerManager.addIQProvider( MailboxIQ.ELEMENT_NAME, MailboxIQ.NAMESPACE, new MailboxIQProvider()); providerManager.addIQProvider( NewMailNotificationIQ.ELEMENT_NAME, NewMailNotificationIQ.NAMESPACE, new NewMailNotificationProvider()); Connection connection = jabberProvider.getConnection(); connection.addPacketListener(new MailboxIQListener(), new PacketTypeFilter(MailboxIQ.class)); connection.addPacketListener( new NewMailNotificationListener(), new PacketTypeFilter(NewMailNotificationIQ.class)); if (opSetPersPresence.getCurrentStatusMessage().equals(JabberStatusEnum.OFFLINE)) return; // create a query with -1 values for newer-than-tid and // newer-than-time attributes MailboxQueryIQ mailboxQuery = new MailboxQueryIQ(); if (logger.isTraceEnabled()) logger.trace( "sending mailNotification for acc: " + jabberProvider.getAccountID().getAccountUniqueID()); jabberProvider.getConnection().sendPacket(mailboxQuery); }
/** * Constructor * * @param provider is the provider that created us */ public OperationSetFileTransferJabberImpl(ProtocolProviderServiceJabberImpl provider) { this.jabberProvider = provider; provider.addRegistrationStateChangeListener(new RegistrationStateListener()); // use only ibb for file transfers FileTransferNegotiator.IBB_ONLY = true; }
/** * 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 (jabberProvider == null) throw new IllegalStateException( "The provider must be non-null and signed on the " + "service before being able to send a file."); else if (!jabberProvider.isRegistered()) { // if we are not registered but the current status is online // change the current status if (opSetPersPresence.getPresenceStatus().isOnline()) { opSetPersPresence.fireProviderStatusChangeEvent( opSetPersPresence.getPresenceStatus(), jabberProvider.getJabberStatusEnum().getStatus(JabberStatusEnum.OFFLINE)); } throw new IllegalStateException( "The provider must be signed on the service before " + "being able to send a file."); } }
/** Initialize additional services, like gmail notifications and message carbons. */ private void initAdditionalServices() { // subscribe for Google (Gmail or Google Apps) notifications // for new mail messages. boolean enableGmailNotifications = jabberProvider .getAccountID() .getAccountPropertyBoolean("GMAIL_NOTIFICATIONS_ENABLED", false); if (enableGmailNotifications) subscribeForGmailNotifications(); boolean enableCarbon = isCarbonSupported() && !jabberProvider .getAccountID() .getAccountPropertyBoolean(ProtocolProviderFactory.IS_CARBON_DISABLED, false); if (enableCarbon) { enableDisableCarbon(true); } else { isCarbonEnabled = false; } }
/** * Creates an instance of this operation set. * * @param provider a reference to the <tt>ProtocolProviderServiceImpl</tt> that created us and * that we'll use for retrieving the underlying aim connection. */ OperationSetBasicInstantMessagingJabberImpl(ProtocolProviderServiceJabberImpl provider) { this.jabberProvider = provider; packetFilters.add(new GroupMessagePacketFilter()); packetFilters.add(new PacketTypeFilter(org.jivesoftware.smack.packet.Message.class)); provider.addRegistrationStateChangeListener(new RegistrationStateListener()); ProviderManager man = ProviderManager.getInstance(); MessageCorrectionExtensionProvider extProvider = new MessageCorrectionExtensionProvider(); man.addExtensionProvider( MessageCorrectionExtension.ELEMENT_NAME, MessageCorrectionExtension.NAMESPACE, extProvider); }
/** * Sends enable or disable carbon packet to the server. * * @param enable if <tt>true</tt> sends enable packet otherwise sends disable packet. */ private void enableDisableCarbon(final boolean enable) { IQ iq = new IQ() { @Override public String getChildElementXML() { return "<" + (enable ? "enable" : "disable") + " xmlns='urn:xmpp:carbons:2' />"; } }; Packet response = null; try { PacketCollector packetCollector = jabberProvider .getConnection() .createPacketCollector(new PacketIDFilter(iq.getPacketID())); iq.setFrom(jabberProvider.getOurJID()); iq.setType(IQ.Type.SET); jabberProvider.getConnection().sendPacket(iq); response = packetCollector.nextResult(SmackConfiguration.getPacketReplyTimeout()); packetCollector.cancel(); } catch (Exception e) { logger.error("Failed to enable carbon.", e); } isCarbonEnabled = false; if (response == null) { logger.error("Failed to enable carbon. No response is received."); } else if (response.getError() != null) { logger.error("Failed to enable carbon: " + response.getError()); } else if (!(response instanceof IQ) || !((IQ) response).getType().equals(IQ.Type.RESULT)) { logger.error("Failed to enable carbon. The response is not correct."); } else { isCarbonEnabled = true; } }
/** * Initiate a Google Talk session {@link SessionIQ}. * * @param sessionInitiateExtensions a collection of additional and optional * <tt>PacketExtension</tt>s to be added to the <tt>initiate</tt> {@link SessionIQ} which is * to initiate the session with this <tt>CallPeerGTalkImpl</tt> * @throws OperationFailedException exception */ protected synchronized void initiateSession(Iterable<PacketExtension> sessionInitiateExtensions) throws OperationFailedException { sid = SessionIQ.generateSID(); initiator = false; // Create the media description that we'd like to send to the other side. RtpDescriptionPacketExtension offer = getMediaHandler().createDescription(); ProtocolProviderServiceJabberImpl protocolProvider = getProtocolProvider(); sessionInitIQ = GTalkPacketFactory.createSessionInitiate( protocolProvider.getOurJID(), this.peerJID, sid, offer); if (sessionInitiateExtensions != null) { for (PacketExtension sessionInitiateExtension : sessionInitiateExtensions) { sessionInitIQ.addExtension(sessionInitiateExtension); } } protocolProvider.getConnection().sendPacket(sessionInitIQ); // for Google Voice JID without resource we do not harvest and send // candidates if (getAddress().endsWith(ProtocolProviderServiceJabberImpl.GOOGLE_VOICE_DOMAIN)) { return; } getMediaHandler() .harvestCandidates( offer.getPayloadTypes(), new CandidatesSender() { public void sendCandidates(Iterable<GTalkCandidatePacketExtension> candidates) { CallPeerGTalkImpl.this.sendCandidates(candidates); } }); }
/** * Determines whether the protocol supports the supplied content type for the given contact. * * @param contentType the type we want to check * @param contact contact which is checked for supported contentType * @return <tt>true</tt> if the contact supports it and <tt>false</tt> otherwise. */ @Override public boolean isContentTypeSupported(String contentType, Contact contact) { // by default we support default mime type, for other mimetypes // method must be overriden if (contentType.equals(DEFAULT_MIME_TYPE)) return true; else if (contentType.equals(HTML_MIME_TYPE)) { String toJID = recentJIDForAddress.get(contact.getAddress()); if (toJID == null) toJID = contact.getAddress(); return jabberProvider.isFeatureListSupported(toJID, HTML_NAMESPACE); } return false; }
/** * Sends local candidate addresses from the local peer to the remote peer using the * <tt>candidates</tt> {@link SessionIQ}. * * @param candidates the local candidate addresses to be sent from the local peer to the remote * peer using the <tt>candidates</tt> {@link SessionIQ} */ protected void sendCandidates(Iterable<GTalkCandidatePacketExtension> candidates) { ProtocolProviderServiceJabberImpl protocolProvider = getProtocolProvider(); SessionIQ candidatesIQ = new SessionIQ(); candidatesIQ.setGTalkType(GTalkType.CANDIDATES); candidatesIQ.setFrom(protocolProvider.getOurJID()); candidatesIQ.setInitiator(isInitiator() ? getAddress() : protocolProvider.getOurJID()); candidatesIQ.setID(getSID()); candidatesIQ.setTo(getAddress()); candidatesIQ.setType(IQ.Type.SET); for (GTalkCandidatePacketExtension candidate : candidates) { // Android phone and Google Talk client does not seems to like IPv6 // candidates since it reject the IQ candidates with an error // so do not send IPv6 candidates to Android phone or Talk client if (isAndroidOrVtokOrTalkClient(getAddress()) && NetworkUtils.isIPv6Address(candidate.getAddress())) continue; candidatesIQ.addExtension(candidate); } protocolProvider.getConnection().sendPacket(candidatesIQ); }
/** * 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(); }
/** * 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); }
/** * 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)); }
/** * Sends a file transfer request to the given <tt>toContact</tt>. * * @return the transfer object * @param toContact the contact that should receive the file * @param file file to send * @param gw special gateway to be used for receiver if its jid misses the domain part */ FileTransfer sendFile(Contact toContact, File file, String gw) throws IllegalStateException, IllegalArgumentException, OperationNotSupportedException { OutgoingFileTransferJabberImpl outgoingTransfer = null; try { assertConnected(); if (file.length() > getMaximumFileLength()) throw new IllegalArgumentException("File length exceeds the allowed one for this protocol"); String fullJid = null; // Find the jid of the contact which support file transfer // and is with highest priority if more than one found // if we have equals priorities // choose the one that is more available OperationSetMultiUserChat mucOpSet = jabberProvider.getOperationSet(OperationSetMultiUserChat.class); if (mucOpSet != null && mucOpSet.isPrivateMessagingContact(toContact.getAddress())) { fullJid = toContact.getAddress(); } else { Iterator<Presence> iter = jabberProvider.getConnection().getRoster().getPresences(toContact.getAddress()); int bestPriority = -1; PresenceStatus jabberStatus = null; while (iter.hasNext()) { Presence presence = iter.next(); if (jabberProvider.isFeatureListSupported( presence.getFrom(), new String[] { "http://jabber.org/protocol/si", "http://jabber.org/protocol/si/profile/file-transfer" })) { int priority = (presence.getPriority() == Integer.MIN_VALUE) ? 0 : presence.getPriority(); if (priority > bestPriority) { bestPriority = priority; fullJid = presence.getFrom(); jabberStatus = OperationSetPersistentPresenceJabberImpl.jabberStatusToPresenceStatus( presence, jabberProvider); } else if (priority == bestPriority && jabberStatus != null) { PresenceStatus tempStatus = OperationSetPersistentPresenceJabberImpl.jabberStatusToPresenceStatus( presence, jabberProvider); if (tempStatus.compareTo(jabberStatus) > 0) { fullJid = presence.getFrom(); jabberStatus = tempStatus; } } } } } // First we check if file transfer is at all supported for this // contact. if (fullJid == null) { throw new OperationNotSupportedException( "Contact client or server does not support file transfers."); } if (gw != null && !fullJid.contains("@") && !fullJid.endsWith(gw)) { fullJid = fullJid + "@" + gw; } OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(fullJid); outgoingTransfer = new OutgoingFileTransferJabberImpl(toContact, file, transfer, jabberProvider); // Notify all interested listeners that a file transfer has been // created. FileTransferCreatedEvent event = new FileTransferCreatedEvent(outgoingTransfer, new Date()); fireFileTransferCreated(event); // Send the file through the Jabber file transfer. transfer.sendFile(file, "Sending file"); // Start the status and progress thread. new FileTransferProgressThread(transfer, outgoingTransfer).start(); } catch (XMPPException e) { logger.error("Failed to send file.", e); } return outgoingTransfer; }
/** * Creates an html description of the specified mailbox. * * @param mailboxIQ the mailboxIQ that we are to describe. * @return an html description of <tt>mailboxIQ</tt> */ private String createMailboxDescription(MailboxIQ mailboxIQ) { int threadCount = mailboxIQ.getThreadCount(); String resourceHeaderKey = threadCount > 1 ? "service.gui.NEW_GMAIL_MANY_HEADER" : "service.gui.NEW_GMAIL_HEADER"; String resourceFooterKey = threadCount > 1 ? "service.gui.NEW_GMAIL_MANY_FOOTER" : "service.gui.NEW_GMAIL_FOOTER"; // FIXME Escape HTML! String newMailHeader = JabberActivator.getResources() .getI18NString( resourceHeaderKey, new String[] { jabberProvider.getAccountID().getService(), // {0} - service name mailboxIQ.getUrl(), // {1} - inbox URI Integer.toString(threadCount) // {2} - thread count }); StringBuilder message = new StringBuilder(newMailHeader); // we now start an html table for the threads. message.append("<table width=100% cellpadding=2 cellspacing=0 "); message.append("border=0 bgcolor=#e8eef7>"); Iterator<MailThreadInfo> threads = mailboxIQ.threads(); String maxThreadsStr = (String) JabberActivator.getConfigurationService() .getProperty(PNAME_MAX_GMAIL_THREADS_PER_NOTIFICATION); int maxThreads = 5; try { if (maxThreadsStr != null) maxThreads = Integer.parseInt(maxThreadsStr); } catch (NumberFormatException e) { if (logger.isDebugEnabled()) logger.debug("Failed to parse max threads count: " + maxThreads + ". Going for default."); } // print a maximum of MAX_THREADS for (int i = 0; i < maxThreads && threads.hasNext(); i++) { message.append(threads.next().createHtmlDescription()); } message.append("</table><br/>"); if (threadCount > maxThreads) { String messageFooter = JabberActivator.getResources() .getI18NString( resourceFooterKey, new String[] { mailboxIQ.getUrl(), // {0} - inbox URI Integer.toString(threadCount - maxThreads) // {1} - thread count }); message.append(messageFooter); } return message.toString(); }
/** * Helper function used to send a message to a contact, with the given extensions attached. * * @param to The contact to send the message to. * @param toResource The resource to send the message to or null if no resource has been specified * @param message The message to send. * @param extensions The XMPP extensions that should be attached to the message before sending. * @return The MessageDeliveryEvent that resulted after attempting to send this message, so the * calling function can modify it if needed. */ private MessageDeliveredEvent sendMessage( Contact to, ContactResource toResource, Message message, PacketExtension[] extensions) { if (!(to instanceof ContactJabberImpl)) throw new IllegalArgumentException("The specified contact is not a Jabber contact." + to); assertConnected(); org.jivesoftware.smack.packet.Message msg = new org.jivesoftware.smack.packet.Message(); String toJID = null; if (toResource != null) { if (toResource.equals(ContactResource.BASE_RESOURCE)) { toJID = to.getAddress(); } else toJID = ((ContactResourceJabberImpl) toResource).getFullJid(); } if (toJID == null) { toJID = to.getAddress(); } msg.setPacketID(message.getMessageUID()); msg.setTo(toJID); for (PacketExtension ext : extensions) { msg.addExtension(ext); } if (logger.isTraceEnabled()) logger.trace("Will send a message to:" + toJID + " chat.jid=" + toJID); MessageDeliveredEvent msgDeliveryPendingEvt = new MessageDeliveredEvent(message, to, toResource); MessageDeliveredEvent[] transformedEvents = messageDeliveryPendingTransform(msgDeliveryPendingEvt); if (transformedEvents == null || transformedEvents.length == 0) return null; for (MessageDeliveredEvent event : transformedEvents) { String content = event.getSourceMessage().getContent(); if (message.getContentType().equals(HTML_MIME_TYPE)) { msg.setBody(Html2Text.extractText(content)); // Check if the other user supports XHTML messages // make sure we use our discovery manager as it caches calls if (jabberProvider.isFeatureListSupported(toJID, HTML_NAMESPACE)) { // Add the XHTML text to the message XHTMLManager.addBody(msg, OPEN_BODY_TAG + content + CLOSE_BODY_TAG); } } else { // this is plain text so keep it as it is. msg.setBody(content); } // msg.addExtension(new Version()); if (event.isMessageEncrypted() && isCarbonEnabled) { msg.addExtension(new CarbonPacketExtension.PrivateExtension()); } MessageEventManager.addNotificationsRequests(msg, true, false, false, true); String threadID = getThreadIDForAddress(toJID); if (threadID == null) threadID = nextThreadID(); msg.setThread(threadID); msg.setType(org.jivesoftware.smack.packet.Message.Type.chat); msg.setFrom(jabberProvider.getConnection().getUser()); jabberProvider.getConnection().sendPacket(msg); putJidForAddress(toJID, threadID); } return new MessageDeliveredEvent(message, to, toResource); }
/** * 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)); }