summaryrefslogtreecommitdiffstats
blob: 7b6e5d7728ea62ea760e66d6d8e429628c07d8dd (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
/*
 * Copyright (c) 2010-2015, Isode Limited, London, England.
 * All rights reserved.
 */
package com.isode.stroke.queries;

import java.util.Vector;

import com.isode.stroke.elements.ErrorPayload;
import com.isode.stroke.elements.IQ;
import com.isode.stroke.signals.Slot1;
import com.isode.stroke.jid.JID;
import java.util.Collections;

/**
 * This class is responsible for routing all IQ stanzas to the handlers. It's
 * needed because, unlike Message and Presence, there are strict rules about
 * replying to IQ stanzas, replies need to be tracked, handled by the
 * responsible bit of code, replied to, etc. and when it's an outgoing IQ, it
 * needs to be tracked such that when the reply comes in, the callback is
 * called.
 */
public class IQRouter {

    private Vector<IQHandler> handlers_ = new Vector<IQHandler>();
    private IQChannel channel_;
    private JID jid_ = new JID();
    private JID from_ = new JID();
    private Vector<IQHandler> queuedRemoves_ = new Vector<IQHandler>();
    private boolean queueRemoves_;

    public IQRouter(IQChannel channel) {
        channel_ = channel;
        queueRemoves_ = false;
        channel_.onIQReceived.connect(new Slot1<IQ>() {

            public void call(IQ p1) {
                handleIQ(p1);
            }
        });
    }

    public void addHandler(IQHandler handler) {
        synchronized (handlers_) {
            handlers_.add(handler);
        }
    }

    public void removeHandler(IQHandler handler) {
        if (queueRemoves_) {
            queuedRemoves_.add(handler);
        }
        else {
            synchronized (handlers_) {
                handlers_.remove(handler);
            }
        }
    }

    public void sendIQ(IQ iq) {
        if (from_.isValid() && !iq.getFrom().isValid()) {
            iq.setFrom(from_);
        }
        channel_.sendIQ(iq);
    }

    public String getNewIQID() {
        return channel_.getNewIQID();
    }

    public boolean isAvailable() {
        return channel_.isAvailable();
    }

	/**
	 * Checks whether the given jid is the account JID (i.e. it is either
	 * the bare JID, or it is the empty JID).
	 * Can be used to check whether a stanza is sent by the server on behalf
	 * of the user's account.
	 */
    public boolean isAccountJID(final JID jid) {
		return jid.isValid() ? jid_.toBare().compare(jid, JID.CompareType.WithResource) == 0 : true;
	}

	private void handleIQ(IQ iq) {
        queueRemoves_ = true;
        boolean handled = false;
        Vector<IQHandler> i = new Vector<IQHandler>();
        i.addAll(handlers_);
        Collections.reverse(i);
        synchronized (handlers_) {
            for (IQHandler handler : i) {
                handled |= handler.handleIQ(iq);
                if (handled) {
                    break;
                }
            }
        }
        if (!handled && (iq.getType().equals(IQ.Type.Get) || iq.getType().equals(IQ.Type.Set))) {
            sendIQ(IQ.createError(iq.getFrom(), iq.getID(), ErrorPayload.Condition.FeatureNotImplemented, ErrorPayload.Type.Cancel));
        }

        processPendingRemoves();
        queueRemoves_ = false;
    }

    public void processPendingRemoves() {
        synchronized(handlers_) {
            for(IQHandler handler : queuedRemoves_) {
                handlers_.remove(handler);
            }
        }
        queuedRemoves_.clear();
    }

    /**
     * Sets the JID of this IQ router.
     *
     * This JID is used by requests to check whether incoming results
     * are addressed correctly
     * @param jid the JID
     */
    public void setJID(final JID jid) {
	jid_ = jid;
    }

    public JID getJID() {
	return jid_;
    }

    /**
     * Sets the 'from' JID for all outgoing IQ stanzas.
     *
     * By default, IQRouter does not add a from to IQ stanzas, since
     * this is automatically added by the server. This overrides this
     * default behavior, which is necessary for e.g. components.
     */
    public void setFrom(final JID from) {
        from_ = from;
    }
}