001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.kvem.jsr082.obex;
027:
028: import java.io.IOException;
029: import java.io.InterruptedIOException;
030: import java.util.Calendar;
031: import java.util.Date;
032: import java.util.TimeZone;
033: import java.util.Vector;
034: import java.util.Stack;
035: import javax.microedition.io.Connection;
036: import javax.obex.ResponseCodes;
037: import javax.obex.Authenticator;
038: import com.sun.midp.log.Logging;
039:
040: /**
041: * Obex core protocol.
042: */
043: public abstract class ObexPacketStream implements Connection {
044:
045: /** Debug information, should be false for RR. */
046: private static final boolean DEBUG = false;
047: private static final boolean DEBUG2 = false;
048:
049: // OBEX operations opcodes
050: static final int OPCODE_CONNECT = 0x80;
051: static final int OPCODE_DISCONNECT = 0x81;
052: static final int OPCODE_PUT = 0x02;
053: static final int OPCODE_GET = 0x03;
054: static final int OPCODE_SETPATH = 0x85;
055: static final int OPCODE_CONTINUE = 0x90;
056: static final int OPCODE_ABORT = 0xFF;
057: static final int OPCODE_FINAL = 0x80;
058: static final int OPCODE_GET_FINAL = OPCODE_GET | OPCODE_FINAL;
059:
060: static final byte[] PACKET_ABORT = { (byte) OPCODE_ABORT, 0, 0 };
061: static final byte[] PACKET_CONTINUE = { (byte) OPCODE_CONTINUE, 0,
062: 0 };
063: static final byte[] PACKET_DISCONNECT = { (byte) OPCODE_DISCONNECT,
064: 0, 0 };
065: static final byte[] PACKET_SUCCESS = {
066: (byte) ResponseCodes.OBEX_HTTP_OK, 0, 0 };
067:
068: static final byte[] PACKET_BAD_REQUEST = {
069: (byte) ResponseCodes.OBEX_HTTP_BAD_REQUEST, 0, 0 };
070:
071: static final byte[] PACKET_NOT_IMPLEMENTED = {
072: (byte) ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, 0, 0 };
073:
074: private static final int HEADER_BODY = 0x48;
075: private static final int HEADER_EOFBODY = 0x49;
076: private static final int HEADER_CONNECTION_ID = 0xCB;
077: static final int HEADER_AUTH_CHALLENGE = 0x4D;
078: static final int HEADER_AUTH_RESPONSE = 0x4E;
079:
080: static TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
081:
082: private static final int TRANSPORT_READ_INTERVAL = 10;
083:
084: private ObexTransport transport;
085:
086: Authenticator authenticator;
087:
088: /**
089: * Generated authentication responses. They will be send in sendPacket().
090: * Stored in <CODE>byte[]</CODE> format.
091: */
092: Vector authResponses = new Vector();
093:
094: /**
095: * Sent authentication challenges.
096: * They will be used for check authentication responses.
097: */
098: Vector authChallenges = new Vector();
099:
100: /**
101: * True when buffer contains packet for sending and some headers can
102: * be added.
103: */
104: boolean moreHeaders = false;
105:
106: /**
107: * Outgoing packet contains authentication challenges, so it should be
108: * sent immediatly.
109: */
110: boolean challengesToSend = false;
111:
112: /**
113: * True if one of sending headers cannot feet in empty packet.
114: */
115: boolean headerOverflow = false;
116:
117: /**
118: * True if sending packet contains target header.
119: */
120: boolean containsTargetHeader = false;
121:
122: /**
123: * Queue of outgoing headers, not feet in packet.
124: */
125: Vector queuedHeaders;
126: QueuedHeader newHeader;
127: Stack emptyHeadersPool;
128:
129: /**
130: * Set when sending auth challenge,
131: * reset when received valid auth response in next
132: * packet.
133: */
134: boolean authFailed = false;
135:
136: /**
137: * True if this is ClientSession, false in ServerConnectionImpl.
138: */
139: boolean isClient;
140:
141: /**
142: * Client is connected flag. Ignored by ServerConnectionImpl.
143: */
144: boolean isConnected;
145:
146: int OBEX_MAXIMUM_PACKET_LENGTH;
147:
148: byte[] buffer, cache;
149: int packetLength;
150: int packetOffset;
151: int packetType;
152:
153: int maxSendLength;
154:
155: boolean dataOpened, dataClosed;
156: boolean isEof;
157: int dataOffset;
158:
159: /**
160: * Connection id used in <code>setConnectionID</code>,
161: * <code>getConnectioID</code>.
162: */
163:
164: ObexPacketStream(ObexTransport transport) {
165: this .transport = transport;
166: OBEX_MAXIMUM_PACKET_LENGTH = transport.getMaximumPacketSize();
167: buffer = new byte[OBEX_MAXIMUM_PACKET_LENGTH];
168: cache = new byte[OBEX_MAXIMUM_PACKET_LENGTH];
169: maxSendLength = OBEX_MAXIMUM_PACKET_LENGTH;
170: newHeader = new QueuedHeader(this );
171: queuedHeaders = new Vector();
172: emptyHeadersPool = new Stack();
173: }
174:
175: // interface visible function
176: public void close() {
177: try {
178: if (transport != null) {
179: transport.close();
180: }
181: } catch (IOException e) {
182: // nothing
183: }
184: transport = null;
185: }
186:
187: public void setAuthenticator(Authenticator authenticator) {
188: if (authenticator == null) {
189: throw new NullPointerException("null authenticator");
190: }
191: this .authenticator = authenticator;
192: }
193:
194: public Connection getTransport() throws IOException {
195: if (transport == null) {
196: throw new IOException("connection error");
197: }
198: return transport.getUnderlyingConnection();
199: }
200:
201: /**
202: * Sets link broken flag.
203: */
204: void brokenLink() {
205: close();
206: }
207:
208: boolean isClosed() {
209: return transport == null;
210: }
211:
212: void packetBegin(byte[] head) {
213: if (DEBUG) {
214: System.out.println("packetBegin()");
215: }
216:
217: containsTargetHeader = false;
218: moreHeaders = true;
219: challengesToSend = false;
220: System.arraycopy(head, 0, buffer, 0, head.length);
221: packetLength = head.length;
222: authChallenges.removeAllElements();
223: dataOpened = false;
224: dataClosed = false;
225: dataOffset = -3; // generate aoobe when accessed
226: }
227:
228: int packetAddData(byte[] data, int offset, int length) {
229: if (DEBUG) {
230: System.out.println("packetAddData()");
231: }
232: // preventing writing several data blocks, just in case
233: if (dataClosed)
234: return 0;
235:
236: if (!dataOpened) {
237: // let it be at least 3 bytes workload to create new Body header
238: if (packetLength + 6 > maxSendLength) {
239: return 0;
240: }
241: buffer[packetLength] = HEADER_BODY;
242: dataOffset = packetLength;
243: packetLength += 3;
244: dataOpened = true;
245: }
246:
247: int len;
248: if (packetLength + length > maxSendLength) {
249: len = maxSendLength - packetLength;
250: } else {
251: len = length;
252: }
253: System.arraycopy(data, offset, buffer, packetLength, len);
254: packetLength += len;
255: return len;
256: }
257:
258: int getPacketLength() {
259: return packetLength;
260: }
261:
262: void restorePacketLength(int len) {
263: packetLength = len;
264: }
265:
266: boolean packetEOFBody() {
267: if (DEBUG) {
268: System.out.println("packetEOFBody()");
269: }
270: if (dataClosed) {
271: return false;
272: }
273: if (dataOpened) {
274: buffer[dataOffset + 0] = HEADER_EOFBODY;
275: return true;
276: } else {
277: if (packetLength + 3 > maxSendLength) {
278: return false;
279: }
280: buffer[packetLength++] = HEADER_EOFBODY;
281: buffer[packetLength++] = 0; // length
282: buffer[packetLength++] = 3;
283: return true;
284: }
285: }
286:
287: void packetMarkFinal() {
288: if (DEBUG) {
289: System.out.println("packetMarkFinal()");
290: }
291: buffer[0] |= 0x80;
292: }
293:
294: void setPacketType(int type) {
295: if (DEBUG) {
296: System.out.println("setPacketType()");
297: }
298: buffer[0] = (byte) type;
299: }
300:
301: /**
302: * Finish packet and send it. Remove Connection ID header if packet also
303: * contains TARGET header.
304: */
305: void packetEndStripConnID() throws IOException {
306:
307: // first header id is in 3 byte on all packet except CONNECT
308: // and this function is known not to be called for connect() operation.
309: if ((buffer[3] & 0xFF) == HeaderSetImpl.TARGET) {
310: packetLength -= 5;
311:
312: // length of Connection ID packet is 5 bytes:
313: // 1 byte header + 4 byte int value
314: for (int i = 3; i < packetLength; i++) {
315: buffer[i] = buffer[i + 5];
316: }
317: }
318: packetEnd();
319: }
320:
321: void packetEnd() throws IOException {
322: if (DEBUG) {
323: System.out.println("packetEnd()");
324: }
325: moreHeaders = false;
326:
327: if (transport == null) {
328: throw new IOException("connection error");
329: }
330:
331: if (dataOpened) {
332: // closing Body header
333: int len = packetLength - dataOffset;
334: buffer[dataOffset + 1] = (byte) (len >> 8);
335: buffer[dataOffset + 2] = (byte) len;
336: dataOpened = false;
337: dataClosed = true;
338: }
339: // update packet length field
340: buffer[1] = (byte) (packetLength / 0x100);
341: buffer[2] = (byte) (packetLength % 0x100);
342:
343: if (DEBUG) {
344: int len = packetLength;
345: if (!DEBUG2 && len > 20) {
346: len = 20;
347: }
348: System.out.println("send:");
349: for (int i = 0; i < len; i++) {
350: System.out.print(" 0x"
351: + Integer.toHexString(buffer[i] & 0xFF));
352: int chr = buffer[i] & 0xFF;
353: if (chr >= 32 && chr < 128) {
354: System.out.print("(" + (char) (buffer[i] & 0xFF)
355: + ")");
356: }
357: }
358: if (packetLength != len) {
359: System.out.print("...");
360: }
361: System.out.println("");
362: }
363: try {
364: transport.write(buffer, packetLength);
365: } catch (IOException e) {
366: brokenLink();
367: throw e;
368: }
369: }
370:
371: /**
372: * Connection Identifier:
373: * * must be first header in packet
374: * * 0xFFFFFFFF considered invalid - it is up to application
375: * * can't be sent on connect() request
376: * * can't be used with Target header in one packet
377: */
378: final void packetAddConnectionID(long id, HeaderSetImpl headers) {
379: // SPEC: Illegal to send a Connection Id and a Target header
380: // in the same operation.
381: if (headers != null
382: && headers.getHeader(HeaderSetImpl.TARGET) != null) {
383: return;
384: }
385: if (id < 0L || id > 0xFFFFFFFFL) {
386: return;
387: }
388: buffer[packetLength++] = (byte) HEADER_CONNECTION_ID;
389: encodeInt(id);
390: }
391:
392: /**
393: * This method is called to handle a situation than header is too large.
394: * @throws IOException
395: */
396: abstract void headerTooLarge() throws IOException;
397:
398: /**
399: * Adds the specified headers to the packet.
400: */
401: final void packetAddHeaders(HeaderSetImpl headers)
402: throws IOException {
403: if (DEBUG) {
404: System.out.println("packetAddHeaders()");
405: }
406: headerOverflow = false;
407: newHeader.sendAllQueued();
408:
409: if (headers == null) {
410: return;
411: }
412:
413: int[] idList = headers.getHeaderList();
414:
415: if (!headers.challenges.isEmpty()) {
416: newHeader.sendOrQueue(HEADER_AUTH_CHALLENGE,
417: headers.challenges);
418: }
419:
420: if (idList == null) {
421: return;
422: }
423:
424: for (int i = 0; i < idList.length; i++) {
425: int id = idList[i];
426: Object value = headers.getHeader(id);
427: newHeader.sendOrQueue(id, value);
428: }
429: }
430:
431: void packetAddAuthResponses() throws IOException {
432: try {
433: for (int i = 0; i < authResponses.size(); i++) {
434: if (DEBUG) {
435: System.out
436: .println("packetAddAuthResponses(): added response");
437: }
438: ObexAuth response = (ObexAuth) authResponses
439: .elementAt(i);
440: int len = response.replyAuthChallenge(buffer,
441: packetLength, authenticator);
442: packetLength += len;
443: }
444: } catch (ArrayIndexOutOfBoundsException e) {
445: throw new IOException("auth response request too large");
446: }
447: if (packetLength > maxSendLength) {
448: throw new IOException("auth response request too large");
449: }
450: }
451:
452: final void sendPacket(byte[] head, long connectionId,
453: HeaderSetImpl headers, boolean allHeaders)
454: throws IOException {
455: packetBegin(head);
456: packetAddConnectionID(connectionId, headers);
457: packetAddAuthResponses();
458: packetAddHeaders(headers);
459: if (allHeaders && !queuedHeaders.isEmpty()) {
460: queuedHeaders.removeAllElements();
461: throw new IOException("packet too large for peer");
462: }
463: packetEnd();
464: }
465:
466: private final void encodeInt(long val) {
467: buffer[packetLength++] = (byte) (val >> 24);
468: buffer[packetLength++] = (byte) (val >> 16);
469: buffer[packetLength++] = (byte) (val >> 8);
470: buffer[packetLength++] = (byte) val;
471: }
472:
473: /**
474: * Reads at least <code>length</code> bytes starting from the given offset
475: * into the internal buffer. More than <code>length</code> bytes may be
476: * actually read. The calling function must ensure the buffer is large
477: * enough to store the entire packet.
478: *
479: * @param offset starting offset in the destination buffer
480: * @param length minimum number of bytes to read
481: * @return number of bytes actually read
482: * @throws IOException if an I/O error occurs
483: */
484: private int readLeast(int offset, int length) throws IOException {
485: if (transport == null) {
486: throw new IOException("connection error");
487: }
488: int read = 0;
489: while (read < length) {
490: int count = transport.read(cache);
491: System.arraycopy(cache, 0, buffer, offset + read, count);
492: read += count;
493: if (read < length) {
494: try {
495: Thread.sleep(TRANSPORT_READ_INTERVAL);
496: } catch (InterruptedException e) {
497: throw new InterruptedIOException(e.getMessage());
498: }
499: }
500: }
501: return read;
502: }
503:
504: final void recvPacket() throws IOException {
505: authResponses.removeAllElements();
506: if (transport == null) {
507: throw new IOException("connection error");
508: }
509: try {
510: int read = readLeast(0, 3);
511: packetType = buffer[0] & 0xff;
512: packetLength = ((buffer[1] & 0xff) << 8)
513: + (buffer[2] & 0xff);
514: if (DEBUG) {
515: Logging.report(Logging.INFORMATION, 0, "Expecting "
516: + packetLength + " bytes to arrive...");
517: }
518: if (read < packetLength) {
519: readLeast(read, packetLength - read);
520: }
521:
522: // dump packet:
523: if (DEBUG) {
524: int len = packetLength;
525: if (!DEBUG2 && len > 20) {
526: len = 20;
527: }
528:
529: System.out.println("recv: ");
530: for (int i = 0; i < len; i++) {
531: System.out.print(" 0x"
532: + Integer.toHexString(buffer[i] & 0xFF)
533: .toUpperCase());
534: }
535: if (len != packetLength)
536: System.out.print("...");
537: System.out.println("");
538: }
539: } catch (IOException e) {
540: brokenLink();
541: throw e;
542: }
543: }
544:
545: private final void parseHeader(HeaderSetImpl headers)
546: throws IOException {
547: if (DEBUG) {
548: System.out.println("parseHeader()");
549: }
550: try {
551: int headerId = buffer[packetOffset++] & 0xff;
552: int inputType = headerId >> 6;
553: int outputType = HeaderSetImpl.internalType(headerId);
554: if (outputType != HeaderSetImpl.TYPE_UNSUPPORTED) {
555: inputType = outputType;
556: }
557:
558: Object result = null;
559:
560: switch (inputType) {
561: // ids which require special handling
562: case HeaderSetImpl.TYPE_SPECIAL_TIME_ISO:
563: try {
564: result = decodeTime8601();
565: } catch (IOException e) {
566: // IMPL_NOTE: Got invalid time header,
567: // should probably just ignore it.
568: }
569: break;
570:
571: case HeaderSetImpl.TYPE_SPECIAL_TIME_4:
572: long date = decodeInt();
573: Calendar cal = Calendar.getInstance(utcTimeZone);
574: cal.setTime(new Date(date * 1000L));
575: result = cal;
576: packetOffset += 4;
577: break;
578:
579: case HeaderSetImpl.TYPE_SPECIAL_TYPE:
580: int len = decodeLength16(packetOffset) - 3;
581: packetOffset += 2;
582: if (buffer[packetOffset + len - 1] != 0) {
583: throw new IOException("protocol error, "
584: + "type field not null terminated");
585: }
586: result = new String(buffer, packetOffset, len - 1,
587: "ISO-8859-1");
588: packetOffset += len;
589: break;
590:
591: // normal ids
592: case HeaderSetImpl.TYPE_LONG:
593: result = new Long(decodeInt());
594: packetOffset += 4;
595: break;
596:
597: case HeaderSetImpl.TYPE_UNICODE:
598: len = decodeLength16(packetOffset) - 3;
599: packetOffset += 2;
600: if (len < 2 || buffer[packetOffset + len - 1] != 0
601: || buffer[packetOffset + len - 2] != 0) {
602: throw new IOException("protocol error, "
603: + "unicode string is not null terminated");
604: }
605: result = new String(buffer, packetOffset, len - 2,
606: "UTF-16BE");
607: // result = new String(buffer, packetOffset, len,
608: // "ISO-8859-1");
609: packetOffset += len;
610: break;
611:
612: case HeaderSetImpl.TYPE_BYTEARRAY:
613: len = decodeLength16(packetOffset) - 3;
614: packetOffset += 2;
615: result = new byte[len];
616: System.arraycopy(buffer, packetOffset, result, 0, len);
617: packetOffset += len;
618: break;
619:
620: case HeaderSetImpl.TYPE_BYTE:
621: result = new Byte(buffer[packetOffset++]);
622: break;
623:
624: case HeaderSetImpl.TYPE_AUTH_CHALLENGE:
625: len = decodeLength16(packetOffset);
626: ObexAuth response = ObexAuth.parseAuthChallenge(buffer,
627: packetOffset - 1, len);
628: if (response != null)
629: authResponses.addElement(response);
630: packetOffset += len - 1;
631: return;
632:
633: case HeaderSetImpl.TYPE_AUTH_RESPONSE:
634: len = decodeLength16(packetOffset);
635: boolean good = ObexAuth.checkAuthResponse(buffer,
636: packetOffset - 1, len, this , authChallenges);
637: if (good)
638: authFailed = false;
639: packetOffset += len - 1;
640: if (DEBUG) {
641: System.out.println("checkAuthResponse() = " + good);
642: }
643: return;
644: }
645:
646: if (packetOffset > packetLength) {
647: throw new IOException("protocol error");
648: }
649:
650: if (outputType != HeaderSetImpl.TYPE_UNSUPPORTED) {
651: headers.setHeader(headerId, result);
652: } else if (DEBUG) {
653: System.out.println("unsupported header id = 0x"
654: + Integer.toHexString(headerId).toUpperCase());
655: }
656: } catch (ArrayIndexOutOfBoundsException e) {
657: throw new IOException("protocol error");
658: }
659: }
660:
661: /**
662: * Called when requested authentication failed.
663: * Implemented on server to call handler.
664: */
665: abstract void onAuthenticationFailure(byte[] username)
666: throws IOException;
667:
668: void onMissingAuthResponse() throws IOException {
669: }
670:
671: boolean shouldSendAuthResponse() {
672: return (packetType == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
673: && (authResponses.size() != 0);
674: }
675:
676: /**
677: * Parser all packet headers, BODY headers should not apear
678: * and silently ignored.
679: */
680: final void parsePacketHeaders(HeaderSetImpl headers, int offset)
681: throws IOException {
682: if (DEBUG) {
683: System.out.println("parsePacketHeaders()");
684: }
685:
686: packetOffset = offset;
687: headers.packetType = buffer[0] & 0xFF;
688:
689: parseConnectionID();
690:
691: while (packetOffset != packetLength) {
692: parseHeader(headers);
693: }
694: parseEnd();
695: }
696:
697: private final void parseConnectionID() {
698:
699: int headerId = buffer[packetOffset] & 0xFF;
700: // parse connection ID
701: if (packetOffset + 5 > packetLength
702: || headerId != HEADER_CONNECTION_ID) {
703: return;
704: }
705: packetOffset++;
706: long id = decodeInt();
707: packetOffset += 4;
708: setConnectionID(id);
709: }
710:
711: public abstract void setConnectionID(long id);
712:
713: public abstract long getConnectionID();
714:
715: /**
716: * Begin parsing packet headers in packet possibly containing BODY
717: * (data fields).
718: */
719: final void parsePacketDataBegin(HeaderSetImpl headers, int offset) {
720: if (DEBUG) {
721: System.out.println("parsePacketDataBegin()");
722: }
723: packetOffset = offset;
724: headers.packetType = buffer[0] & 0xFF;
725:
726: parseConnectionID();
727: dataOffset = packetOffset;
728: }
729:
730: /**
731: * Parse packet headers, put BODY field content (data) in specified output
732: * array. If output is null, search for a BODY block and return 1 if it is
733: * found.
734: * @return number of bytes put in output.
735: */
736: final int parsePacketData(HeaderSetImpl headers, byte[] output,
737: int outputOffset, int outputLength) throws IOException {
738: if (DEBUG2) {
739: System.out.println("parsePacketData()");
740: }
741: int result = 0;
742: while (true) {
743: int len = packetOffset - dataOffset;
744: if (DEBUG2) {
745: System.out.print("packetOffset = " + packetOffset
746: + " dataOffset = " + dataOffset);
747: System.out.println(" len = " + len);
748: }
749: if (len > 0) {
750: if (output == null) {
751: // special case for serching first data block
752: // without actual read
753: return 1;
754: }
755: if (len > outputLength)
756: len = outputLength;
757: System.arraycopy(buffer, dataOffset, output,
758: outputOffset, len);
759: outputOffset += len;
760: outputLength -= len;
761: dataOffset += len;
762: result += len;
763: if (outputLength == 0)
764: return result;
765: continue;
766: }
767:
768: if (DEBUG) {
769: System.out.println("packetOffset = " + packetOffset
770: + " packetLength = " + packetLength);
771: }
772: if (packetOffset == packetLength) {
773: return result;
774: }
775: int headerId = buffer[packetOffset] & 0xff;
776:
777: if (headerId == HEADER_BODY || headerId == HEADER_EOFBODY) {
778: isEof = (headerId == HEADER_EOFBODY);
779: dataOffset = packetOffset + 3;
780: int length = decodeLength16(packetOffset + 1);
781: if (packetOffset + length > packetLength) {
782: throw new IOException("protocol error");
783: }
784: packetOffset += length;
785: continue;
786: }
787:
788: parseHeader(headers);
789: dataOffset = packetOffset;
790: }
791: }
792:
793: final void parseEnd() throws IOException {
794: if (DEBUG) {
795: System.out.println("parseEnd()");
796: }
797: if (authFailed) {
798: authFailed = false;
799: onMissingAuthResponse();
800: }
801: }
802:
803: final int decodeLength16(int off) {
804: return ((((int) buffer[off]) & 0xFF) << 8)
805: + (((int) buffer[off + 1]) & 0xFF);
806: }
807:
808: private final long decodeInt() {
809: return ((buffer[packetOffset + 0] & 0xffl) << 24)
810: + ((buffer[packetOffset + 1] & 0xffl) << 16)
811: + ((buffer[packetOffset + 2] & 0xffl) << 8)
812: + (buffer[packetOffset + 3] & 0xffl);
813: }
814:
815: private final Calendar decodeTime8601() throws IOException {
816: int year, month, date, hour, minute, second;
817:
818: int len = decodeLength16(packetOffset) - 3;
819: packetOffset += 2;
820:
821: if (len < 15 || len > 16 || buffer[packetOffset + 8] != 0x54 // 'T'
822: || (len == 16 && buffer[packetOffset + 15] != 0x5A)) { // 'Z'
823: packetOffset += len;
824: throw new IOException("corrupted time header");
825: }
826: for (int i = 0; i < 14; i++) {
827: if (i == 8)
828: continue;
829: int chr = buffer[packetOffset + i] - 0x30; // '0'
830: if (chr < 0 || chr > 9) {
831: packetOffset += len;
832: throw new IOException("corrupted time header");
833: }
834: }
835:
836: year = (buffer[packetOffset + 0] - 0x30) * 1000
837: + (buffer[packetOffset + 1] - 0x30) * 100
838: + (buffer[packetOffset + 2] - 0x30) * 10
839: + (buffer[packetOffset + 3] - 0x30);
840: month = (buffer[packetOffset + 4] - 0x30) * 10
841: + (buffer[packetOffset + 5] - 0x30);
842: date = (buffer[packetOffset + 6] - 0x30) * 10
843: + (buffer[packetOffset + 7] - 0x30);
844:
845: hour = (buffer[packetOffset + 9] - 0x30) * 10
846: + (buffer[packetOffset + 10] - 0x30);
847: minute = (buffer[packetOffset + 11] - 0x30) * 10
848: + (buffer[packetOffset + 12] - 0x30);
849: second = (buffer[packetOffset + 13] - 0x30) * 10
850: + (buffer[packetOffset + 14] - 0x30);
851:
852: // is check validness of time fields required?
853:
854: Calendar cal;
855: // 'len' value 15 means local time,
856: // 16 means UTC (in this case time string has 'Z' suffix)
857: if (len == 16) {
858: cal = Calendar.getInstance(utcTimeZone);
859: } else {
860: cal = Calendar.getInstance();
861: }
862: cal.set(Calendar.YEAR, year);
863: cal.set(Calendar.MONTH, month - 1); // Calendar.JANUARY = 0
864: cal.set(Calendar.DATE, date);
865:
866: // ISO 8601 standard uses the 24-hour clock
867: // Therefore you use HOUR_OF_DAY not HOUR
868: cal.set(Calendar.HOUR_OF_DAY, hour);
869:
870: cal.set(Calendar.MINUTE, minute);
871: cal.set(Calendar.SECOND, second);
872:
873: // set milliseconds to zero since
874: // ISO 8601 uses only second precision
875: cal.set(Calendar.MILLISECOND, 0);
876:
877: packetOffset += len;
878: return cal;
879: }
880:
881: static int validateStatus(int status) {
882: switch (status) {
883: case ResponseCodes.OBEX_DATABASE_FULL:
884: case ResponseCodes.OBEX_DATABASE_LOCKED:
885: case ResponseCodes.OBEX_HTTP_ACCEPTED:
886: case ResponseCodes.OBEX_HTTP_BAD_GATEWAY:
887: case ResponseCodes.OBEX_HTTP_BAD_METHOD:
888: case ResponseCodes.OBEX_HTTP_BAD_REQUEST:
889: case ResponseCodes.OBEX_HTTP_CONFLICT:
890: case ResponseCodes.OBEX_HTTP_CREATED:
891: case ResponseCodes.OBEX_HTTP_ENTITY_TOO_LARGE:
892: case ResponseCodes.OBEX_HTTP_FORBIDDEN:
893: case ResponseCodes.OBEX_HTTP_GATEWAY_TIMEOUT:
894: case ResponseCodes.OBEX_HTTP_GONE:
895: case ResponseCodes.OBEX_HTTP_INTERNAL_ERROR:
896: case ResponseCodes.OBEX_HTTP_LENGTH_REQUIRED:
897: case ResponseCodes.OBEX_HTTP_MOVED_PERM:
898: case ResponseCodes.OBEX_HTTP_MOVED_TEMP:
899: case ResponseCodes.OBEX_HTTP_MULT_CHOICE:
900: case ResponseCodes.OBEX_HTTP_NO_CONTENT:
901: case ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE:
902: case ResponseCodes.OBEX_HTTP_NOT_AUTHORITATIVE:
903: case ResponseCodes.OBEX_HTTP_NOT_FOUND:
904: case ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED:
905: case ResponseCodes.OBEX_HTTP_NOT_MODIFIED:
906: case ResponseCodes.OBEX_HTTP_OK:
907: case ResponseCodes.OBEX_HTTP_PARTIAL:
908: case ResponseCodes.OBEX_HTTP_PAYMENT_REQUIRED:
909: case ResponseCodes.OBEX_HTTP_PRECON_FAILED:
910: case ResponseCodes.OBEX_HTTP_PROXY_AUTH:
911: case ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE:
912: case ResponseCodes.OBEX_HTTP_RESET:
913: case ResponseCodes.OBEX_HTTP_SEE_OTHER:
914: case ResponseCodes.OBEX_HTTP_TIMEOUT:
915: case ResponseCodes.OBEX_HTTP_UNAUTHORIZED:
916: case ResponseCodes.OBEX_HTTP_UNAVAILABLE:
917: case ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE:
918: case ResponseCodes.OBEX_HTTP_USE_PROXY:
919: case ResponseCodes.OBEX_HTTP_VERSION:
920: return status;
921: default:
922: return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
923: }
924: }
925: }
|