• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2008-2009, Motorola, Inc.
3  *
4  * All rights reserved.
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  * - Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * - 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  * - Neither the name of the Motorola, Inc. nor the names of its contributors
17  * may be used to endorse or promote products derived from this software
18  * without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 package javax.obex;
34 
35 import android.security.Md5MessageDigest;
36 
37 import java.io.ByteArrayOutputStream;
38 import java.io.IOException;
39 import java.io.UnsupportedEncodingException;
40 import java.util.Calendar;
41 import java.util.Date;
42 import java.util.TimeZone;
43 
44 /**
45  * This class defines a set of helper methods for the implementation of Obex.
46  * @hide
47  */
48 public final class ObexHelper {
49 
50     /**
51      * Defines the basic packet length used by OBEX. Every OBEX packet has the
52      * same basic format:<BR>
53      * Byte 0: Request or Response Code Byte 1&2: Length of the packet.
54      */
55     public static final int BASE_PACKET_LENGTH = 3;
56 
57     /** Prevent object construction of helper class */
ObexHelper()58     private ObexHelper() {
59     }
60 
61     /**
62      * The maximum packet size for OBEX packets that this client can handle. At
63      * present, this must be changed for each port. TODO: The max packet size
64      * should be the Max incoming MTU minus TODO: L2CAP package headers and
65      * RFCOMM package headers. TODO: Retrieve the max incoming MTU from TODO:
66      * LocalDevice.getProperty().
67      */
68     /*
69      * android note set as 0xFFFE to match remote MPS
70      */
71     public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
72 
73     public static final int OBEX_OPCODE_CONNECT = 0x80;
74 
75     public static final int OBEX_OPCODE_DISCONNECT = 0x81;
76 
77     public static final int OBEX_OPCODE_PUT = 0x02;
78 
79     public static final int OBEX_OPCODE_PUT_FINAL = 0x82;
80 
81     public static final int OBEX_OPCODE_GET = 0x03;
82 
83     public static final int OBEX_OPCODE_GET_FINAL = 0x83;
84 
85     public static final int OBEX_OPCODE_RESERVED = 0x04;
86 
87     public static final int OBEX_OPCODE_RESERVED_FINAL = 0x84;
88 
89     public static final int OBEX_OPCODE_SETPATH = 0x85;
90 
91     public static final int OBEX_OPCODE_ABORT = 0xFF;
92 
93     public static final int OBEX_AUTH_REALM_CHARSET_ASCII = 0x00;
94 
95     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_1 = 0x01;
96 
97     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_2 = 0x02;
98 
99     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_3 = 0x03;
100 
101     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_4 = 0x04;
102 
103     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_5 = 0x05;
104 
105     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_6 = 0x06;
106 
107     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_7 = 0x07;
108 
109     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_8 = 0x08;
110 
111     public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_9 = 0x09;
112 
113     public static final int OBEX_AUTH_REALM_CHARSET_UNICODE = 0xFF;
114 
115     /**
116      * Updates the HeaderSet with the headers received in the byte array
117      * provided. Invalid headers are ignored.
118      * <P>
119      * The first two bits of an OBEX Header specifies the type of object that is
120      * being sent. The table below specifies the meaning of the high bits.
121      * <TABLE>
122      * <TR>
123      * <TH>Bits 8 and 7</TH>
124      * <TH>Value</TH>
125      * <TH>Description</TH>
126      * </TR>
127      * <TR>
128      * <TD>00</TD>
129      * <TD>0x00</TD>
130      * <TD>Null Terminated Unicode text, prefixed with 2 byte unsigned integer</TD>
131      * </TR>
132      * <TR>
133      * <TD>01</TD>
134      * <TD>0x40</TD>
135      * <TD>Byte Sequence, length prefixed with 2 byte unsigned integer</TD>
136      * </TR>
137      * <TR>
138      * <TD>10</TD>
139      * <TD>0x80</TD>
140      * <TD>1 byte quantity</TD>
141      * </TR>
142      * <TR>
143      * <TD>11</TD>
144      * <TD>0xC0</TD>
145      * <TD>4 byte quantity - transmitted in network byte order (high byte first</TD>
146      * </TR>
147      * </TABLE>
148      * This method uses the information in this table to determine the type of
149      * Java object to create and passes that object with the full header to
150      * setHeader() to update the HeaderSet object. Invalid headers will cause an
151      * exception to be thrown. When it is thrown, it is ignored.
152      * @param header the HeaderSet to update
153      * @param headerArray the byte array containing headers
154      * @return the result of the last start body or end body header provided;
155      *         the first byte in the result will specify if a body or end of
156      *         body is received
157      * @throws IOException if an invalid header was found
158      */
updateHeaderSet(HeaderSet header, byte[] headerArray)159     public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException {
160         int index = 0;
161         int length = 0;
162         int headerID;
163         byte[] value = null;
164         byte[] body = null;
165         HeaderSet headerImpl = header;
166         try {
167             while (index < headerArray.length) {
168                 headerID = 0xFF & headerArray[index];
169                 switch (headerID & (0xC0)) {
170 
171                     /*
172                      * 0x00 is a unicode null terminate string with the first
173                      * two bytes after the header identifier being the length
174                      */
175                     case 0x00:
176                         // Fall through
177                         /*
178                          * 0x40 is a byte sequence with the first
179                          * two bytes after the header identifier being the length
180                          */
181                     case 0x40:
182                         boolean trimTail = true;
183                         index++;
184                         length = 0xFF & headerArray[index];
185                         length = length << 8;
186                         index++;
187                         length += 0xFF & headerArray[index];
188                         length -= 3;
189                         index++;
190                         value = new byte[length];
191                         System.arraycopy(headerArray, index, value, 0, length);
192                         if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
193                             trimTail = false;
194                         }
195                         switch (headerID) {
196                             case HeaderSet.TYPE:
197                                 try {
198                                     // Remove trailing null
199                                     if (trimTail == false) {
200                                         headerImpl.setHeader(headerID, new String(value, 0,
201                                                 value.length, "ISO8859_1"));
202                                     } else {
203                                         headerImpl.setHeader(headerID, new String(value, 0,
204                                                 value.length - 1, "ISO8859_1"));
205                                     }
206                                 } catch (UnsupportedEncodingException e) {
207                                     throw e;
208                                 }
209                                 break;
210 
211                             case HeaderSet.AUTH_CHALLENGE:
212                                 headerImpl.mAuthChall = new byte[length];
213                                 System.arraycopy(headerArray, index, headerImpl.mAuthChall, 0,
214                                         length);
215                                 break;
216 
217                             case HeaderSet.AUTH_RESPONSE:
218                                 headerImpl.mAuthResp = new byte[length];
219                                 System.arraycopy(headerArray, index, headerImpl.mAuthResp, 0,
220                                         length);
221                                 break;
222 
223                             case HeaderSet.BODY:
224                                 /* Fall Through */
225                             case HeaderSet.END_OF_BODY:
226                                 body = new byte[length + 1];
227                                 body[0] = (byte)headerID;
228                                 System.arraycopy(headerArray, index, body, 1, length);
229                                 break;
230 
231                             case HeaderSet.TIME_ISO_8601:
232                                 try {
233                                     String dateString = new String(value, "ISO8859_1");
234                                     Calendar temp = Calendar.getInstance();
235                                     if ((dateString.length() == 16)
236                                             && (dateString.charAt(15) == 'Z')) {
237                                         temp.setTimeZone(TimeZone.getTimeZone("UTC"));
238                                     }
239                                     temp.set(Calendar.YEAR, Integer.parseInt(dateString.substring(
240                                             0, 4)));
241                                     temp.set(Calendar.MONTH, Integer.parseInt(dateString.substring(
242                                             4, 6)));
243                                     temp.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateString
244                                             .substring(6, 8)));
245                                     temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateString
246                                             .substring(9, 11)));
247                                     temp.set(Calendar.MINUTE, Integer.parseInt(dateString
248                                             .substring(11, 13)));
249                                     temp.set(Calendar.SECOND, Integer.parseInt(dateString
250                                             .substring(13, 15)));
251                                     headerImpl.setHeader(HeaderSet.TIME_ISO_8601, temp);
252                                 } catch (UnsupportedEncodingException e) {
253                                     throw e;
254                                 }
255                                 break;
256 
257                             default:
258                                 if ((headerID & 0xC0) == 0x00) {
259                                     headerImpl.setHeader(headerID, ObexHelper.convertToUnicode(
260                                             value, true));
261                                 } else {
262                                     headerImpl.setHeader(headerID, value);
263                                 }
264                         }
265 
266                         index += length;
267                         break;
268 
269                     /*
270                      * 0x80 is a byte header.  The only valid byte headers are
271                      * the 16 user defined byte headers.
272                      */
273                     case 0x80:
274                         index++;
275                         try {
276                             headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index]));
277                         } catch (Exception e) {
278                             // Not a valid header so ignore
279                         }
280                         index++;
281                         break;
282 
283                     /*
284                      * 0xC0 is a 4 byte unsigned integer header and with the
285                      * exception of TIME_4_BYTE will be converted to a Long
286                      * and added.
287                      */
288                     case 0xC0:
289                         index++;
290                         value = new byte[4];
291                         System.arraycopy(headerArray, index, value, 0, 4);
292                         try {
293                             if (headerID != HeaderSet.TIME_4_BYTE) {
294                                 // Determine if it is a connection ID.  These
295                                 // need to be handled differently
296                                 if (headerID == HeaderSet.CONNECTION_ID) {
297                                     headerImpl.mConnectionID = new byte[4];
298                                     System.arraycopy(value, 0, headerImpl.mConnectionID, 0, 4);
299                                 } else {
300                                     headerImpl.setHeader(headerID, Long
301                                             .valueOf(convertToLong(value)));
302                                 }
303                             } else {
304                                 Calendar temp = Calendar.getInstance();
305                                 temp.setTime(new Date(convertToLong(value) * 1000L));
306                                 headerImpl.setHeader(HeaderSet.TIME_4_BYTE, temp);
307                             }
308                         } catch (Exception e) {
309                             // Not a valid header so ignore
310                             throw new IOException("Header was not formatted properly");
311                         }
312                         index += 4;
313                         break;
314                 }
315 
316             }
317         } catch (IOException e) {
318             throw new IOException("Header was not formatted properly");
319         }
320 
321         return body;
322     }
323 
324     /**
325      * Creates the header part of OBEX packet based on the header provided.
326      * TODO: Could use getHeaderList() to get the array of headers to include
327      * and then use the high two bits to determine the the type of the object
328      * and construct the byte array from that. This will make the size smaller.
329      * @param head the header used to construct the byte array
330      * @param nullOut <code>true</code> if the header should be set to
331      *        <code>null</code> once it is added to the array or
332      *        <code>false</code> if it should not be nulled out
333      * @return the header of an OBEX packet
334      */
createHeader(HeaderSet head, boolean nullOut)335     public static byte[] createHeader(HeaderSet head, boolean nullOut) {
336         Long intHeader = null;
337         String stringHeader = null;
338         Calendar dateHeader = null;
339         Byte byteHeader = null;
340         StringBuffer buffer = null;
341         byte[] value = null;
342         byte[] result = null;
343         byte[] lengthArray = new byte[2];
344         int length;
345         HeaderSet headImpl = null;
346         ByteArrayOutputStream out = new ByteArrayOutputStream();
347         headImpl = head;
348 
349         try {
350             /*
351              * Determine if there is a connection ID to send.  If there is,
352              * then it should be the first header in the packet.
353              */
354             if ((headImpl.mConnectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) {
355 
356                 out.write((byte)HeaderSet.CONNECTION_ID);
357                 out.write(headImpl.mConnectionID);
358             }
359 
360             // Count Header
361             intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT);
362             if (intHeader != null) {
363                 out.write((byte)HeaderSet.COUNT);
364                 value = ObexHelper.convertToByteArray(intHeader.longValue());
365                 out.write(value);
366                 if (nullOut) {
367                     headImpl.setHeader(HeaderSet.COUNT, null);
368                 }
369             }
370 
371             // Name Header
372             stringHeader = (String)headImpl.getHeader(HeaderSet.NAME);
373             if (stringHeader != null) {
374                 out.write((byte)HeaderSet.NAME);
375                 value = ObexHelper.convertToUnicodeByteArray(stringHeader);
376                 length = value.length + 3;
377                 lengthArray[0] = (byte)(0xFF & (length >> 8));
378                 lengthArray[1] = (byte)(0xFF & length);
379                 out.write(lengthArray);
380                 out.write(value);
381                 if (nullOut) {
382                     headImpl.setHeader(HeaderSet.NAME, null);
383                 }
384             }
385 
386             // Type Header
387             stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE);
388             if (stringHeader != null) {
389                 out.write((byte)HeaderSet.TYPE);
390                 try {
391                     value = stringHeader.getBytes("ISO8859_1");
392                 } catch (UnsupportedEncodingException e) {
393                     throw e;
394                 }
395 
396                 length = value.length + 4;
397                 lengthArray[0] = (byte)(255 & (length >> 8));
398                 lengthArray[1] = (byte)(255 & length);
399                 out.write(lengthArray);
400                 out.write(value);
401                 out.write(0x00);
402                 if (nullOut) {
403                     headImpl.setHeader(HeaderSet.TYPE, null);
404                 }
405             }
406 
407             // Length Header
408             intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH);
409             if (intHeader != null) {
410                 out.write((byte)HeaderSet.LENGTH);
411                 value = ObexHelper.convertToByteArray(intHeader.longValue());
412                 out.write(value);
413                 if (nullOut) {
414                     headImpl.setHeader(HeaderSet.LENGTH, null);
415                 }
416             }
417 
418             // Time ISO Header
419             dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601);
420             if (dateHeader != null) {
421 
422                 /*
423                  * The ISO Header should take the form YYYYMMDDTHHMMSSZ.  The
424                  * 'Z' will only be included if it is a UTC time.
425                  */
426                 buffer = new StringBuffer();
427                 int temp = dateHeader.get(Calendar.YEAR);
428                 for (int i = temp; i < 1000; i = i * 10) {
429                     buffer.append("0");
430                 }
431                 buffer.append(temp);
432                 temp = dateHeader.get(Calendar.MONTH);
433                 if (temp < 10) {
434                     buffer.append("0");
435                 }
436                 buffer.append(temp);
437                 temp = dateHeader.get(Calendar.DAY_OF_MONTH);
438                 if (temp < 10) {
439                     buffer.append("0");
440                 }
441                 buffer.append(temp);
442                 buffer.append("T");
443                 temp = dateHeader.get(Calendar.HOUR_OF_DAY);
444                 if (temp < 10) {
445                     buffer.append("0");
446                 }
447                 buffer.append(temp);
448                 temp = dateHeader.get(Calendar.MINUTE);
449                 if (temp < 10) {
450                     buffer.append("0");
451                 }
452                 buffer.append(temp);
453                 temp = dateHeader.get(Calendar.SECOND);
454                 if (temp < 10) {
455                     buffer.append("0");
456                 }
457                 buffer.append(temp);
458 
459                 if (dateHeader.getTimeZone().getID().equals("UTC")) {
460                     buffer.append("Z");
461                 }
462 
463                 try {
464                     value = buffer.toString().getBytes("ISO8859_1");
465                 } catch (UnsupportedEncodingException e) {
466                     throw e;
467                 }
468 
469                 length = value.length + 3;
470                 lengthArray[0] = (byte)(255 & (length >> 8));
471                 lengthArray[1] = (byte)(255 & length);
472                 out.write(HeaderSet.TIME_ISO_8601);
473                 out.write(lengthArray);
474                 out.write(value);
475                 if (nullOut) {
476                     headImpl.setHeader(HeaderSet.TIME_ISO_8601, null);
477                 }
478             }
479 
480             // Time 4 Byte Header
481             dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE);
482             if (dateHeader != null) {
483                 out.write(HeaderSet.TIME_4_BYTE);
484 
485                 /*
486                  * Need to call getTime() twice.  The first call will return
487                  * a java.util.Date object.  The second call returns the number
488                  * of milliseconds since January 1, 1970.  We need to convert
489                  * it to seconds since the TIME_4_BYTE expects the number of
490                  * seconds since January 1, 1970.
491                  */
492                 value = ObexHelper.convertToByteArray(dateHeader.getTime().getTime() / 1000L);
493                 out.write(value);
494                 if (nullOut) {
495                     headImpl.setHeader(HeaderSet.TIME_4_BYTE, null);
496                 }
497             }
498 
499             // Description Header
500             stringHeader = (String)headImpl.getHeader(HeaderSet.DESCRIPTION);
501             if (stringHeader != null) {
502                 out.write((byte)HeaderSet.DESCRIPTION);
503                 value = ObexHelper.convertToUnicodeByteArray(stringHeader);
504                 length = value.length + 3;
505                 lengthArray[0] = (byte)(255 & (length >> 8));
506                 lengthArray[1] = (byte)(255 & length);
507                 out.write(lengthArray);
508                 out.write(value);
509                 if (nullOut) {
510                     headImpl.setHeader(HeaderSet.DESCRIPTION, null);
511                 }
512             }
513 
514             // Target Header
515             value = (byte[])headImpl.getHeader(HeaderSet.TARGET);
516             if (value != null) {
517                 out.write((byte)HeaderSet.TARGET);
518                 length = value.length + 3;
519                 lengthArray[0] = (byte)(255 & (length >> 8));
520                 lengthArray[1] = (byte)(255 & length);
521                 out.write(lengthArray);
522                 out.write(value);
523                 if (nullOut) {
524                     headImpl.setHeader(HeaderSet.TARGET, null);
525                 }
526             }
527 
528             // HTTP Header
529             value = (byte[])headImpl.getHeader(HeaderSet.HTTP);
530             if (value != null) {
531                 out.write((byte)HeaderSet.HTTP);
532                 length = value.length + 3;
533                 lengthArray[0] = (byte)(255 & (length >> 8));
534                 lengthArray[1] = (byte)(255 & length);
535                 out.write(lengthArray);
536                 out.write(value);
537                 if (nullOut) {
538                     headImpl.setHeader(HeaderSet.HTTP, null);
539                 }
540             }
541 
542             // Who Header
543             value = (byte[])headImpl.getHeader(HeaderSet.WHO);
544             if (value != null) {
545                 out.write((byte)HeaderSet.WHO);
546                 length = value.length + 3;
547                 lengthArray[0] = (byte)(255 & (length >> 8));
548                 lengthArray[1] = (byte)(255 & length);
549                 out.write(lengthArray);
550                 out.write(value);
551                 if (nullOut) {
552                     headImpl.setHeader(HeaderSet.WHO, null);
553                 }
554             }
555 
556             // Connection ID Header
557             value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER);
558             if (value != null) {
559                 out.write((byte)HeaderSet.APPLICATION_PARAMETER);
560                 length = value.length + 3;
561                 lengthArray[0] = (byte)(255 & (length >> 8));
562                 lengthArray[1] = (byte)(255 & length);
563                 out.write(lengthArray);
564                 out.write(value);
565                 if (nullOut) {
566                     headImpl.setHeader(HeaderSet.APPLICATION_PARAMETER, null);
567                 }
568             }
569 
570             // Object Class Header
571             value = (byte[])headImpl.getHeader(HeaderSet.OBJECT_CLASS);
572             if (value != null) {
573                 out.write((byte)HeaderSet.OBJECT_CLASS);
574                 length = value.length + 3;
575                 lengthArray[0] = (byte)(255 & (length >> 8));
576                 lengthArray[1] = (byte)(255 & length);
577                 out.write(lengthArray);
578                 out.write(value);
579                 if (nullOut) {
580                     headImpl.setHeader(HeaderSet.OBJECT_CLASS, null);
581                 }
582             }
583 
584             // Check User Defined Headers
585             for (int i = 0; i < 16; i++) {
586 
587                 //Unicode String Header
588                 stringHeader = (String)headImpl.getHeader(i + 0x30);
589                 if (stringHeader != null) {
590                     out.write((byte)i + 0x30);
591                     value = ObexHelper.convertToUnicodeByteArray(stringHeader);
592                     length = value.length + 3;
593                     lengthArray[0] = (byte)(255 & (length >> 8));
594                     lengthArray[1] = (byte)(255 & length);
595                     out.write(lengthArray);
596                     out.write(value);
597                     if (nullOut) {
598                         headImpl.setHeader(i + 0x30, null);
599                     }
600                 }
601 
602                 // Byte Sequence Header
603                 value = (byte[])headImpl.getHeader(i + 0x70);
604                 if (value != null) {
605                     out.write((byte)i + 0x70);
606                     length = value.length + 3;
607                     lengthArray[0] = (byte)(255 & (length >> 8));
608                     lengthArray[1] = (byte)(255 & length);
609                     out.write(lengthArray);
610                     out.write(value);
611                     if (nullOut) {
612                         headImpl.setHeader(i + 0x70, null);
613                     }
614                 }
615 
616                 // Byte Header
617                 byteHeader = (Byte)headImpl.getHeader(i + 0xB0);
618                 if (byteHeader != null) {
619                     out.write((byte)i + 0xB0);
620                     out.write(byteHeader.byteValue());
621                     if (nullOut) {
622                         headImpl.setHeader(i + 0xB0, null);
623                     }
624                 }
625 
626                 // Integer header
627                 intHeader = (Long)headImpl.getHeader(i + 0xF0);
628                 if (intHeader != null) {
629                     out.write((byte)i + 0xF0);
630                     out.write(ObexHelper.convertToByteArray(intHeader.longValue()));
631                     if (nullOut) {
632                         headImpl.setHeader(i + 0xF0, null);
633                     }
634                 }
635             }
636 
637             // Add the authentication challenge header
638             if (headImpl.mAuthChall != null) {
639                 out.write((byte)HeaderSet.AUTH_CHALLENGE);
640                 length = headImpl.mAuthChall.length + 3;
641                 lengthArray[0] = (byte)(255 & (length >> 8));
642                 lengthArray[1] = (byte)(255 & length);
643                 out.write(lengthArray);
644                 out.write(headImpl.mAuthChall);
645                 if (nullOut) {
646                     headImpl.mAuthChall = null;
647                 }
648             }
649 
650             // Add the authentication response header
651             if (headImpl.mAuthResp != null) {
652                 out.write((byte)HeaderSet.AUTH_RESPONSE);
653                 length = headImpl.mAuthResp.length + 3;
654                 lengthArray[0] = (byte)(255 & (length >> 8));
655                 lengthArray[1] = (byte)(255 & length);
656                 out.write(lengthArray);
657                 out.write(headImpl.mAuthResp);
658                 if (nullOut) {
659                     headImpl.mAuthResp = null;
660                 }
661             }
662 
663         } catch (IOException e) {
664         } finally {
665             result = out.toByteArray();
666             try {
667                 out.close();
668             } catch (Exception ex) {
669             }
670         }
671 
672         return result;
673 
674     }
675 
676     /**
677      * Determines where the maximum divide is between headers. This method is
678      * used by put and get operations to separate headers to a size that meets
679      * the max packet size allowed.
680      * @param headerArray the headers to separate
681      * @param start the starting index to search
682      * @param maxSize the maximum size of a packet
683      * @return the index of the end of the header block to send or -1 if the
684      *         header could not be divided because the header is too large
685      */
findHeaderEnd(byte[] headerArray, int start, int maxSize)686     public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) {
687 
688         int fullLength = 0;
689         int lastLength = -1;
690         int index = start;
691         int length = 0;
692 
693         while ((fullLength < maxSize) && (index < headerArray.length)) {
694             int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
695             lastLength = fullLength;
696 
697             switch (headerID & (0xC0)) {
698 
699                 case 0x00:
700                     // Fall through
701                 case 0x40:
702 
703                     index++;
704                     length = (headerArray[index] < 0 ? headerArray[index] + 256
705                             : headerArray[index]);
706                     length = length << 8;
707                     index++;
708                     length += (headerArray[index] < 0 ? headerArray[index] + 256
709                             : headerArray[index]);
710                     length -= 3;
711                     index++;
712                     index += length;
713                     fullLength += length + 3;
714                     break;
715 
716                 case 0x80:
717 
718                     index++;
719                     index++;
720                     fullLength += 2;
721                     break;
722 
723                 case 0xC0:
724 
725                     index += 5;
726                     fullLength += 5;
727                     break;
728 
729             }
730 
731         }
732 
733         /*
734          * Determine if this is the last header or not
735          */
736         if (lastLength == 0) {
737             /*
738              * Since this is the last header, check to see if the size of this
739              * header is less then maxSize.  If it is, return the length of the
740              * header, otherwise return -1.  The length of the header is
741              * returned since it would be the start of the next header
742              */
743             if (fullLength < maxSize) {
744                 return headerArray.length;
745             } else {
746                 return -1;
747             }
748         } else {
749             return lastLength + start;
750         }
751     }
752 
753     /**
754      * Converts the byte array to a long.
755      * @param b the byte array to convert to a long
756      * @return the byte array as a long
757      */
convertToLong(byte[] b)758     public static long convertToLong(byte[] b) {
759         long result = 0;
760         long value = 0;
761         long power = 0;
762 
763         for (int i = (b.length - 1); i >= 0; i--) {
764             value = b[i];
765             if (value < 0) {
766                 value += 256;
767             }
768 
769             result = result | (value << power);
770             power += 8;
771         }
772 
773         return result;
774     }
775 
776     /**
777      * Converts the long to a 4 byte array. The long must be non negative.
778      * @param l the long to convert
779      * @return a byte array that is the same as the long
780      */
convertToByteArray(long l)781     public static byte[] convertToByteArray(long l) {
782         byte[] b = new byte[4];
783 
784         b[0] = (byte)(255 & (l >> 24));
785         b[1] = (byte)(255 & (l >> 16));
786         b[2] = (byte)(255 & (l >> 8));
787         b[3] = (byte)(255 & l);
788 
789         return b;
790     }
791 
792     /**
793      * Converts the String to a UNICODE byte array. It will also add the ending
794      * null characters to the end of the string.
795      * @param s the string to convert
796      * @return the unicode byte array of the string
797      */
convertToUnicodeByteArray(String s)798     public static byte[] convertToUnicodeByteArray(String s) {
799         if (s == null) {
800             return null;
801         }
802 
803         char c[] = s.toCharArray();
804         byte[] result = new byte[(c.length * 2) + 2];
805         for (int i = 0; i < c.length; i++) {
806             result[(i * 2)] = (byte)(c[i] >> 8);
807             result[((i * 2) + 1)] = (byte)c[i];
808         }
809 
810         // Add the UNICODE null character
811         result[result.length - 2] = 0;
812         result[result.length - 1] = 0;
813 
814         return result;
815     }
816 
817     /**
818      * Retrieves the value from the byte array for the tag value specified. The
819      * array should be of the form Tag - Length - Value triplet.
820      * @param tag the tag to retrieve from the byte array
821      * @param triplet the byte sequence containing the tag length value form
822      * @return the value of the specified tag
823      */
getTagValue(byte tag, byte[] triplet)824     public static byte[] getTagValue(byte tag, byte[] triplet) {
825 
826         int index = findTag(tag, triplet);
827         if (index == -1) {
828             return null;
829         }
830 
831         index++;
832         int length = triplet[index] & 0xFF;
833 
834         byte[] result = new byte[length];
835         index++;
836         System.arraycopy(triplet, index, result, 0, length);
837 
838         return result;
839     }
840 
841     /**
842      * Finds the index that starts the tag value pair in the byte array provide.
843      * @param tag the tag to look for
844      * @param value the byte array to search
845      * @return the starting index of the tag or -1 if the tag could not be found
846      */
findTag(byte tag, byte[] value)847     public static int findTag(byte tag, byte[] value) {
848         int length = 0;
849 
850         if (value == null) {
851             return -1;
852         }
853 
854         int index = 0;
855 
856         while ((index < value.length) && (value[index] != tag)) {
857             length = value[index + 1] & 0xFF;
858             index += length + 2;
859         }
860 
861         if (index >= value.length) {
862             return -1;
863         }
864 
865         return index;
866     }
867 
868     /**
869      * Converts the byte array provided to a unicode string.
870      * @param b the byte array to convert to a string
871      * @param includesNull determine if the byte string provided contains the
872      *        UNICODE null character at the end or not; if it does, it will be
873      *        removed
874      * @return a Unicode string
875      * @throws IllegalArgumentException if the byte array has an odd length
876      */
convertToUnicode(byte[] b, boolean includesNull)877     public static String convertToUnicode(byte[] b, boolean includesNull) {
878         if (b == null || b.length == 0) {
879             return null;
880         }
881         int arrayLength = b.length;
882         if (!((arrayLength % 2) == 0)) {
883             throw new IllegalArgumentException("Byte array not of a valid form");
884         }
885         arrayLength = (arrayLength >> 1);
886         if (includesNull) {
887             arrayLength -= 1;
888         }
889 
890         char[] c = new char[arrayLength];
891         for (int i = 0; i < arrayLength; i++) {
892             int upper = b[2 * i];
893             int lower = b[(2 * i) + 1];
894             if (upper < 0) {
895                 upper += 256;
896             }
897             if (lower < 0) {
898                 lower += 256;
899             }
900             // If upper and lower both equal 0, it should be the end of string.
901             // Ignore left bytes from array to avoid potential issues
902             if (upper == 0 && lower == 0) {
903                 return new String(c, 0, i);
904             }
905 
906             c[i] = (char)((upper << 8) | lower);
907         }
908 
909         return new String(c);
910     }
911 
912     /**
913      * Compute the MD5 hash of the byte array provided. Does not accumulate
914      * input.
915      * @param in the byte array to hash
916      * @return the MD5 hash of the byte array
917      */
computeMd5Hash(byte[] in)918     public static byte[] computeMd5Hash(byte[] in) {
919         Md5MessageDigest md5 = new Md5MessageDigest();
920         return md5.digest(in);
921     }
922 
923     /**
924      * Computes an authentication challenge header.
925      * @param nonce the challenge that will be provided to the peer; the
926      *        challenge must be 16 bytes long
927      * @param realm a short description that describes what password to use
928      * @param access if <code>true</code> then full access will be granted if
929      *        successful; if <code>false</code> then read only access will be
930      *        granted if successful
931      * @param userID if <code>true</code>, a user ID is required in the reply;
932      *        if <code>false</code>, no user ID is required
933      * @throws IllegalArgumentException if the challenge is not 16 bytes long;
934      *         if the realm can not be encoded in less then 255 bytes
935      * @throws IOException if the encoding scheme ISO 8859-1 is not supported
936      */
computeAuthenticationChallenge(byte[] nonce, String realm, boolean access, boolean userID)937     public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access,
938             boolean userID) throws IOException {
939         byte[] authChall = null;
940 
941         if (nonce.length != 16) {
942             throw new IllegalArgumentException("Nonce must be 16 bytes long");
943         }
944 
945         /*
946          * The authentication challenge is a byte sequence of the following form
947          * byte 0: 0x00 - the tag for the challenge
948          * byte 1: 0x10 - the length of the challenge; must be 16
949          * byte 2-17: the authentication challenge
950          * byte 18: 0x01 - the options tag; this is optional in the spec, but
951          *                 we are going to include it in every message
952          * byte 19: 0x01 - length of the options; must be 1
953          * byte 20: the value of the options; bit 0 is set if user ID is
954          *          required; bit 1 is set if access mode is read only
955          * byte 21: 0x02 - the tag for authentication realm; only included if
956          *                 an authentication realm is specified
957          * byte 22: the length of the authentication realm; only included if
958          *          the authentication realm is specified
959          * byte 23: the encoding scheme of the authentication realm; we will use
960          *          the ISO 8859-1 encoding scheme since it is part of the KVM
961          * byte 24 & up: the realm if one is specified.
962          */
963         if (realm == null) {
964             authChall = new byte[21];
965         } else {
966             if (realm.length() >= 255) {
967                 throw new IllegalArgumentException("Realm must be less then 255 bytes");
968             }
969             authChall = new byte[24 + realm.length()];
970             authChall[21] = 0x02;
971             authChall[22] = (byte)(realm.length() + 1);
972             authChall[23] = 0x01; // ISO 8859-1 Encoding
973             System.arraycopy(realm.getBytes("ISO8859_1"), 0, authChall, 24, realm.length());
974         }
975 
976         // Include the nonce field in the header
977         authChall[0] = 0x00;
978         authChall[1] = 0x10;
979         System.arraycopy(nonce, 0, authChall, 2, 16);
980 
981         // Include the options header
982         authChall[18] = 0x01;
983         authChall[19] = 0x01;
984         authChall[20] = 0x00;
985 
986         if (!access) {
987             authChall[20] = (byte)(authChall[20] | 0x02);
988         }
989         if (userID) {
990             authChall[20] = (byte)(authChall[20] | 0x01);
991         }
992 
993         return authChall;
994     }
995 }
996