001/**
002 *
003 * Copyright 2003-2005 Jive Software.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.jingleold.packet;
018
019import org.jivesoftware.smack.packet.ExtensionElement;
020import org.jivesoftware.smackx.jingleold.nat.ICECandidate;
021import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
022
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.Iterator;
026import java.util.List;
027
028/**
029 * A jingle transport extension.
030 *
031 * @author Alvaro Saurin <alvaro.saurin@gmail.com>
032 */
033public class JingleTransport implements ExtensionElement {
034
035    // static
036
037    public static final String NODENAME = "transport";
038
039    // non-static
040
041    protected String namespace;
042
043    protected final List<JingleTransportCandidate> candidates = new ArrayList<JingleTransportCandidate>();
044
045    /**
046     * Default constructor.
047     */
048    public JingleTransport() {
049        super();
050    }
051
052    /**
053     * Utility constructor, with a transport candidate element.
054     *
055     * @param candidate A transport candidate element to add.
056     */
057    public JingleTransport(final JingleTransportCandidate candidate) {
058        super();
059        addCandidate(candidate);
060    }
061
062    /**
063     * Copy constructor.
064     *
065     * @param tr the other jingle transport.
066     */
067    public JingleTransport(final JingleTransport tr) {
068        if (tr != null) {
069            namespace = tr.namespace;
070
071            if (tr.candidates.size() > 0) {
072                candidates.addAll(tr.candidates);
073            }
074        }
075    }
076
077    /**
078     * Adds a transport candidate.
079     *
080     * @param candidate the candidate
081     */
082    public void addCandidate(final JingleTransportCandidate candidate) {
083        if (candidate != null) {
084            synchronized (candidates) {
085                candidates.add(candidate);
086            }
087        }
088    }
089
090    /**
091     * Get an iterator for the candidates.
092     *
093     * @return an iterator
094     */
095    public Iterator<JingleTransportCandidate> getCandidates() {
096        return Collections.unmodifiableList(getCandidatesList()).iterator();
097    }
098
099    /**
100     * Get the list of candidates.
101     *
102     * @return The candidates list.
103     */
104    public List<JingleTransportCandidate> getCandidatesList() {
105        ArrayList<JingleTransportCandidate> res = null;
106        synchronized (candidates) {
107            res = new ArrayList<JingleTransportCandidate>(candidates);
108        }
109        return res;
110    }
111
112    /**
113     * Get the number of transport candidates.
114     *
115     * @return The number of transport candidates contained.
116     */
117    public int getCandidatesCount() {
118        return getCandidatesList().size();
119    }
120
121    /**
122     * Returns the XML element name of the element.
123     *
124     * @return the XML element name of the element.
125     */
126    @Override
127    public String getElementName() {
128        return NODENAME;
129    }
130
131    /**
132     * Set the namespace.
133     *
134     * @param ns The namespace
135     */
136    protected void setNamespace(final String ns) {
137        namespace = ns;
138    }
139
140    /**
141     * Get the namespace.
142     *
143     * @return The namespace
144     */
145    @Override
146    public String getNamespace() {
147        return namespace;
148    }
149
150    /**
151     * Return the XML representation for this element.
152     */
153    @Override
154    public String toXML() {
155        StringBuilder buf = new StringBuilder();
156
157        buf.append('<').append(getElementName()).append(" xmlns=\"");
158        buf.append(getNamespace()).append("\" ");
159
160        synchronized (candidates) {
161            if (getCandidatesCount() > 0) {
162                buf.append('>');
163                Iterator<JingleTransportCandidate> iter = getCandidates();
164
165                while (iter.hasNext()) {
166                    JingleTransportCandidate candidate = iter.next();
167                    buf.append(candidate.toXML());
168                }
169                buf.append("</").append(getElementName()).append('>');
170            } else {
171                buf.append("/>");
172            }
173        }
174
175        return buf.toString();
176    }
177
178    /**
179     * Candidate element in the transport. This class acts as a view of the
180     * "TransportCandidate" in the Jingle space.
181     *
182     * @author Alvaro Saurin
183     * @see TransportCandidate
184     */
185    public static abstract class JingleTransportCandidate {
186
187        public static final String NODENAME = "candidate";
188
189        // The transport candidate contained in the element.
190        protected TransportCandidate transportCandidate;
191
192        /**
193         * Creates a new TransportNegotiator child.
194         */
195        public JingleTransportCandidate() {
196            super();
197        }
198
199        /**
200         * Creates a new TransportNegotiator child.
201         *
202         * @param candidate the jmf transport candidate
203         */
204        public JingleTransportCandidate(final TransportCandidate candidate) {
205            super();
206            setMediaTransport(candidate);
207        }
208
209        /**
210         * Returns the XML element name of the element.
211         *
212         * @return the XML element name of the element.
213         */
214        public static String getElementName() {
215            return NODENAME;
216        }
217
218        /**
219         * Get the current transportElement candidate.
220         *
221         * @return the transportElement candidate
222         */
223        public TransportCandidate getMediaTransport() {
224            return transportCandidate;
225        }
226
227        /**
228         * Set the transportElement candidate.
229         *
230         * @param cand the transportElement candidate
231         */
232        public void setMediaTransport(final TransportCandidate cand) {
233            if (cand != null) {
234                transportCandidate = cand;
235            }
236        }
237
238        /**
239         * Get the list of attributes.
240         *
241         * @return a string with the list of attributes.
242         */
243        protected String getChildElements() {
244            return null;
245        }
246
247        /**
248         * Obtain a valid XML representation of a trancport candidate.
249         *
250         * @return A string containing the XML dump of the transport candidate.
251         */
252        public String toXML() {
253            StringBuilder buf = new StringBuilder();
254            String childElements = getChildElements();
255
256            if (transportCandidate != null && childElements != null) {
257                buf.append('<').append(getElementName()).append(' ');
258                buf.append(childElements);
259                buf.append("/>");
260            }
261
262            return buf.toString();
263        }
264    }
265
266    // Subclasses
267
268    /**
269     * RTP-ICE profile.
270     */
271    public static class Ice extends JingleTransport {
272        public static final String NAMESPACE = "urn:xmpp:tmp:jingle:transports:ice-udp";
273
274        public Ice() {
275            super();
276            setNamespace(NAMESPACE);
277        }
278
279        /**
280         * Add a transport candidate.
281         *
282         * @see org.jivesoftware.smackx.jingleold.packet.JingleTransport#addCandidate(org.jivesoftware.smackx.jingleold.packet.JingleTransport.JingleTransportCandidate)
283         */
284        @Override
285        public void addCandidate(final JingleTransportCandidate candidate) {
286            super.addCandidate(candidate);
287        }
288
289        /**
290         * Get the list of candidates. As a "raw-udp" transport can only contain
291         * one candidate, we use the first in the list...
292         *
293         * @see org.jivesoftware.smackx.jingleold.packet.JingleTransport#getCandidates()
294         */
295        @Override
296        public List<JingleTransportCandidate> getCandidatesList() {
297            List<JingleTransportCandidate> copy = new ArrayList<JingleTransportCandidate>();
298            List<JingleTransportCandidate> superCandidatesList = super.getCandidatesList();
299            for (int i = 0; i < superCandidatesList.size(); i++) {
300                copy.add(superCandidatesList.get(i));
301            }
302
303            return copy;
304        }
305
306        public static class Candidate extends JingleTransportCandidate {
307            /**
308             * Default constructor.
309             */
310            public Candidate() {
311                super();
312            }
313
314            /**
315             * Constructor with a transport candidate.
316             */
317            public Candidate(final TransportCandidate tc) {
318                super(tc);
319            }
320
321            /**
322             * Get the elements of this candidate.
323             */
324            @Override
325            protected String getChildElements() {
326                StringBuilder buf = new StringBuilder();
327
328                if (transportCandidate != null) {// && transportCandidate instanceof ICECandidate) {
329                    ICECandidate tci = (ICECandidate) transportCandidate;
330
331                    // We convert the transportElement candidate to XML here...
332                    buf.append(" generation=\"").append(tci.getGeneration()).append('"');
333                    buf.append(" ip=\"").append(tci.getIp()).append('"');
334                    buf.append(" port=\"").append(tci.getPort()).append('"');
335                    buf.append(" network=\"").append(tci.getNetwork()).append('"');
336                    buf.append(" username=\"").append(tci.getUsername()).append('"');
337                    buf.append(" password=\"").append(tci.getPassword()).append('"');
338                    buf.append(" preference=\"").append(tci.getPreference()).append('"');
339                    buf.append(" type=\"").append(tci.getType()).append('"');
340
341                    // Optional elements
342                    if (transportCandidate.getName() != null) {
343                        buf.append(" name=\"").append(tci.getName()).append('"');
344                    }
345                }
346
347                return buf.toString();
348            }
349
350        }
351    }
352
353    /**
354     * Raw UDP profile.
355     */
356    public static class RawUdp extends JingleTransport {
357        public static final String NAMESPACE = "http://www.xmpp.org/extensions/xep-0177.html#ns";
358
359        public RawUdp() {
360            super();
361            setNamespace(NAMESPACE);
362        }
363
364        /**
365         * Add a transport candidate.
366         *
367         * @see org.jivesoftware.smackx.jingleold.packet.JingleTransport#addCandidate(org.jivesoftware.smackx.jingleold.packet.JingleTransport.JingleTransportCandidate)
368         */
369        @Override
370        public void addCandidate(final JingleTransportCandidate candidate) {
371            candidates.clear();
372            super.addCandidate(candidate);
373        }
374
375        /**
376         * Get the list of candidates. As a "raw-udp" transport can only contain
377         * one candidate, we use the first in the list...
378         *
379         * @see org.jivesoftware.smackx.jingleold.packet.JingleTransport#getCandidates()
380         */
381        @Override
382        public List<JingleTransportCandidate> getCandidatesList() {
383            List<JingleTransportCandidate> copy = new ArrayList<JingleTransportCandidate>();
384            List<JingleTransportCandidate> superCandidatesList = super.getCandidatesList();
385            if (superCandidatesList.size() > 0) {
386                copy.add(superCandidatesList.get(0));
387            }
388
389            return copy;
390        }
391
392        /**
393         * Raw-udp transport candidate.
394         */
395        public static class Candidate extends JingleTransportCandidate {
396            /**
397             * Default constructor.
398             */
399            public Candidate() {
400                super();
401            }
402
403            /**
404             * Constructor with a transport candidate.
405             */
406            public Candidate(final TransportCandidate tc) {
407                super(tc);
408            }
409
410            /**
411             * Get the elements of this candidate.
412             */
413            @Override
414            protected String getChildElements() {
415                StringBuilder buf = new StringBuilder();
416
417                if (transportCandidate != null && transportCandidate instanceof TransportCandidate.Fixed) {
418                    TransportCandidate.Fixed tcf = (TransportCandidate.Fixed) transportCandidate;
419
420                    buf.append(" generation=\"").append(tcf.getGeneration()).append('"');
421                    buf.append(" ip=\"").append(tcf.getIp()).append('"');
422                    buf.append(" port=\"").append(tcf.getPort()).append('"');
423
424                    // Optional parameters
425                    String name = tcf.getName();
426                    if (name != null) {
427                        buf.append(" name=\"").append(name).append('"');
428                    }
429                }
430                return buf.toString();
431            }
432
433        }
434    }
435}