/* * 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 #include 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; }