/* * Copyright (c) 2001 Tommy Bohlin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* obexsrv.c * * 2do: * - GET command? * - HINT_FILE_SERVER? */ #include #include #include /********************************************************************** * Constants **********************************************************************/ static const char id_server[]="obex server"; static const char id_connection[]="obex server connection"; static const char id_inbuf[]="obex server inbuf"; static const char id_collbuf[]="obex server collbuf"; static const char id_outbuf[]="obex server outbuf"; /********************************************************************** * Data structures **********************************************************************/ typedef struct OBEXServerPrivate { OBEXServer obex; Object* obexObject; Object* xferObject; LSAP* lsap; struct OBEXConnection* connections; } OBEXServerPrivate; typedef struct OBEXConnection { struct OBEXConnection* next; OBEXServerPrivate* obex; int maxSendSize; int op; int chunkLength; int chunkCode; int outOfs; int outLength; int outMax; u_char* outBuf; int inLength; int inMax; u_char* inBuf; int collPos; int collLen; int collOn; u_char* collBuf; } OBEXConnection; /********************************************************************** * Internal functions **********************************************************************/ static void sendConnectResponse(Connection* con) { u_char hdr[7]; hdr[0]=RES_SUCCESS|OBEX_LAST; putBEShort(hdr+1,7); hdr[3]=OBEX_VERSION; hdr[4]=0; /* NB: Stick to max frame size for now. We may want to increase * it later if it works for other devices. Max OBEX size is 65535 */ putBEShort(hdr+5,connGetRecvDataSize(con)); connWrite(con,hdr,7); } static void sendSuccess(Connection* con) { u_char hdr[3]; hdr[0]=RES_SUCCESS|OBEX_LAST; putBEShort(hdr+1,3); connWrite(con,hdr,3); } static void sendContinue(Connection* con) { u_char hdr[3]; hdr[0]=RES_CONTINUE|OBEX_LAST; putBEShort(hdr+1,3); connWrite(con,hdr,3); } static void sendBadRequest(Connection* con) { u_char hdr[3]; hdr[0]=RES_BAD_REQUEST|OBEX_LAST; putBEShort(hdr+1,3); connWrite(con,hdr,3); } static void sendNotImplemented(Connection* con) { u_char hdr[3]; hdr[0]=RES_NOT_IMPLEMENTED|OBEX_LAST; putBEShort(hdr+1,3); connWrite(con,hdr,3); } static int headerLength(OBEXConnection* oc, int ofs) { int len=oc->outLength-ofs; switch(oc->outBuf[ofs]&TYPE_MASK) { case TYPE_UNICODE: if(len>2) { int l1=getBEShort(oc->outBuf+ofs+1); if(l1<5) l1=5; if(len>l1) len=l1; } break; case TYPE_BYTES: if(len>2) { int l1=getBEShort(oc->outBuf+ofs+1); if(l1<3) l1=3; if(len>l1) len=l1; } break; case TYPE_INT1: if(len>2) len=2; break; case TYPE_INT4: if(len>5) len=5; break; } return len; } static void sendChunk(Connection* con, OBEXConnection* oc) { u_char hdr[6]; if(oc->chunkLength>0) { int l=oc->outOfs+oc->chunkLength; while(oc->outLength-l>=3) { int l1=l+headerLength(oc,l); if(l1-oc->outOfs+6>oc->maxSendSize) break; l=l1; } if(l-oc->outOfs+6>oc->maxSendSize) { hdr[0]=OP_PUT; putBEShort(hdr+1,oc->maxSendSize); hdr[3]=oc->chunkCode==(HI_END_OF_BODY|TYPE_BYTES) ? (HI_BODY|TYPE_BYTES) : oc->chunkCode; putBEShort(hdr+4,oc->maxSendSize-3); connWrite2(con,hdr,6,oc->outBuf+oc->outOfs,oc->maxSendSize-6); oc->chunkLength-=oc->maxSendSize-6; oc->outOfs+=oc->maxSendSize-6; } else { hdr[0]=OP_PUT; if(l==oc->outLength) hdr[0]|=OBEX_LAST; putBEShort(hdr+1,l-oc->outOfs+6); hdr[3]=oc->chunkCode; putBEShort(hdr+4,oc->chunkLength+3); connWrite2(con,hdr,6,oc->outBuf+oc->outOfs,l-oc->outOfs); oc->chunkLength=0; oc->outOfs=l; } } else { int l=oc->outOfs; while(oc->outLength-l>=3) { int l1=l+headerLength(oc,l); if(l1-oc->outOfs+3>oc->maxSendSize) break; l=l1; } if(l>oc->outOfs) { hdr[0]=OP_PUT; if(l==oc->outLength) hdr[0]|=OBEX_LAST; putBEShort(hdr+1,l-oc->outOfs+3); connWrite2(con,hdr,3,oc->outBuf+oc->outOfs,l-oc->outOfs); oc->outOfs=l; } else if(oc->outLength-oc->outOfs>=3) { oc->chunkCode=oc->outBuf[oc->outOfs]; oc->chunkLength=headerLength(oc,oc->outOfs)-3; oc->outOfs+=3; hdr[0]=OP_PUT; putBEShort(hdr+1,oc->maxSendSize); hdr[3]=oc->chunkCode==(HI_END_OF_BODY|TYPE_BYTES) ? (HI_BODY|TYPE_BYTES) : oc->chunkCode; putBEShort(hdr+4,oc->maxSendSize-3); connWrite2(con,hdr,6,oc->outBuf+oc->outOfs,oc->maxSendSize-6); oc->chunkLength-=oc->maxSendSize-6; oc->outOfs+=oc->maxSendSize-6; } else { hdr[0]=OP_PUT|OBEX_LAST; putBEShort(hdr+1,oc->outLength-oc->outOfs+3); connWrite2(con,hdr,3,oc->outBuf+oc->outOfs,oc->outLength-oc->outOfs); oc->outOfs=oc->outLength; } } } static void status(Connection* con, int event, void* buf, int len) { OBEXConnection* oc=(OBEXConnection*)con->handle; if(event==CONN_CLOSED) { if(oc->obex) { OBEXConnection** oh=&oc->obex->connections; OBEXConnection* o; while((o=*oh)) { if(o==oc) { *oh=o->next; break; } else { oh=&o->next; } } } if(oc->outBuf) freeMem(oc->outBuf); if(oc->inBuf) freeMem(oc->inBuf); freeMem(oc); if(oc->obex->obex.debug&OBEX_DEBUG_INFO) birda_log("obex connection closed\n"); connClose(con); } } static void data(Connection* con, void* buf0, int len) { u_char* buf=(u_char*)buf0; OBEXConnection* oc=(OBEXConnection*)con->handle; int len1; u_char op; if(oc->collOn) { birda_log("add to collBuf %d+%d (%d)\n",oc->collPos,len,oc->collLen); len1=oc->collPos+len; if(len1>oc->collLen) { birda_log("coll buffer overflow\n"); sendBadRequest(con); oc->chunkLength=0; oc->outLength=0; oc->inLength=0; oc->collOn=0; return; } memcpy(oc->collBuf+oc->collPos,buf,len); if(len1collLen) { birda_log("continue %d\n", len1,oc->collLen); oc->collPos=len1; sendContinue(con); return; } buf=oc->collBuf; len=oc->collLen; oc->collOn=0; } if(len<3) { birda_log("short obex packet\n"); sendBadRequest(con); oc->chunkLength=0; oc->outLength=0; oc->inLength=0; return; } len1=getBEShort(buf+1); if(len1>len) { birda_log("bad length in OBEX packet %d>%d\n",len1,len); if(oc->collBuf) freeMem(oc->collBuf); oc->collBuf=allocMem(id_collbuf,len1); memcpy(oc->collBuf,buf,len); oc->collOn=1; oc->collPos=len; oc->collLen=len1; sendContinue(con); return; } op=buf[0]&~OBEX_LAST; if(op!=oc->op) { oc->op=op; oc->chunkLength=0; oc->outLength=0; oc->inLength=0; } if(op==RES_CONTINUE) { if(oc->outOfsoutLength) sendChunk(con,oc); } else { oc->chunkLength=0; oc->outOfs=0; oc->outLength=0; while(oc->inMaxinLength+len-1) { oc->inMax*=2; oc->inBuf=growMem(oc->inBuf,oc->inMax); } if(len1>3) { memcpy(oc->inBuf+oc->inLength,buf+3,len1-3); oc->inLength+=len1-3; } if(buf[0]&OBEX_LAST) { switch(op) { case OP_CONNECT: if(len>=7) { int version=buf[3]; int flags=buf[4]; int maxlen=getBEShort(buf+5); if(oc->obex->obex.debug&OBEX_DEBUG_INFO) { birda_log("obex connect version=%d.%d flags=%d maxlen=%d\n", version>>4,version&15,flags,maxlen); if(len>7) { birda_log("optional data: "); showBytes(buf+7,len-7); } } oc->maxSendSize=connGetSendDataSize(con); if(oc->maxSendSize>maxlen) oc->maxSendSize=maxlen; sendConnectResponse(con); } else { birda_log("short obex connect packet\n"); sendBadRequest(con); } break; case OP_DISCONNECT: if(oc->obex->obex.debug&OBEX_DEBUG_INFO) birda_log("obex disconnect\n"); sendSuccess(con); break; case OP_PUT: if(oc->obex->obex.debug&OBEX_DEBUG_INFO) birda_log("obex put\n"); sendSuccess(con); if(oc->obex->obex.object) oc->obex->obex.object(&oc->obex->obex,oc->inBuf,oc->inLength); break; case OP_GET: birda_log("obex get is not supported\n"); sendNotImplemented(con); break; default: birda_log("obex op code %x not supported\n",op); sendNotImplemented(con); break; } oc->inLength=0; } else { sendContinue(con); } } } static bool accept(LSAP* lsap, Connection* con, void* buf, int len) { OBEXConnection* oc=allocMem(id_connection,sizeof(OBEXConnection)); OBEXServerPrivate* obex=(OBEXServerPrivate*)lsap->handle; oc->obex=obex; oc->inLength=0; oc->inMax=256; oc->inBuf=allocMem(id_inbuf,oc->inMax); oc->chunkLength=0; oc->outOfs=0; oc->outLength=0; oc->outMax=256; oc->outBuf=allocMem(id_outbuf,oc->outMax); oc->maxSendSize=connGetSendDataSize(con); if(oc->maxSendSize>255) oc->maxSendSize=255; oc->next=obex->connections; obex->connections=oc; con->handle=oc; con->status=status; con->data=data; return TRUE; } /********************************************************************** * External functions **********************************************************************/ void obexSrvClose(OBEXServer* obex) { OBEXServerPrivate* op=(OBEXServerPrivate*)obex; OBEXConnection* oc; iasObjDelete(op->obexObject); iasObjDelete(op->xferObject); lsapClose(op->lsap); for(oc=op->connections;oc;oc=oc->next) oc->obex=0; if(oc->inBuf) freeMem(oc->inBuf); if(oc->outBuf) freeMem(oc->outBuf); if(oc->collBuf) freeMem(oc->collBuf); freeMem(op); } OBEXServer* createOBEXServer(LAP* lap, IASServer* ias) { OBEXServerPrivate* obexp=allocMem(id_server,sizeof(OBEXServerPrivate)); obexp->obex.object=0; obexp->lsap=lapNewLSAP(lap,LM_TINY_TP); obexp->lsap->handle=obexp; obexp->lsap->accept=accept; obexp->obexObject=iasSrvNewObject(ias,"OBEX"); iasObjNewInteger(obexp->obexObject,"IrDA:TinyTP:LsapSel",lsapGetSelector(obexp->lsap)); obexp->xferObject=iasSrvNewObject(ias,"OBEX:IrXfer"); iasObjNewInteger(obexp->xferObject,"IrDA:TinyTP:LsapSel",lsapGetSelector(obexp->lsap)); lap->flags|=HINT_IROBEX; return &obexp->obex; }