/** * 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); }
/** * Dispatches the event received from a JAIN-SIP <tt>SipProvider</tt> to one of our "candidate * recipient" listeners. * * @param event the event received for a <tt>SipProvider</tt>. */ public void processIOException(IOExceptionEvent event) { try { if (logger.isTraceEnabled()) logger.trace(event); // impossible to dispatch, log here if (logger.isDebugEnabled()) logger.debug("@todo implement processIOException()"); } catch (Throwable exc) { // any exception thrown within our code should be caught here // so that we could log it rather than interrupt stack activity with // it. this.logApplicationException(DialogTerminatedEvent.class, exc); } }
/** * Sets the icon for the given file. * * @param file the file to set an icon for * @return the byte array containing the thumbnail */ private byte[] getFileThumbnail(File file) { byte[] bytes = null; if (FileUtils.isImage(file.getName())) { try { ImageIcon image = new ImageIcon(file.toURI().toURL()); int width = image.getIconWidth(); int height = image.getIconHeight(); if (width > THUMBNAIL_WIDTH) width = THUMBNAIL_WIDTH; if (height > THUMBNAIL_HEIGHT) height = THUMBNAIL_HEIGHT; bytes = ImageUtils.getScaledInstanceInBytes(image.getImage(), width, height); } catch (MalformedURLException e) { if (logger.isDebugEnabled()) logger.debug("Could not locate image.", e); } } return bytes; }
/** * Sends a file transfer request to the given <tt>toContact</tt> by specifying the local and * remote file path and the <tt>fromContact</tt>, sending the file. * * @param toContact the contact that should receive the file * @param file the file to send * @return the transfer object * @throws IllegalStateException if the protocol provider is not registered or connected * @throws IllegalArgumentException if some of the arguments doesn't fit the protocol requirements */ public FileTransfer sendFile(Contact toContact, File file) throws IllegalStateException, IllegalArgumentException { assertConnected(); if (file.length() > getMaximumFileLength()) throw new IllegalArgumentException("File length exceeds the allowed one for this protocol"); // Get the aim connection AimConnection aimConnection = icqProvider.getAimConnection(); // Create an outgoing file transfer instance OutgoingFileTransfer outgoingFileTransfer = aimConnection .getIcbmService() .getRvConnectionManager() .createOutgoingFileTransfer(new Screenname(toContact.getAddress())); String id = String.valueOf(outgoingFileTransfer.getRvSessionInfo().getRvSession().getRvSessionId()); FileTransferImpl outFileTransfer = new FileTransferImpl(outgoingFileTransfer, id, toContact, file, FileTransfer.OUT); // Adding the file to the outgoing file transfer try { outgoingFileTransfer.setSingleFile(new File(file.getPath())); } catch (IOException e) { if (logger.isDebugEnabled()) logger.debug("Error sending file", e); return null; } // Notify all interested listeners that a file transfer has been // created. FileTransferCreatedEvent event = new FileTransferCreatedEvent(outFileTransfer, new Date()); fireFileTransferCreated(event); // Sending the file outgoingFileTransfer.sendRequest(new InvitationMessage("")); outFileTransfer.fireStatusChangeEvent(FileTransferStatusChangeEvent.PREPARING); return outFileTransfer; }
/** * Sends the <tt>message</tt> to the destination indicated by the <tt>to</tt> contact. * * @param to the <tt>Contact</tt> to send <tt>message</tt> to * @param message the <tt>Message</tt> to send. * @throws java.lang.IllegalStateException if the underlying stack is not registered and * initialized. * @throws java.lang.IllegalArgumentException if <tt>to</tt> is not an instance of ContactImpl. */ public void sendInstantMessage(Contact to, Message message) throws IllegalStateException, IllegalArgumentException { if (!(to instanceof ContactSipImpl)) throw new IllegalArgumentException("The specified contact is not a Sip contact." + to); assertConnected(); // offline message if (to.getPresenceStatus().equals(sipStatusEnum.getStatus(SipStatusEnum.OFFLINE)) && !offlineMessageSupported) { if (logger.isDebugEnabled()) logger.debug("trying to send a message to an offline contact"); fireMessageDeliveryFailed( message, to, MessageDeliveryFailedEvent.OFFLINE_MESSAGES_NOT_SUPPORTED); return; } // create the message Request mes; try { mes = createMessageRequest(to, message); } catch (OperationFailedException ex) { logger.error("Failed to create the message.", ex); fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.INTERNAL_ERROR); return; } try { sendMessageRequest(mes, to, message); } catch (TransactionUnavailableException ex) { logger.error( "Failed to create messageTransaction.\n" + "This is most probably a network connection error.", ex); fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.NETWORK_FAILURE); return; } catch (SipException ex) { logger.error("Failed to send the message.", ex); fireMessageDeliveryFailed(message, to, MessageDeliveryFailedEvent.INTERNAL_ERROR); return; } }
/** * Notify all listeners of the corresponding account detail change event. * * @param source the protocol provider service source * @param eventID the int ID of the event to dispatch * @param oldValue the value that the changed property had before the change occurred. * @param newValue the value that the changed property currently has (after the change has * occurred). */ public void fireServerStoredDetailsChangeEvent( ProtocolProviderService source, int eventID, Object oldValue, Object newValue) { ServerStoredDetailsChangeEvent evt = new ServerStoredDetailsChangeEvent(source, eventID, oldValue, newValue); Collection<ServerStoredDetailsChangeListener> listeners; synchronized (serverStoredDetailsListeners) { listeners = new ArrayList<ServerStoredDetailsChangeListener>(serverStoredDetailsListeners); } if (logger.isDebugEnabled()) logger.debug( "Dispatching a Contact Property Change Event to" + listeners.size() + " listeners. Evt=" + evt); for (ServerStoredDetailsChangeListener listener : listeners) listener.serverStoredDetailsChanged(evt); }
/** * Handles buttons action events. * * @param evt the <tt>ActionEvent</tt> that notified us */ public void actionPerformed(ActionEvent evt) { JButton sourceButton = (JButton) evt.getSource(); if (sourceButton.equals(openFileButton)) { this.openFile(downloadFile); } else if (sourceButton.equals(openFolderButton)) { try { File downloadDir = GuiActivator.getFileAccessService().getDefaultDownloadDirectory(); GuiActivator.getDesktopService().open(downloadDir); } catch (IllegalArgumentException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_DOES_NOT_EXIST")); } catch (NullPointerException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_DOES_NOT_EXIST")); } catch (UnsupportedOperationException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FILE_OPEN_NOT_SUPPORTED")); } catch (SecurityException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_NO_PERMISSION")); } catch (IOException e) { if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_NO_APPLICATION")); } catch (Exception e) { if (logger.isDebugEnabled()) logger.debug("Unable to open file.", e); this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_FAILED")); } } else if (sourceButton.equals(cancelButton)) { if (fileTransfer != null) fileTransfer.cancel(); } }
/** Thread entry point. */ @Override public void run() { int status; long progress; String statusReason = ""; while (true) { try { Thread.sleep(10); status = parseJabberStatus(jabberTransfer.getStatus()); progress = fileTransfer.getTransferedBytes(); if (status == FileTransferStatusChangeEvent.FAILED || status == FileTransferStatusChangeEvent.COMPLETED || status == FileTransferStatusChangeEvent.CANCELED || status == FileTransferStatusChangeEvent.REFUSED) { if (fileTransfer instanceof OutgoingFileTransferJabberImpl) { ((OutgoingFileTransferJabberImpl) fileTransfer).removeThumbnailRequestListener(); } // sometimes a filetransfer can be preparing // and than completed : // transfered in one iteration of current thread // so it won't go through intermediate state - inProgress // make sure this won't happen if (status == FileTransferStatusChangeEvent.COMPLETED && fileTransfer.getStatus() == FileTransferStatusChangeEvent.PREPARING) { fileTransfer.fireStatusChangeEvent( FileTransferStatusChangeEvent.IN_PROGRESS, "Status changed"); fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress); } break; } fileTransfer.fireStatusChangeEvent(status, "Status changed"); fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress); } catch (InterruptedException e) { if (logger.isDebugEnabled()) logger.debug("Unable to sleep thread.", e); } } if (jabberTransfer.getError() != null) { logger.error( "An error occured while transfering file: " + jabberTransfer.getError().getMessage()); } if (jabberTransfer.getException() != null) { logger.error( "An exception occured while transfering file: ", jabberTransfer.getException()); if (jabberTransfer.getException() instanceof XMPPException) { XMPPError error = ((XMPPException) jabberTransfer.getException()).getXMPPError(); if (error != null) if (error.getCode() == 406 || error.getCode() == 403) status = FileTransferStatusChangeEvent.REFUSED; } statusReason = jabberTransfer.getException().getMessage(); } if (initialFileSize > 0 && status == FileTransferStatusChangeEvent.COMPLETED && fileTransfer.getTransferedBytes() < initialFileSize) { status = FileTransferStatusChangeEvent.CANCELED; } fileTransfer.fireStatusChangeEvent(status, statusReason); fileTransfer.fireProgressChangeEvent(System.currentTimeMillis(), progress); }
/** * 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(); }
/** * Logs exceptions that have occurred in the application while processing events originating from * the stack. * * @param eventClass the class of the jain-sip event that we were handling when the exception was * thrown. * @param exc the exception that we need to log. */ private void logApplicationException(Class<DialogTerminatedEvent> eventClass, Throwable exc) { String message = "An error occurred while processing event of type: " + eventClass.getName(); logger.error(message, exc); if (logger.isDebugEnabled()) logger.debug(message, exc); }
/** * Find the <tt>ProtocolProviderServiceSipImpl</tt> (one of our "candidate recipient" listeners) * which this <tt>request</tt> should be dispatched to. The strategy is to look first at the * request URI, and then at the To field to find a matching candidate for dispatching. Note that * this method takes a <tt>Request</tt> as param, and not a <tt>ServerTransaction</tt>, because * sometimes <tt>RequestEvent</tt>s have no associated <tt>ServerTransaction</tt>. * * @param request the <tt>Request</tt> to find a recipient for. * @return a suitable <tt>ProtocolProviderServiceSipImpl</tt>. */ private ProtocolProviderServiceSipImpl findTargetFor(Request request) { if (request == null) { logger.error("request shouldn't be null."); return null; } List<ProtocolProviderServiceSipImpl> currentListenersCopy = new ArrayList<ProtocolProviderServiceSipImpl>(this.getSipListeners()); // Let's first narrow down candidate choice by comparing // addresses and ports (no point in delivering to a provider with a // non matching IP address since they will reject it anyway). filterByAddress(currentListenersCopy, request); if (currentListenersCopy.size() == 0) { logger.error("no listeners"); return null; } URI requestURI = request.getRequestURI(); if (requestURI.isSipURI()) { String requestUser = ((SipURI) requestURI).getUser(); List<ProtocolProviderServiceSipImpl> candidates = new ArrayList<ProtocolProviderServiceSipImpl>(); // check if the Request-URI username is // one of ours usernames for (ProtocolProviderServiceSipImpl listener : currentListenersCopy) { String ourUserID = listener.getAccountID().getUserID(); // logger.trace(ourUserID + " *** " + requestUser); if (ourUserID.equals(requestUser)) { if (logger.isTraceEnabled()) logger.trace("suitable candidate found: " + listener.getAccountID()); candidates.add(listener); } } // the perfect match // every other case is approximation if (candidates.size() == 1) { ProtocolProviderServiceSipImpl perfectMatch = candidates.get(0); if (logger.isTraceEnabled()) logger.trace("Will dispatch to \"" + perfectMatch.getAccountID() + "\""); return perfectMatch; } // more than one account match if (candidates.size() > 1) { // check if a custom param exists in the contact // address (set for registrar accounts) for (ProtocolProviderServiceSipImpl candidate : candidates) { String hostValue = ((SipURI) requestURI).getParameter(SipStackSharing.CONTACT_ADDRESS_CUSTOM_PARAM_NAME); if (hostValue == null) continue; if (hostValue.equals(candidate.getContactAddressCustomParamValue())) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "\" the custom param was set"); return candidate; } } // Past this point, our guess is not reliable. We try to find // the "least worst" match based on parameters like the To field // check if the To header field host part // matches any of our SIP hosts for (ProtocolProviderServiceSipImpl candidate : candidates) { URI fromURI = ((FromHeader) request.getHeader(FromHeader.NAME)).getAddress().getURI(); if (fromURI.isSipURI() == false) continue; SipURI ourURI = (SipURI) candidate.getOurSipAddress((SipURI) fromURI).getURI(); String ourHost = ourURI.getHost(); URI toURI = ((ToHeader) request.getHeader(ToHeader.NAME)).getAddress().getURI(); if (toURI.isSipURI() == false) continue; String toHost = ((SipURI) toURI).getHost(); // logger.trace(toHost + "***" + ourHost); if (toHost.equals(ourHost)) { if (logger.isTraceEnabled()) logger.trace( "Will dispatch to \"" + candidate.getAccountID() + "\" because " + "host in the To: is the same as in our AOR"); return candidate; } } // fallback on the first candidate ProtocolProviderServiceSipImpl target = candidates.iterator().next(); logger.info( "Will randomly dispatch to \"" + target.getAccountID() + "\" because there is ambiguity on the username from" + " the Request-URI"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } // fallback on any account ProtocolProviderServiceSipImpl target = currentListenersCopy.iterator().next(); if (logger.isDebugEnabled()) logger.debug( "Will randomly dispatch to \"" + target.getAccountID() + "\" because the username in the Request-URI " + "is unknown or empty"); if (logger.isTraceEnabled()) logger.trace("\n" + request); return target; } else { logger.error("Request-URI is not a SIP URI, dropping"); } return null; }
/** * Attach JAIN-SIP <tt>SipProvider</tt> and <tt>ListeningPoint</tt> to the stack either for clear * communications or TLS. Clear UDP and TCP <tt>ListeningPoint</tt>s are not handled separately as * the former is a fallback for the latter (depending on the size of the data transmitted). Both * <tt>ListeningPoint</tt>s must be bound to the same address and port in order for the related * <tt>SipProvider</tt> to be created. If a UDP or TCP <tt>ListeningPoint</tt> cannot bind, retry * for both on another port. * * @param preferredPort which port to try first to bind. * @param retries how many times should we try to find a free port to bind * @param secure whether to create the TLS SipProvider. or the clear UDP/TCP one. * @throws TransportNotSupportedException in case we try to create a provider for a transport not * currently supported by jain-sip * @throws InvalidArgumentException if we try binding to an illegal port (which we won't) * @throws ObjectInUseException if another <tt>SipProvider</tt> is already associated with this * <tt>ListeningPoint</tt>. * @throws TransportAlreadySupportedException if there is already a ListeningPoint associated to * this <tt>SipProvider</tt> with the same transport of the <tt>ListeningPoint</tt>. * @throws TooManyListenersException if we try to add a new <tt>SipListener</tt> with a * <tt>SipProvider</tt> when one was already registered. */ private void createProvider(int preferredPort, int retries, boolean secure) throws TransportNotSupportedException, InvalidArgumentException, ObjectInUseException, TransportAlreadySupportedException, TooManyListenersException { String context = (secure ? "TLS: " : "clear UDP/TCP: "); if (retries < 0) { // very unlikely to happen with the default 50 retries logger.error(context + "couldn't find free ports to listen on."); return; } ListeningPoint tlsLP = null; ListeningPoint udpLP = null; ListeningPoint tcpLP = null; try { if (secure) { tlsLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.TLS); if (logger.isTraceEnabled()) logger.trace("TLS secure ListeningPoint has been created."); this.secureJainSipProvider = this.stack.createSipProvider(tlsLP); this.secureJainSipProvider.addSipListener(this); } else { udpLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.UDP); tcpLP = this.stack.createListeningPoint( NetworkUtils.IN_ADDR_ANY, preferredPort, ListeningPoint.TCP); if (logger.isTraceEnabled()) logger.trace("UDP and TCP clear ListeningPoints have " + "been created."); this.clearJainSipProvider = this.stack.createSipProvider(udpLP); this.clearJainSipProvider.addListeningPoint(tcpLP); this.clearJainSipProvider.addSipListener(this); } if (logger.isTraceEnabled()) logger.trace(context + "SipProvider has been created."); } catch (InvalidArgumentException ex) { // makes sure we didn't leave an open listener // as both UDP and TCP listener have to bind to the same port if (tlsLP != null) this.stack.deleteListeningPoint(tlsLP); if (udpLP != null) this.stack.deleteListeningPoint(udpLP); if (tcpLP != null) this.stack.deleteListeningPoint(tcpLP); // FIXME: "Address already in use" is not working // as ex.getMessage() displays in the locale language in SC // (getMessage() is always supposed to be English though) // this should be a temporary workaround // if (ex.getMessage().indexOf("Address already in use") != -1) // another software is probably using the port if (ex.getCause() instanceof java.io.IOException) { if (logger.isDebugEnabled()) logger.debug("Port " + preferredPort + " seems in use for either TCP or UDP."); // tries again on a new random port int currentlyTriedPort = NetworkUtils.getRandomPortNumber(); if (logger.isDebugEnabled()) logger.debug("Retrying bind on port " + currentlyTriedPort); this.createProvider(currentlyTriedPort, retries - 1, secure); } else throw ex; } }