summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsc2000-12-04 13:20:06 +0000
committersc2000-12-04 13:20:06 +0000
commit7caee026d7e303d18afb3a23bd05f44d83f176ab (patch)
tree8a3987cef018b130121831835375d751fd9c5939
parent3ce8eb18e2859e9e1531f20bc477abe7151b11f4 (diff)
downloadivy-league-7caee026d7e303d18afb3a23bd05f44d83f176ab.zip
ivy-league-7caee026d7e303d18afb3a23bd05f44d83f176ab.tar.gz
ivy-league-7caee026d7e303d18afb3a23bd05f44d83f176ab.tar.bz2
ivy-league-7caee026d7e303d18afb3a23bd05f44d83f176ab.tar.xz
First implementation of the OBEX protocol (mainly for IrDA)
-rw-r--r--comm/ObexStream.cc629
-rw-r--r--comm/ObexStream.h97
2 files changed, 726 insertions, 0 deletions
diff --git a/comm/ObexStream.cc b/comm/ObexStream.cc
new file mode 100644
index 0000000..d70caa1
--- /dev/null
+++ b/comm/ObexStream.cc
@@ -0,0 +1,629 @@
+/*
+ * Ivy League
+ *
+ * OBEX protocol
+ *
+ * Copyright 2000
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * by Stephane Chatty
+ *
+ * $Id$
+ *
+ */
+
+#include "ObexStream.h"
+#include "error.h"
+#include <ostream.h>
+
+#include <stdio.h>
+
+IvlObexStream :: IvlObexStream (IvlAddress* a, IvlBaseScheduler* mpx)
+: IvlStream (a)
+{
+ if (Listen () < 0) {
+ SysError (ErrWarn, "IvlObexStream::IvlObexStream: Listen");
+ } else {
+ SetMode (IORead);
+ Add (mpx);
+ }
+}
+
+IvlObexStream :: ~IvlObexStream ()
+{
+}
+
+void
+IvlObexStream :: HandleRead ()
+{
+ int fd = Accept ();
+ if (fd < 0) {
+ SysError (ErrWarn, "IvlObexStream::HandleRead: Accept");
+ return;
+ }
+
+ IvlObexAgent* cl = new IvlObexAgent (fd, this);
+}
+
+#if 0
+void
+IvlObexStream :: DiscoverDevices ()
+{
+}
+#endif
+
+/******************************************************************************/
+
+/* OBEX request codes */
+#define OBEX_FINAL_BIT 0x80
+#define OBEX_CONNECT_REQ 0x00
+#define OBEX_DISCONNECT_REQ 0x01
+#define OBEX_PUT_REQ 0x02
+#define OBEX_GET_REQ 0x03
+#define OBEX_COMMAND_REQ 0x04
+#define OBEX_SETPATH_REQ 0x05
+#define OBEX_ABORT_REQ 0x7f
+
+/* OBEX headers encoding */
+#define OBEX_HI_MASK 0xc0
+#define OBEX_UNICODE_HDR 0x00
+#define OBEX_STRING_HDR 0x40
+#define OBEX_BYTE_HDR 0x80
+#define OBEX_INT_HDR 0xc0
+
+/* Version 1.1 */
+#define OBEX_VERSION 0x11
+
+/* OBEX response codes*/
+#define OBEX_CONTINUE_RSP 0x10
+#define OBEX_SWITCH_PRO_RSP 0x11
+#define OBEX_SUCCESS_RSP 0x20
+#define OBEX_CREATED_RSP 0x21
+#define OBEX_ACCEPTED_RSP 0x22
+#define OBEX_NO_CONTENT_RSP 0x24
+#define OBEX_BAD_REQUEST_RSP 0x40
+#define OBEX_UNAUTHORIZED_RSP 0x41
+#define OBEX_PAYMENT_REQUIRED_RSP 0x42
+#define OBEX_FORBIDDEN_RSP 0x43
+#define OBEX_NOT_FOUND_RSP 0x44
+#define OBEX_METHOD_NOT_ALLOWED_RSP 0x45
+#define OBEX_CONFLICT_RSP 0x49
+#define OBEX_INTERNAL_SERVER_ERROR_RSP 0x50
+#define OBEX_NOT_IMPLEMENTED_RSP 0x51
+#define OBEX_DATABASE_FULL_RSP 0x60
+#define OBEX_DATABASE_LOCKED_RSP 0x61
+
+/* OBEX header types */
+#define OBEX_HEADER_COUNT 0x00 /* Number of objects (used by connect) */
+#define OBEX_HEADER_NAME 0x01 /* Name of the object */
+#define OBEX_HEADER_TYPE 0x02 /* Type of the object */
+#define OBEX_HEADER_LENGTH 0x03 /* Total lenght of object */
+#define OBEX_HEADER_TIME 0x04 /* Last modification of object */
+#define OBEX_HEADER_DESCRIPTION 0x05 /* Description of object */
+#define OBEX_HEADER_TARGET 0x06 /* Identifies the target for the object */
+#define OBEX_HEADER_HTTP 0x07 /* an HTTP 1.x header */
+#define OBEX_HEADER_BODY 0x08 /* Data part of the object */
+#define OBEX_HEADER_BODY_END 0x09 /* Last data part of the object */
+#define OBEX_HEADER_WHO 0x0a /* Identifies the sender of the object */
+#define OBEX_HEADER_CONNECTIONID 0x0b /* Connection identifier */
+#define OBEX_HEADER_PARAMS 0x0c /* App. parameters */
+#define OBEX_HEADER_CHALLENGE 0x0d /* Auth. challenge */
+#define OBEX_HEADER_RESPONSE 0x0e /* Auth. response */
+#define OBEX_HEADER_CLASS 0x0f /* Object class */
+
+
+class IvlObexHeader {
+public:
+ byte Hi;
+ lword Length;
+ lword Offset;
+ void* Buf;
+ IvlObexHeader (byte hi, lword l, lword o) : Hi (hi), Length (l), Offset (o) {}
+};
+
+IvlObexAgent :: IvlObexAgent (int fd, IvlObexStream* l)
+: IvlBufStream (),
+ MyLink (l),
+ CurObj (0)
+{
+ SetMode (IORead);
+ IvlChannel::Open (fd);
+ Add (l->GetScheduler ());
+ OutSize = 255;
+}
+
+/* this is a problem: how do I make sure that agents created this way (after a
+DiscoverDevices) are not also created when peers connect to me? Looks similar to
+the situation with Ivy agents. */
+IvlObexAgent :: IvlObexAgent (IvlAddress* a, IvlObexStream* l)
+: IvlBufStream (0, a),
+ MyLink (l),
+ CurObj (0)
+{
+ if (!Setup ()) {
+ cerr << "Obex warning. Cannot set up stream: ";
+ perror ("");
+ return;
+ }
+ SetMode (IORead);
+ Add (l->GetScheduler ());
+ OutSize = 255;
+}
+
+IvlObexAgent :: ~IvlObexAgent ()
+{
+}
+
+
+void
+IvlObexAgent :: HandleRead ()
+{
+ if (ReadInput () <= 0) {
+ /* could emit a OBEX_EV_LINKERR event to the upper software layers */
+ return;
+ }
+
+ /* if not enough data for reading packet length, wait for future input */
+ if (InBuffer.BufLength () < 3)
+ return;
+
+ sword len;
+ InBuffer.PeekShort (len, 1);
+ len = ntohs (len);
+
+ /* if packet is not complete, wait for future input */
+ if (InBuffer.BufLength () < len)
+ return;
+
+
+ /* should add tests depending on what we are expecting */
+
+ /* read operation code and skip packet length */
+ byte reqcode;
+ InBuffer.ReadByte (reqcode);
+ InBuffer.Flush (2);
+
+ /* extract 'final' bit from reqcode */
+ bool final = reqcode & OBEX_FINAL_BIT;
+ reqcode = reqcode & ~OBEX_FINAL_BIT;
+
+ switch (reqcode) {
+ case OBEX_CONNECT_REQ:
+ ParseConnect (len, final);
+ break;
+ case OBEX_DISCONNECT_REQ:
+ ParseDisconnect (len, final);
+ break;
+ case OBEX_ABORT_REQ:
+ ParseAbort (len, final);
+ break;
+ case OBEX_PUT_REQ:
+ ParsePut (len, final);
+ break;
+ case OBEX_GET_REQ:
+ ParseGet (len, final);
+ break;
+ case OBEX_SETPATH_REQ:
+ ParseSetPath (len, final);
+ break;
+ case OBEX_CONTINUE_RSP:
+ cerr << "--> continue.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_SWITCH_PRO_RSP:
+ cerr << "--> switch protocol.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_SUCCESS_RSP:
+ cerr << "--> success.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_CREATED_RSP:
+ cerr << "--> created.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_ACCEPTED_RSP:
+ cerr << "--> accepted.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_NO_CONTENT_RSP:
+ cerr << "--> no content.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_BAD_REQUEST_RSP:
+ cerr << "--> bad request.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_UNAUTHORIZED_RSP:
+ cerr << "--> unauthorized.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_PAYMENT_REQUIRED_RSP:
+ cerr << "--> payment required.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_FORBIDDEN_RSP:
+ cerr << "--> forbidden.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_NOT_FOUND_RSP:
+ cerr << "--> not found.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_METHOD_NOT_ALLOWED_RSP:
+ cerr << "--> not allowed.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_CONFLICT_RSP:
+ cerr << "--> conflict.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_INTERNAL_SERVER_ERROR_RSP:
+ cerr << "--> server error.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_NOT_IMPLEMENTED_RSP:
+ cerr << "--> not implemented.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_DATABASE_FULL_RSP:
+ cerr << "--> database full.\n";
+ ParseHeaders (len, final);
+ break;
+ case OBEX_DATABASE_LOCKED_RSP:
+ cerr << "--> database locked.\n";
+ ParseHeaders (len, final);
+ break;
+ default:
+ cerr << "unexpected request!\n";
+ break;
+ }
+}
+
+#if 0
+ /* if this is the first packet of an object */
+ if (!CurObj) {
+ cerr << "new object\n";
+ /* allocate new object
+ or deliver OBEX_INTERNAL_SERVER_ERROR_RSP to peer */
+
+ DecodeObjectPart ();
+
+ if (!final)
+ /* should deliver OBEX_EV_REQHINT to upper layers */
+ ;
+ } else {
+ DecodeObjectPart ();
+ }
+
+#endif
+
+void
+IvlObexAgent :: ParseConnect (sword len, bool final)
+{
+ if (len < 4) {
+ cerr << "IvlObexAgent: malformed connection header\n";
+ /* should send OBEX_BAD_REQUEST_RSP to peer */
+ /* could deliver OBEX_EV_PARSEERR to the upper software layers */
+ InBuffer.Flush ();
+ return;
+ }
+
+ /*** faudrait aussi traiter le cas ou il n'y a pas de Connect au
+ debut de la connexion ? */
+
+ byte version;
+ byte flags;
+ sword max_packet_length;
+ InBuffer.ReadByte (version);
+ InBuffer.ReadByte (flags);
+ InBuffer.ReadShort (max_packet_length);
+ OutSize = ntohs (max_packet_length);
+
+ cerr << "--> connect: version " << (version >> 4) << "." << (version & 0x0f)
+ << ", " << OutSize << " bytes packets max\n";
+
+ IvlEvent* ev = new IvlObexAgentEvent (this, IvlObexAgentEvent::ObexNewAgent);
+ MyLink->NewAgents.Dispatch (*ev);
+
+ ParseHeaders (len, final);
+
+
+ /* this will have to be improved, of course,
+ but the response to Connect is not like other responses */
+ OutBuffer.WriteByte (OBEX_SUCCESS_RSP | OBEX_FINAL_BIT);
+ OutBuffer.WriteShort (htons (7));
+ OutBuffer.WriteByte (OBEX_VERSION);
+ OutBuffer.WriteByte (0);
+ OutBuffer.WriteShort (htons (1024));
+ SendResponse ();
+
+}
+
+void
+IvlObexAgent :: ParseDisconnect (sword len, bool final)
+{
+ cerr << "--> disconnect.\n";
+ InBuffer.Flush ();
+ OutBuffer.WriteByte (OBEX_SUCCESS_RSP | OBEX_FINAL_BIT);
+ OutBuffer.WriteShort (htons (3));
+ SendResponse ();
+ OutSize = 255;
+}
+
+void
+IvlObexAgent :: ParseAbort (sword len, bool final)
+{
+ cerr << "--> abort.\n";
+ InBuffer.Flush ();
+}
+
+void
+IvlObexAgent :: ParsePut (sword len, bool final)
+{
+ cerr << "--> put " << len << " bytes " << (final ? "!\n" : ".. \n");
+ ParseHeaders (len, final);
+ byte rsp = final ? OBEX_SUCCESS_RSP : OBEX_CONTINUE_RSP;
+ OutBuffer.WriteByte (rsp | OBEX_FINAL_BIT);
+ OutBuffer.WriteShort (htons (3));
+ SendResponse ();
+}
+
+void
+IvlObexAgent :: ParseGet (sword len, bool final)
+{
+ cerr << "--> get.\n";
+ InBuffer.Flush ();
+}
+
+void
+IvlObexAgent :: ParseSetPath (sword len, bool final)
+{
+ cerr << "--> setpath.\n";
+ InBuffer.Flush ();
+}
+
+void
+IvlObexAgent :: ParseHeaders (sword, bool)
+{
+ while (InBuffer.BufLength () > 0) {
+ byte hi;
+ InBuffer.ReadByte (hi);
+
+ switch (hi & ~OBEX_HI_MASK) {
+ case OBEX_HEADER_COUNT:
+ cerr << "COUNT:\n";
+ break;
+ case OBEX_HEADER_NAME:
+ cerr << "NAME:\n";
+ break;
+ case OBEX_HEADER_TYPE:
+ cerr << "TYPE:\n";
+ break;
+ case OBEX_HEADER_LENGTH:
+ cerr << "LENGTH:\n";
+ break;
+ case OBEX_HEADER_TIME:
+ cerr << "TIME:\n";
+ break;
+ case OBEX_HEADER_DESCRIPTION:
+ cerr << "DESCR:\n";
+ break;
+ case OBEX_HEADER_TARGET:
+ cerr << "TARGET:\n";
+ break;
+ case OBEX_HEADER_HTTP:
+ cerr << "HTTP:\n";
+ break;
+ case OBEX_HEADER_BODY:
+ cerr << "BODY:\n";
+ break;
+ case OBEX_HEADER_BODY_END:
+ cerr << "BODY END:\n";
+ break;
+ case OBEX_HEADER_WHO:
+ cerr << "WHO:\n";
+ break;
+ case OBEX_HEADER_CONNECTIONID:
+ cerr << "CONNECTIONID\n";
+ break;
+ case OBEX_HEADER_PARAMS:
+ cerr << "PQRQMS\n";
+ break;
+ case OBEX_HEADER_CHALLENGE:
+ cerr << "CHALLENGE\n";
+ break;
+ case OBEX_HEADER_RESPONSE:
+ cerr << "RESPONSE:\n";
+ break;
+ case OBEX_HEADER_CLASS:
+ cerr << "CLASS\n";
+ break;
+ default:
+ cerr << "UNKNOWN:\n";
+ }
+ switch (hi & OBEX_HI_MASK) {
+ case OBEX_UNICODE_HDR:
+ {
+ sword hlen;
+ InBuffer.ReadShort (hlen);
+ hlen = ntohs (hlen);
+ byte buf[hlen-3];
+ InBuffer.ReadBuf (buf, hlen-3);
+ int l = hlen/2 + 1;
+ unsigned char str[l];
+ unsigned char* p = str;
+ unsigned char* q = buf+1;
+ while (*p = *q++) {
+ p++;
+ q++;
+ }
+ cerr << "Unicode header, " << hlen - 3 << " bytes: \"" << str << "\"\n";
+ break;
+ }
+ case OBEX_STRING_HDR:
+ {
+ sword hlen;
+ InBuffer.ReadShort (hlen);
+ hlen = ntohs (hlen);
+ byte buf[hlen-3+1]; // on ajoute un pour l'impression de debug
+ InBuffer.ReadBuf (buf, hlen-3);
+ buf[hlen-3] = '\0'; // pour l'impression de debug
+ cerr << "String header, " << hlen - 3 << " bytes: \"" << (char*) buf << "\"\n";
+ break;
+ }
+ case OBEX_INT_HDR:
+ {
+ lword l;
+ InBuffer.ReadLong (l);
+ l = ntohl (l);
+ cerr << "Int-long header: 0x" << hex (l) << "\n";
+ break;
+ }
+ case OBEX_BYTE_HDR:
+ {
+ byte b;
+ InBuffer.ReadByte (b);
+ cerr << "Char-long header: 0x" << hex (b) << "\n";
+ break;
+ }
+ default:
+ cerr << "bad header 0x" << hex (hi) << "\n";
+
+ }
+ }
+
+ /* if not correctly parsed object, then
+ deliver OBEX_BAD_REQUEST_RSP to peer
+ and deliver OBEX_EV_PARSEERR to upper soft layers*/
+}
+
+void
+IvlObexAgent :: SendResponse ()
+{
+ Flush ();
+}
+
+void
+IvlObexAgent :: SendHeader (byte hi, byte encoding, const char* hv)
+{
+ OutBuffer.WriteByte (hi | encoding);
+
+ switch (encoding) {
+ case OBEX_UNICODE_HDR:
+ {
+ int len = 2 * (strlen (hv) + 1);
+ OutBuffer.WriteShort (htons (3 + len));
+ byte buf[len];
+ byte* p = buf;
+ while (*hv) {
+ *p++ = '\0';
+ *p++ = *hv++;
+ }
+ *p++ = '\0';
+ *p++ = '\0';
+ OutBuffer.WriteBuf ((const byte*) buf, len);
+ break;
+ }
+ break;
+ case OBEX_STRING_HDR:
+ {
+ int len = strlen (hv) + 1;
+ OutBuffer.WriteShort (htons (3 + len));
+ OutBuffer.WriteBuf ((const byte*) hv, len);
+ break;
+ }
+ case OBEX_BYTE_HDR:
+ OutBuffer.WriteByte ((byte) hv);
+ break;
+ case OBEX_INT_HDR:
+ OutBuffer.WriteLong (htonl ((lword) hv));
+ break;
+ }
+}
+
+
+void
+IvlObexAgent :: SendObject (IvlObexObject& o)
+{
+ /* faudrait d'abord envoyer la connexion ! */
+ OutBuffer.WriteByte (OBEX_PUT_REQ | OBEX_FINAL_BIT);
+ int l = 3 + (1 + 4) + (3 + 2 * (strlen (o.Name) + 1)) + (3 + strlen (o.Body) + 1);
+ OutBuffer.WriteShort (htons (l));// FIXME!!!!
+
+ if (o.Class)
+ SendHeader (OBEX_HEADER_CLASS, OBEX_INT_HDR, (const char*) o.Class);
+
+ if (o.Name)
+ SendHeader (OBEX_HEADER_NAME, OBEX_UNICODE_HDR, o.Name);
+
+ if (o.Description)
+ SendHeader (OBEX_HEADER_DESCRIPTION, OBEX_STRING_HDR, o.Description);
+
+ if (o.Body)
+ SendHeader (OBEX_HEADER_BODY_END, OBEX_STRING_HDR, o.Body);
+
+
+#if 0
+ /* a faire a terme : decouper l'emission en plusieurs paquets, dont
+ un avec un BODY_END vide (cf doc page 29) */
+ /* et puis aussi ajouter la deconnexion */
+
+ OutBuffer.WriteByte (OBEX_PUT_REQ | OBEX_FINAL_BIT);
+ OutBuffer.WriteShort (htons (3));
+ SendHeader (OBEX_HEADER_BODY_END, 0);
+#endif
+ SendResponse ();
+}
+
+
+IvlEventType* IvlObexAgentEvent::ObexNewAgent = new IvlEventType ("new agent", 0, 0);
+IvlEventType* IvlObexAgentEvent::ObexAgentBye = new IvlEventType ("agent bye", 0, 0);
+
+IvlObexAgentEvent :: IvlObexAgentEvent (IvlObexAgent* a, IvlEventType* t)
+: IvlEvent (t),
+ Agent (a)
+{
+}
+
+IvlObexAgentEvent :: ~IvlObexAgentEvent ()
+{
+}
+
+
+IvlObexObject :: IvlObexObject ()
+: Name (),
+ Description (),
+ Body (),
+ Class (0)
+{
+}
+
+IvlObexObject :: ~IvlObexObject ()
+{
+}
+
+void
+IvlObexObject :: SetName (const char* n)
+{
+ Name = n;
+}
+
+void
+IvlObexObject :: SetDescription (const char* d)
+{
+ Description = d;
+}
+
+void
+IvlObexObject :: SetBody (const char* b)
+{
+ Body = b;
+}
+
+void
+IvlObexObject :: SetClass (lword c)
+{
+ Class = c;
+}
+
diff --git a/comm/ObexStream.h b/comm/ObexStream.h
new file mode 100644
index 0000000..6e83013
--- /dev/null
+++ b/comm/ObexStream.h
@@ -0,0 +1,97 @@
+/*
+ * Ivy League
+ *
+ * OBEX protocol
+ *
+ * Copyright 2000
+ * Centre d'Etudes de la Navigation Aerienne (CENA)
+ *
+ * by Stephane Chatty
+ *
+ * $Id$
+ *
+ */
+
+#ifndef ObexStream_H_
+#define ObexStream_H_
+
+#include "ivl/cplus_bugs.h"
+#include "Stream.h"
+class IvlAddress;
+#include "ivl/List.h"
+#include "Trigger.h"
+#include "Scheduler.h"
+#include "BufStream.h"
+#include "Event.h"
+
+
+class IvlObexStream : public IvlStream {
+public:
+ IvlTrigger NewAgents;
+
+ IvlObexStream (IvlAddress*, IvlBaseScheduler* = IvlScd);
+ ~IvlObexStream ();
+
+ IvlChannel* Copy () const;
+ void HandleRead ();
+};
+
+class IvlObexHeader;
+class IvlObexObject;
+
+class IvlObexAgent : public IvlBufStream {
+private:
+ IvlObexStream* MyLink;
+ void* CurObj;
+ IvlListOf<IvlObexHeader*> OutHeaders;
+
+ void HandleRead ();
+ void ParseConnect (sword, bool);
+ void ParseDisconnect (sword, bool);
+ void ParseAbort (sword, bool);
+ void ParsePut (sword, bool);
+ void ParseGet (sword, bool);
+ void ParseSetPath (sword, bool);
+ void ParseHeaders (sword, bool);
+ void SendResponse ();
+ void SendHeader (byte, byte, const char*);
+
+public:
+ IvlObexAgent (int, IvlObexStream*);
+ IvlObexAgent (IvlAddress*, IvlObexStream*);
+ ~IvlObexAgent ();
+ void SendObject (IvlObexObject&);
+};
+
+class IvlObexAgentEvent : public IvlEvent {
+public:
+static IvlEventType* ObexNewAgent, *ObexAgentBye;
+
+protected:
+ IvlObexAgent* Agent;
+
+public:
+ IvlObexAgentEvent (IvlObexAgent*, IvlEventType*);
+ ~IvlObexAgentEvent ();
+
+inline IvlObexAgent* GetAgent () const { return Agent;}
+};
+
+class IvlObexObject {
+friend class IvlObexAgent;
+protected:
+ IvlString Name;
+ IvlString Description;
+ IvlString Body; // should not be a text string, actually
+ lword Class;
+
+public:
+ IvlObexObject ();
+ ~IvlObexObject ();
+ void SetName (const char*);
+ void SetDescription (const char*);
+ void SetBody (const char*);
+ void SetClass (lword);
+};
+
+#endif /* ObexStream_H_ */