aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/protocol/jabber/OutgoingFileTransferJabberImpl.java
blob: b219d683f1100bc666731a0e4481af1313bf4df3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/*
 * 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 java.io.*;

import net.java.sip.communicator.impl.protocol.jabber.extensions.thumbnail.*;
import net.java.sip.communicator.service.protocol.*;
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.StringUtils;
import org.jivesoftware.smackx.filetransfer.*;
import org.jivesoftware.smackx.packet.*;

/**
 * The Jabber protocol extension of the <tt>AbstractFileTransfer</tt>.
 *
 * @author Yana Stamcheva
 */
public class OutgoingFileTransferJabberImpl
    extends AbstractFileTransfer
    implements PacketInterceptor
{
    /**
     * The logger of this class.
     */
    private final Logger logger
        = Logger.getLogger(OutgoingFileTransferJabberImpl.class);

    private final String id;

    private final Contact receiver;

    private final File file;

    private ThumbnailElement thumbnailElement;

    private final ThumbnailRequestListener thumbnailRequestListener
        = new ThumbnailRequestListener();

    /**
     * The jabber outgoing file transfer.
     */
    private final OutgoingFileTransfer jabberTransfer;

    private final ProtocolProviderServiceJabberImpl protocolProvider;

    /**
     * Creates an <tt>OutgoingFileTransferJabberImpl</tt> by specifying the
     * <tt>receiver</tt> contact, the <tt>file</tt>, the <tt>jabberTransfer</tt>,
     * that would be used to send the file through Jabber and the
     * <tt>protocolProvider</tt>.
     *
     * @param receiver the destination contact
     * @param file the file to send
     * @param jabberTransfer the Jabber transfer object, containing all transfer
     * information
     * @param protocolProvider the parent protocol provider
     */
    public OutgoingFileTransferJabberImpl(
        Contact receiver,
        File file,
        OutgoingFileTransfer jabberTransfer,
        ProtocolProviderServiceJabberImpl protocolProvider)
    {
        this.receiver = receiver;
        this.file = file;
        this.jabberTransfer = jabberTransfer;
        this.protocolProvider = protocolProvider;

        // Create the identifier of this file transfer that is used from the
        // history and the user interface to track this transfer.
        this.id = String.valueOf(System.currentTimeMillis())
            + String.valueOf(hashCode());

        // Add this outgoing transfer as a packet interceptor in
        // order to manage thumbnails.
        if (file instanceof ThumbnailedFile
             && ((ThumbnailedFile) file).getThumbnailData() != null
             && ((ThumbnailedFile) file).getThumbnailData().length > 0)
        {
            if (protocolProvider.isFeatureListSupported(
                            protocolProvider.getFullJid(receiver),
                            new String[]{"urn:xmpp:thumbs:0",
                                "urn:xmpp:bob"}))
            {
                protocolProvider.getConnection().addPacketInterceptor(
                    this,
                    new IQTypeFilter(IQ.Type.SET));
            }
        }
    }

    /**
     * Cancels the file transfer.
     */
    @Override
    public void cancel()
    {
        this.jabberTransfer.cancel();
    }

    /**
     * Returns the number of bytes already sent to the recipient.
     *
     * @return the number of bytes already sent to the recipient.
     */
    @Override
    public long getTransferedBytes()
    {
        return jabberTransfer.getBytesSent();
    }

    /**
     * The direction is outgoing.
     * @return OUT.
     */
    public int getDirection()
    {
        return OUT;
    }

    /**
     * Returns the local file that is being transferred or to which we transfer.
     *
     * @return the file
     */
    public File getLocalFile()
    {
        return file;
    }

    /**
     * The contact we are sending the file.
     * @return the receiver.
     */
    public Contact getContact()
    {
        return receiver;
    }

    /**
     * The unique id.
     * @return the id.
     */
    public String getID()
    {
        return id;
    }

    /**
     * Removes previously added thumbnail request listener.
     */
    public void removeThumbnailRequestListener()
    {
        protocolProvider.getConnection()
            .removePacketListener(thumbnailRequestListener);
    }

    /**
     * Listens for all <tt>StreamInitiation</tt> packets and adds a thumbnail
     * to them if a thumbnailed file is supported.
     *
     * @see PacketInterceptor#interceptPacket(Packet)
     */
    public void interceptPacket(Packet packet)
    {
        if (!(packet instanceof StreamInitiation))
            return;

        // If our file is not a thumbnailed file we have nothing to do here.
        if (!(file instanceof ThumbnailedFile))
            return;

        if (logger.isDebugEnabled())
            logger.debug("File transfer packet intercepted"
                    + " in order to add thumbnail.");

        StreamInitiation fileTransferPacket = (StreamInitiation) packet;

        ThumbnailedFile thumbnailedFile = (ThumbnailedFile) file;

        if (jabberTransfer.getStreamID()
                .equals(fileTransferPacket.getSessionID()))
        {
            StreamInitiation.File file = fileTransferPacket.getFile();

            thumbnailElement = new ThumbnailElement(
                StringUtils.parseServer(fileTransferPacket.getTo()),
                thumbnailedFile.getThumbnailData(),
                thumbnailedFile.getThumbnailMimeType(),
                thumbnailedFile.getThumbnailWidth(),
                thumbnailedFile.getThumbnailHeight());

            FileElement fileElement = new FileElement(file, thumbnailElement);

            fileTransferPacket.setFile(fileElement);

            if (logger.isDebugEnabled())
                logger.debug("The file transfer packet with thumbnail: "
                + fileTransferPacket.toXML());

            // Add the request listener in order to listen for requests coming
            // for the advertised thumbnail.
            if (protocolProvider.getConnection() != null)
            {
                protocolProvider.getConnection().addPacketListener(
                    thumbnailRequestListener,
                    new AndFilter(  new PacketTypeFilter(IQ.class),
                                    new IQTypeFilter(IQ.Type.GET)));
            }
        }
        // Remove this packet interceptor after we're done.
        protocolProvider.getConnection().removePacketInterceptor(this);
    }

    /**
     * The <tt>ThumbnailRequestListener</tt> listens for events triggered by
     * the reception of a <tt>ThumbnailIQ</tt> packet. The packet is examined
     * and a <tt>ThumbnailIQ</tt> is created to respond to the thumbnail
     * request received.
     */
    private class ThumbnailRequestListener implements PacketListener
    {
        public void processPacket(Packet packet)
        {
            // If this is not an IQ packet, we're not interested.
            if (!(packet instanceof ThumbnailIQ))
                return;

            ThumbnailIQ thumbnailIQ = (ThumbnailIQ) packet;
            String thumbnailIQCid = thumbnailIQ.getCid();
            Connection connection = protocolProvider.getConnection();

            if ((thumbnailIQCid != null)
                    && thumbnailIQCid.equals(thumbnailElement.getCid()))
            {
                ThumbnailedFile thumbnailedFile = (ThumbnailedFile) file;
                ThumbnailIQ thumbnailResponse = new ThumbnailIQ(
                    thumbnailIQ.getTo(),
                    thumbnailIQ.getFrom(),
                    thumbnailIQCid,
                    thumbnailedFile.getThumbnailMimeType(),
                    thumbnailedFile.getThumbnailData(),
                    IQ.Type.RESULT);

                if (logger.isDebugEnabled())
                    logger.debug("Send thumbnail response to the receiver: "
                        + thumbnailResponse.toXML());

                connection.sendPacket(thumbnailResponse);
            }
            else
            {
                // RETURN <item-not-found/>
            }

            if (connection != null)
                connection.removePacketListener(this);
        }
    }
}