]> sjero.net Git - linphone/blob - p2pproxy/dependencies-src/jxse-src-2.5/impl/src/net/jxta/impl/endpoint/msgframing/WelcomeMessage.java
remove mediastreamer2 and add it as a submodule instead.
[linphone] / p2pproxy / dependencies-src / jxse-src-2.5 / impl / src / net / jxta / impl / endpoint / msgframing / WelcomeMessage.java
1 /*
2  * Copyright (c) 2001-2007 Sun Microsystems, Inc.  All rights reserved.
3  *  
4  *  The Sun Project JXTA(TM) Software License
5  *  
6  *  Redistribution and use in source and binary forms, with or without 
7  *  modification, are permitted provided that the following conditions are met:
8  *  
9  *  1. Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *  
12  *  2. Redistributions in binary form must reproduce the above copyright notice, 
13  *     this list of conditions and the following disclaimer in the documentation 
14  *     and/or other materials provided with the distribution.
15  *  
16  *  3. The end-user documentation included with the redistribution, if any, must 
17  *     include the following acknowledgment: "This product includes software 
18  *     developed by Sun Microsystems, Inc. for JXTA(TM) technology." 
19  *     Alternately, this acknowledgment may appear in the software itself, if 
20  *     and wherever such third-party acknowledgments normally appear.
21  *  
22  *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must 
23  *     not be used to endorse or promote products derived from this software 
24  *     without prior written permission. For written permission, please contact 
25  *     Project JXTA at http://www.jxta.org.
26  *  
27  *  5. Products derived from this software may not be called "JXTA", nor may 
28  *     "JXTA" appear in their name, without prior written permission of Sun.
29  *  
30  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31  *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
32  *  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN 
33  *  MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
34  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
35  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
36  *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
37  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
38  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
39  *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40  *  
41  *  JXTA is a registered trademark of Sun Microsystems, Inc. in the United 
42  *  States and other countries.
43  *  
44  *  Please see the license information page at :
45  *  <http://www.jxta.org/project/www/license.html> for instructions on use of 
46  *  the license in source files.
47  *  
48  *  ====================================================================
49  *  
50  *  This software consists of voluntary contributions made by many individuals 
51  *  on behalf of Project JXTA. For more information on Project JXTA, please see 
52  *  http://www.jxta.org.
53  *  
54  *  This license is based on the BSD license adopted by the Apache Foundation. 
55  */
56
57 package net.jxta.impl.endpoint.msgframing;
58
59
60 import java.io.EOFException;
61 import java.io.InputStream;
62 import java.io.IOException;
63 import java.io.OutputStream;
64 import java.io.UnsupportedEncodingException;
65 import java.net.URI;
66 import java.net.URISyntaxException;
67 import java.nio.ByteBuffer;
68 import java.nio.BufferUnderflowException;
69 import java.util.Arrays;
70 import java.util.List;
71 import java.text.MessageFormat;
72
73 import net.jxta.endpoint.EndpointAddress;
74 import net.jxta.id.ID;
75 import net.jxta.id.IDFactory;
76
77 import java.util.logging.Level;
78 import net.jxta.logging.Logging;
79 import java.util.logging.Logger;
80
81
82 /**
83  * Contains a JXTA connection Welcome Message. The Welcome Message is sent by
84  * both participant peers as the first interchange on newly opened connections.
85  * <p/>
86  * <p/>The Welcome Message contains the following information:
87  * <ul>
88  * <li>The address to which the local peer believes it is connected.</li>
89  * <li>The local peer's return address, the source address.</li>
90  * <li>The local peer's peer id.</li>
91  * <li>A flag which controls propagation behaviour for this conneciton.</li>
92  * </ul>
93  *
94  * @see <a href="https://jxta-spec.dev.java.net/nonav/JXTAProtocols.html#trans-tcpipt"
95  *      target="_blank">JXTA Protocols Specification : TCP/IP Message Transport</a>
96  */
97 public class WelcomeMessage {
98
99     /**
100      * Log4J Logger
101      */
102     private static final Logger LOG = Logger.getLogger(WelcomeMessage.class.getName());
103
104     /**
105      * The Welcome Message Signature/Preamble
106      */
107     private final static String GREETING = "JXTAHELLO";
108
109     /**
110      * A space for separating elements of the welcome message.
111      */
112     private final static String SPACE = " ";
113
114     /**
115      * Version string for Welcome Message Version 1.1
116      */
117     private final static String WELCOME_VERSION_1_1 = "1.1";
118
119     /**
120      * Version string for Welcome Message Version 3.0
121      */
122     private final static String WELCOME_VERSION_3_0 = "3.0";
123
124     /**
125      * The current welcome message version. This is the only version we will emit.
126      */
127     private final static String CURRENTVERSION = WELCOME_VERSION_1_1;
128
129     /**
130      * The destination address that we believe we are connecting to.
131      */
132     private EndpointAddress destinationAddress;
133
134     /**
135      * Our return address, the purported source address of this connection.
136      */
137     private EndpointAddress publicAddress;
138
139     /**
140      * Our peerid, the logical return address.
141      */
142     private ID peerID;
143
144     /**
145      * This connection does not wish to receive any propagation/broadcast/notifications.
146      */
147     private boolean noPropagate;
148
149     /**
150      * The preferred binary wire message format version.
151      */
152     private int preferredMessageVersion;
153
154     /**
155      * The welcome message version we are supporting
156      */
157     private String versionString;
158
159     /**
160      * The welcome message as a text string.
161      */
162     private String welcomeString;
163
164     /**
165      * The welcome message as UTF-8 byte stream.
166      */
167     private byte[] welcomeBytes;
168     private final int MAX_LEN = 4096;
169
170     /**
171      * Default constructor
172      */
173     public WelcomeMessage() {}
174
175     /**
176      * Creates a new instance of WelcomeMessage for our Welcome Message.
177      *
178      * @param destAddr      The destination address that we believe we are connecting to.
179      * @param publicaddress Our return address, the purported source address of this connection.
180      * @param peerid        Our peerid, the logical return address.
181      * @param dontPropagate If <tt>true</tt> this connection does not wish to receive any propagation/broadcast/notifications.
182      */
183     public WelcomeMessage(EndpointAddress destAddr, EndpointAddress publicaddress, ID peerid, boolean dontPropagate) {
184         this(destAddr, publicaddress, peerid, dontPropagate, 0);
185     }
186
187     /**
188      * Creates a new instance of WelcomeMessage for our Welcome Message.
189      *
190      * @param destAddr            The destination address that we believe we are connecting to.
191      * @param publicaddress       Our return address, the purported source address of this connection.
192      * @param peerid              Our peerid, the logical return address.
193      * @param dontPropagate       If <tt>true</tt> this connection does not wish to receive any propagation/broadcast/notifications.
194      * @param preferredMsgVersion Binary Wire Messsage format we prefer.
195      */
196     public WelcomeMessage(EndpointAddress destAddr, EndpointAddress publicaddress, ID peerid, boolean dontPropagate, int preferredMsgVersion) {
197         destinationAddress = destAddr;
198         publicAddress = publicaddress;
199         peerID = peerid;
200         noPropagate = dontPropagate;
201         versionString = CURRENTVERSION;
202         preferredMessageVersion = preferredMsgVersion;
203
204         welcomeString = GREETING + SPACE + destAddr.toString() + SPACE + publicAddress.toString() + SPACE + peerID.toString()
205                 + SPACE + (noPropagate ? "1" : "0") + SPACE + versionString;
206
207         try {
208             welcomeBytes = welcomeString.getBytes("UTF-8");
209         } catch (UnsupportedEncodingException never) {// all implementations must support utf-8
210         }
211     }
212
213     /**
214      * Creates a new instance of WelcomeMessage for another peer's Welcome Message
215      *
216      * @param in The InputStream to read the welcome message from.
217      * @throws IOException If there is a problem reading the welcome header.
218      */
219     public WelcomeMessage(InputStream in) throws IOException {
220         welcomeBytes = new byte[MAX_LEN];
221         int readAt = 0;
222         boolean sawCR = false;
223         boolean sawCRLF = false;
224
225         // read the welcome message
226         do {
227             int c = in.read();
228
229             switch (c) {
230             case -1:
231                 throw new EOFException("Stream terminated before end of welcome message");
232
233             case '\r':
234                 if (sawCR) {
235                     welcomeBytes[readAt++] = (byte) 0x0D;
236                 }
237                 sawCR = true;
238                 break;
239
240             case '\n':
241                 if (sawCR) {
242                     sawCRLF = true;
243                 } else {
244                     welcomeBytes[readAt++] = (byte) 0x0A;
245                 }
246                 break;
247
248             default:
249                 welcomeBytes[readAt++] = (byte) c;
250                 sawCR = false;
251             }
252
253             if (readAt == welcomeBytes.length) {
254                 throw new IOException("Invalid welcome message, too long");
255             }
256
257         } while (!sawCRLF);
258
259         byte[] truncatedBytes = new byte[readAt];
260
261         System.arraycopy(welcomeBytes, 0, truncatedBytes, 0, readAt);
262         welcomeBytes = truncatedBytes;
263         welcomeString = new String(welcomeBytes, "UTF-8");
264         parseWelcome(welcomeString);
265     }
266
267     /**
268      * Attempts to init a welcome object from a socketChannel
269      *
270      * @param buffer the data buffer
271      * @return null if incomplete welcome was received
272      * @throws IOException if an io error occurs
273      */
274     public boolean read(ByteBuffer buffer) throws IOException {
275         int limit = buffer.limit();
276
277         if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
278             LOG.fine(MessageFormat.format("Reading a buffer of size :{0}", limit));
279         }
280         if (limit == 0) {
281             throw new IOException(MessageFormat.format("Invalid welcome message. Invalid length {0}", limit));
282         }
283         int eomPos = findEom(buffer, 0, limit);
284
285         if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
286             LOG.fine(MessageFormat.format("Buffer size :{0} Welcome End-Of-Message pos :{1}", limit, eomPos));
287         }
288         if (eomPos < 0) {
289             return false;
290         }
291         welcomeBytes = new byte[eomPos];
292         try {
293             buffer.get(welcomeBytes, 0, eomPos);
294             // skip <cr><ln>
295             buffer.position(eomPos + 2);
296             if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
297                 LOG.fine(MessageFormat.format("buffer stats :{0}", buffer.toString()));
298             }
299         } catch (BufferUnderflowException buf) {
300             // not enough data, signal for another read
301             return false;
302         }
303         welcomeString = new String(welcomeBytes, "UTF-8");
304         parseWelcome(welcomeString);
305         return true;
306     }
307
308     /**
309      * returns position of <cr><lf> position in buffer, otherwise -1
310      *
311      * @param buffer the byte buffer
312      * @param offset The offset within the buffer array
313      * @param length the length
314      * @return terminating position, or -1 if none found
315      */
316     private int findEom(ByteBuffer buffer, int offset, int length) {
317
318         int lastOffset = length - 2; // we are looking for 2 chars.
319         
320         for (int j = offset; j <= lastOffset; j++) {
321             byte c = buffer.get(j);
322            
323             if (c == '\r') {
324                 c = buffer.get(j + 1);
325                
326                 if (c == '\n') {
327                     return j;
328                 }
329             }
330         }
331         
332         return -1;
333     }
334
335     private void parseWelcome(String welcomeString) throws IOException {
336         List<String> thePieces = Arrays.asList(welcomeString.split("\\s"));
337
338         if (0 == thePieces.size()) {
339             throw new IOException("Invalid welcome message, did not contain any tokens.");
340         }
341
342         if (thePieces.size() < 5) {
343             throw new IOException("Invalid welcome message, did not contain enough tokens.");
344         }
345
346         if (!GREETING.equals(thePieces.get(0))) {
347             throw new IOException("Invalid welcome message, did not start with greeting");
348         }
349
350         try {
351             destinationAddress = new EndpointAddress(thePieces.get(1));
352         } catch (IllegalArgumentException badAddress) {
353             IOException failed = new IOException("Invalid welcome message, bad destination address");
354
355             failed.initCause(badAddress);
356             throw failed;
357         }
358
359         try {
360             publicAddress = new EndpointAddress(thePieces.get(2));
361         } catch (IllegalArgumentException badAddress) {
362             IOException failed = new IOException("Invalid welcome message, bad publicAddress address");
363
364             failed.initCause(badAddress);
365             throw failed;
366         }
367
368         try {
369             URI peerURI = new URI(thePieces.get(3));
370
371             peerID = IDFactory.fromURI(peerURI);
372         } catch (URISyntaxException badURI) {
373             IOException failed = new IOException("Invalid welcome message, bad peer id");
374
375             failed.initCause(badURI);
376             throw failed;
377         }
378
379         versionString = thePieces.get(thePieces.size() - 1);
380
381         if (WELCOME_VERSION_1_1.equals(versionString)) {
382             if (6 != thePieces.size()) {
383                 throw new IOException("Invalid welcome message, incorrect number of tokens.");
384             }
385
386             String noPropagateStr = thePieces.get(4);
387
388             if (noPropagateStr.equals("1")) {
389                 noPropagate = true;
390             } else if (noPropagateStr.equals("0")) {
391                 noPropagate = false;
392             } else {
393                 throw new IOException("Invalid welcome message, illegal value for propagate flag");
394             }
395
396             // preferred message version is not set in
397             preferredMessageVersion = 0;
398         } else if (WELCOME_VERSION_3_0.equals(versionString)) {
399             if (7 != thePieces.size()) {
400                 throw new IOException("Invalid welcome message, incorrect number of tokens.");
401             }
402
403             String noPropagateStr = thePieces.get(4);
404
405             if (noPropagateStr.equals("1")) {
406                 noPropagate = true;
407             } else if (noPropagateStr.equals("0")) {
408                 noPropagate = false;
409             } else {
410                 throw new IOException("Invalid welcome message, illegal value for propagate flag");
411             }
412
413             String preferredVersionStr = thePieces.get(5);
414
415             try {
416                 preferredMessageVersion = Integer.valueOf(preferredVersionStr);
417             } catch (IllegalArgumentException failed) {
418                 IOException failure = new IOException("Invalid welcome message, illegal value for preferred message version");
419
420                 failure.initCause(failed);
421
422                 throw failure;
423             }
424         } else {
425             // Unrecognized Welcome message version. Use default values.
426             noPropagate = false;
427             preferredMessageVersion = 0;
428         }
429         
430         if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
431             LOG.fine("Successfuly parsed a welcome message :" + getWelcomeString());
432         }
433     }
434
435     /**
436      * Write the welcome message to the provided stream.
437      *
438      * @param theStream The OutputStream to which to write the welcome message.
439      * @throws IOException If there is a problem writing the welcome message.
440      */
441     public void sendToStream(OutputStream theStream) throws IOException {
442         theStream.write(welcomeBytes);
443         theStream.write('\r');
444         theStream.write('\n');
445     }
446
447     /**
448      * Write the welcome to a socket channel
449      *
450      * @return A ByteBuffer of the welcome message
451      * @throws java.io.IOException if an io error occurs
452      */
453     public ByteBuffer getByteBuffer() throws IOException {
454         if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) {
455             LOG.fine(MessageFormat.format("Sending welcome message of size:{0}", welcomeBytes.length + 2));
456         }
457         ByteBuffer buffer = ByteBuffer.allocate(welcomeBytes.length + 2);
458
459         buffer.put(welcomeBytes);
460         buffer.put((byte) '\r');
461         buffer.put((byte) '\n');
462         buffer.flip();
463         return buffer;
464     }
465
466     /**
467      * Return the peerid associated with the Welcome Message.
468      *
469      * @return The peer ID from the Welcome Message.
470      */
471     public ID getPeerID() {
472         return peerID;
473     }
474
475     /**
476      * Return the source address associated with the Welcome Message.
477      *
478      * @return The source address from the Welcome Message.
479      */
480     public EndpointAddress getPublicAddress() {
481         return publicAddress;
482     }
483
484     /**
485      * Return the destination address associated with the Welcome Message.
486      *
487      * @return The destination address from the Welcome Message.
488      */
489     public EndpointAddress getDestinationAddress() {
490         return destinationAddress;
491     }
492
493     /**
494      * Return the propagation preference from the Welcome Message.
495      *
496      * @return <tt>true</tt> if <strong>no</strong> propagation is desired
497      *         otherwise <tt>false</tt>
498      */
499     public boolean dontPropagate() {
500         return noPropagate;
501     }
502
503     /**
504      * Return the preferred message version from the Welcome Message.
505      *
506      * @return The preferred Message Version.
507      */
508     public int getPreferredMessageVersion() {
509         return preferredMessageVersion;
510     }
511
512     /**
513      * Return the version associated with the Welcome Message.
514      *
515      * @return The version from the Welcome Message.
516      */
517     public String getWelcomeVersion() {
518         return versionString;
519     }
520
521     /**
522      * Return a String containing the Welcome Message.
523      *
524      * @return a String containing the Welcome Message.
525      */
526     public String getWelcomeString() {
527         return welcomeString;
528     }
529 }