DefaultFileTransferManager.java 9.22 KB
Newer Older
1
/*
2
 * Copyright (C) 1999-2008 Jive Software. All rights reserved.
3
 *
4 5 6 7 8 9 10 11 12 13 14
 * 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.
15
 */
16
package org.jivesoftware.openfire.filetransfer;
17

18
import org.dom4j.Element;
19 20 21 22 23 24 25 26
import org.jivesoftware.openfire.auth.UnauthorizedException;
import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.filetransfer.proxy.ProxyConnectionManager;
import org.jivesoftware.openfire.filetransfer.proxy.ProxyTransfer;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketInterceptor;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.Session;
27 28 29
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
30 31
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
32 33
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
34
import org.xmpp.packet.Packet;
35

36
import java.util.ArrayList;
37
import java.util.List;
38 39 40 41 42 43

/**
 * Provides several utility methods for file transfer manager implementaions to utilize.
 *
 * @author Alexander Wenckus
 */
44
public class DefaultFileTransferManager extends BasicModule implements FileTransferManager {
45

46 47
    private static final Logger Log = LoggerFactory.getLogger( DefaultFileTransferManager.class );

48 49
    private static final String CACHE_NAME = "File Transfer Cache";

50
    private final Cache<String, FileTransfer> fileTransferMap;
51

52
    private final List<FileTransferEventListener> eventListeners = new ArrayList<>();
53

54 55 56
    /**
     * Default constructor creates the cache.
     */
57
    public DefaultFileTransferManager() {
58
        super("File Transfer Manager");
59
        fileTransferMap = CacheFactory.createCache(CACHE_NAME);
60
        InterceptorManager.getInstance().addInterceptor(new MetaFileTransferInterceptor());
61 62
    }

63
    /**
64 65
     * Returns true if the proxy transfer should be matched to an existing file transfer
     * in the system.
66
     *
67 68
     * @return Returns true if the proxy transfer should be matched to an existing file
     * transfer in the system.
69 70
     */
    public boolean isMatchProxyTransfer() {
71
        return JiveGlobals.getBooleanProperty("xmpp.proxy.transfer.required", true);
72 73
    }

74
    protected void cacheFileTransfer(String key, FileTransfer transfer) {
75 76 77
        fileTransferMap.put(key, transfer);
    }

78
    protected FileTransfer retrieveFileTransfer(String key) {
79 80 81
        return fileTransferMap.get(key);
    }

82
    protected static Element getChildElement(Element element, String namespace) {
83 84
        //noinspection unchecked
        List<Element> elements = element.elements();
85 86 87
        if (elements.isEmpty()) {
            return null;
        }
88
        for (Element childElement : elements) {
89 90 91 92 93 94 95 96 97
            String childNamespace = childElement.getNamespaceURI();
            if (namespace.equals(childNamespace)) {
                return childElement;
            }
        }

        return null;
    }

98
    @Override
99
    public boolean acceptIncomingFileTransferRequest(FileTransfer transfer)
100
            throws FileTransferRejectedException
101 102
    {
        if(transfer != null) {
103
            fireFileTransferStart( transfer.getSessionID(), false );
104 105 106 107 108 109 110 111 112
            String streamID = transfer.getSessionID();
            JID from = new JID(transfer.getInitiator());
            JID to = new JID(transfer.getTarget());
            cacheFileTransfer(ProxyConnectionManager.createDigest(streamID, from, to), transfer);
            return true;
        }
        return false;
    }

113
    @Override
114 115 116 117 118 119 120 121 122 123 124 125 126 127
    public void registerProxyTransfer(String transferDigest, ProxyTransfer proxyTransfer)
            throws UnauthorizedException
    {
        FileTransfer transfer = retrieveFileTransfer(transferDigest);
        if (isMatchProxyTransfer() && transfer == null) {
            throw new UnauthorizedException("Unable to match proxy transfer with a file transfer");
        }
        else if (transfer == null) {
            return;
        }

        transfer.setProgress(proxyTransfer);
        cacheFileTransfer(transferDigest, transfer);
    }
128

129 130
    private FileTransfer createFileTransfer(JID from,
                                            JID to, Element siElement) {
131 132 133
        String streamID = siElement.attributeValue("id");
        String mimeType = siElement.attributeValue("mime-type");
        // Check profile, the only type we deal with currently is file transfer
134 135 136 137 138 139 140 141 142 143
        Element fileTransferElement = getChildElement(siElement, NAMESPACE_SI_FILETRANSFER);
        // Not valid form, reject
        if (fileTransferElement == null) {
            return null;
        }
        String fileName = fileTransferElement.attributeValue("name");
        String sizeString = fileTransferElement.attributeValue("size");
        if (fileName == null || sizeString == null) {
            return null;
        }
144

145 146 147
        long size;
        try {
            size = Long.parseLong(sizeString);
148
        }
149 150 151 152 153
        catch (Exception ex) {
            return null;
        }

        return new FileTransfer(from.toString(), to.toString(), streamID, fileName, size, mimeType);
154 155
    }

156 157 158 159
    @Override
    public void addListener( FileTransferEventListener eventListener )
    {
        eventListeners.add( eventListener );
160 161
    }

162 163 164 165
    @Override
    public void removeListener( FileTransferEventListener eventListener )
    {
        eventListeners.remove( eventListener );
166 167
    }

168 169
    @Override
    public void fireFileTransferStart( String sid, boolean isReady ) throws FileTransferRejectedException
170
    {
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
        final FileTransfer transfer = fileTransferMap.get( sid );
        for ( FileTransferEventListener listener : eventListeners )
        {
            try
            {
                listener.fileTransferStart( transfer, isReady );
            }
            catch ( FileTransferRejectedException ex )
            {
                Log.debug( "Listener '{}' rejected file transfer '{}'.", listener, transfer );
                throw ex;
            }
            catch ( Exception ex )
            {
                Log.warn( "Listener '{}' threw exception when being informed of file transfer complete for transfer '{}'.", listener, transfer, ex );
            }
        }
188 189
    }

190 191
    @Override
    public void fireFileTransferCompleted( String sid, boolean wasSuccessful )
192
    {
193 194 195 196 197 198 199 200 201 202 203
        final FileTransfer transfer = fileTransferMap.get( sid );
        for ( FileTransferEventListener listener : eventListeners )
        {
            try
            {
                listener.fileTransferComplete( transfer, wasSuccessful );
            }
            catch ( Exception ex )
            {
                Log.warn( "Listener '{}' threw exception when being informed of file transfer complete for transfer '{}'.", listener, transfer, ex );
            }
204
        }
205 206
    }

207 208 209
    /**
     * Interceptor to grab and validate file transfer meta information.
     */
210
    private class MetaFileTransferInterceptor implements PacketInterceptor {
211
        @Override
212 213 214 215
        public void interceptPacket(Packet packet, Session session, boolean incoming,
                                    boolean processed)
                throws PacketRejectedException
        {
216
            // We only want packets received by the server
217 218 219 220 221 222 223 224
            if (!processed && incoming && packet instanceof IQ) {
                IQ iq = (IQ) packet;
                Element childElement = iq.getChildElement();
                if(childElement == null) {
                    return;
                }

                String namespace = childElement.getNamespaceURI();
225 226 227
                String profile = childElement.attributeValue("profile");
                // Check that the SI is about file transfer and try creating a file transfer
                if (NAMESPACE_SI.equals(namespace) && NAMESPACE_SI_FILETRANSFER.equals(profile)) {
228 229 230 231
                    // If this is a set, check the feature offer
                    if (iq.getType().equals(IQ.Type.set)) {
                        JID from = iq.getFrom();
                        JID to = iq.getTo();
232

233
                        FileTransfer transfer = createFileTransfer(from, to, childElement);
234

235 236 237 238 239 240 241
                        try {
                            if (transfer == null || !acceptIncomingFileTransferRequest(transfer)) {
                                throw new PacketRejectedException();
                            }
                        }
                        catch (FileTransferRejectedException e) {
                            throw new PacketRejectedException(e);
242 243 244 245 246 247
                        }
                    }
                }
            }
        }
    }
248
}