/* * 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. */ /* obexclt.c */ #include #include /********************************************************************** * Constants **********************************************************************/ static const char id_client[]="obex client"; /********************************************************************** * Data structures **********************************************************************/ typedef struct OBEXClientPrivate { OBEXClient obex; LAP* lap; int state; #define STATE_QUERY_OBEX 1 #define STATE_QUERY_XFER 2 #define STATE_CONNECTING 3 #define STATE_LIVE 4 #define STATE_CLOSED 0 IASClient* ias; Connection* con; int maxSendSize; int chunkLength; int chunkCode; int outOfs; int outLength; const u_char* outBuf; } OBEXClientPrivate; /********************************************************************** * Internal functions **********************************************************************/ static void sendConnect(Connection* con) { u_char hdr[7]; hdr[0]=OP_CONNECT|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 sendBadRequest(Connection* con) { u_char hdr[3]; hdr[0]=RES_BAD_REQUEST|OBEX_LAST; putBEShort(hdr+1,3); connWrite(con,hdr,3); } static int headerLength(OBEXClientPrivate* ocp, int ofs) { int len=ocp->outLength-ofs; switch(ocp->outBuf[ofs]&TYPE_MASK) { case TYPE_UNICODE: if(len>2) { int l1=getBEShort(ocp->outBuf+ofs+1); if(l1<5) l1=5; if(len>l1) len=l1; } break; case TYPE_BYTES: if(len>2) { int l1=getBEShort(ocp->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, OBEXClientPrivate* ocp) { u_char hdr[6]; if(ocp->chunkLength>0) { int l=ocp->outOfs+ocp->chunkLength; while(ocp->outLength-l>=3) { int l1=l+headerLength(ocp,l); if(l1-ocp->outOfs+6>ocp->maxSendSize) break; l=l1; } if(l-ocp->outOfs+6>ocp->maxSendSize) { hdr[0]=OP_PUT; putBEShort(hdr+1,ocp->maxSendSize); hdr[3]=ocp->chunkCode==(HI_END_OF_BODY|TYPE_BYTES) ? (HI_BODY|TYPE_BYTES) : ocp->chunkCode; putBEShort(hdr+4,ocp->maxSendSize-3); connWrite2(con,hdr,6,ocp->outBuf+ocp->outOfs,ocp->maxSendSize-6); ocp->chunkLength-=ocp->maxSendSize-6; ocp->outOfs+=ocp->maxSendSize-6; } else { hdr[0]=OP_PUT; if(l==ocp->outLength) hdr[0]|=OBEX_LAST; putBEShort(hdr+1,l-ocp->outOfs+6); hdr[3]=ocp->chunkCode; putBEShort(hdr+4,ocp->chunkLength+3); connWrite2(con,hdr,6,ocp->outBuf+ocp->outOfs,l-ocp->outOfs); ocp->chunkLength=0; ocp->outOfs=l; } } else { int l=ocp->outOfs; while(ocp->outLength-l>=3) { int l1=l+headerLength(ocp,l); if(l1-ocp->outOfs+3>ocp->maxSendSize) break; l=l1; } if(l>ocp->outOfs) { hdr[0]=OP_PUT; if(l==ocp->outLength) hdr[0]|=OBEX_LAST; putBEShort(hdr+1,l-ocp->outOfs+3); connWrite2(con,hdr,3,ocp->outBuf+ocp->outOfs,l-ocp->outOfs); ocp->outOfs=l; } else if(ocp->outLength-ocp->outOfs>=3) { ocp->chunkCode=ocp->outBuf[ocp->outOfs]; ocp->chunkLength=headerLength(ocp,ocp->outOfs)-3; ocp->outOfs+=3; hdr[0]=OP_PUT; putBEShort(hdr+1,ocp->maxSendSize); hdr[3]=ocp->chunkCode==(HI_END_OF_BODY|TYPE_BYTES) ? (HI_BODY|TYPE_BYTES) : ocp->chunkCode; putBEShort(hdr+4,ocp->maxSendSize-3); connWrite2(con,hdr,6,ocp->outBuf+ocp->outOfs,ocp->maxSendSize-6); ocp->chunkLength-=ocp->maxSendSize-6; ocp->outOfs+=ocp->maxSendSize-6; } else { hdr[0]=OP_PUT|OBEX_LAST; putBEShort(hdr+1,ocp->outLength-ocp->outOfs+3); connWrite2(con,hdr,3,ocp->outBuf+ocp->outOfs,ocp->outLength-ocp->outOfs); ocp->outOfs=ocp->outLength; } } } static void shutDown(OBEXClientPrivate* ocp) { ocp->outLength=0; birda_log("shutdown is NYI\n"); } static void connData(Connection* con, void* buf0, int len) { int op,len1; u_char* buf=(u_char*)buf0; OBEXClientPrivate* ocp=(OBEXClientPrivate*)con->handle; if(len<3) { birda_log("short obex packet\n"); sendBadRequest(ocp->con); shutDown(ocp); return; } len1=getBEShort(buf+1); if(len1>len) { birda_log("bad length in OBEX packet\n"); sendBadRequest(ocp->con); shutDown(ocp); return; } if(!(buf[0]&OBEX_LAST)) { birda_log("did not expect long OBEX packet\n"); sendBadRequest(ocp->con); shutDown(ocp); return; } op=buf[0]&~OBEX_LAST; switch(op) { case RES_CONTINUE: sendChunk(con,ocp); break; case OP_DISCONNECT: birda_log("obex disconnect is NYI\n"); break; case RES_SUCCESS: if(ocp->state==STATE_CONNECTING) { ocp->state=STATE_LIVE; ocp->maxSendSize=connGetSendDataSize(con); if(len1>=7) { int maxlen=getBEShort(buf+5); if(ocp->maxSendSize>maxlen) ocp->maxSendSize=maxlen; } if(ocp->outOfsoutLength) sendChunk(con,ocp); } else { if(ocp->obex.status) ocp->obex.status(&ocp->obex,OBEX_SUCCESS); } break; case RES_BAD_REQUEST: birda_log("received bad request response\n"); shutDown(ocp); break; default: birda_log("unexpected obex opcode %x NYI\n",op); break; } } static void connStatus(Connection* con, int event, void* buf, int len) { /* OBEXClient* oc=(OBEXClient*)con->handle; */ if(event==CONN_CLOSED) { birda_log("connStatus disconnect is NYI\n"); } } static void iasStatus(IASClient* ic, int event) { OBEXClientPrivate* ocp=(OBEXClientPrivate*)ic->handle; if(event==IAS_QUERY_COMPLETE) { int type,lsap=0; while((type=iasCltResType(ocp->ias))!=IAS_NONE) { if(type==IAS_INT) { lsap=iasCltResInt(ocp->ias); break; } iasCltResNext(ocp->ias); } if(lsap>0) { iasCltClose(ocp->ias); ocp->ias=0; ocp->state=STATE_CONNECTING; ocp->con=lapNewConnection(ocp->lap,lsap,LM_TINY_TP,0,0); if(ocp->con) { ocp->con->handle=ocp; ocp->con->status=connStatus; ocp->con->data=connData; sendConnect(ocp->con); } else { birda_log("New obex connection failed is NYI\n"); } return; } } if(ocp->state==STATE_QUERY_OBEX) { ocp->state=STATE_QUERY_XFER; if(iasCltGetValueByClass(ocp->ias,"OBEX:IrXfer","IrDA:TinyTP:LsapSel")) { return; } } iasCltClose(ocp->ias); ocp->ias=0; birda_log("ias query failed is NYI\n"); } /********************************************************************** * External functions **********************************************************************/ bool obexCltPut(OBEXClient* oc, const void* buf, int len) { OBEXClientPrivate* ocp=(OBEXClientPrivate*)oc; if(ocp->state!=STATE_CLOSED && ocp->outOfs>=ocp->outLength && buf && len>0) { ocp->chunkLength=0; ocp->outOfs=0; ocp->outLength=len; ocp->outBuf=buf; if(ocp->state==STATE_LIVE) sendChunk(ocp->con,ocp); return TRUE; } else { return FALSE; } } void obexCltClose(OBEXClient* oc) { OBEXClientPrivate* ocp=(OBEXClientPrivate*)oc; if(ocp->ias) iasCltClose(ocp->ias); if(ocp->con) connClose(ocp->con); freeMem(ocp); } OBEXClient* createOBEXClient(LAP* lap) { OBEXClientPrivate* ocp; IASClient* ias=createIASClient(lap); if(!ias) return 0; ocp=allocMem(id_client,sizeof(OBEXClientPrivate)); ocp->obex.handle=0; ocp->obex.status=0; ocp->lap=lap; ocp->state=STATE_QUERY_OBEX; ocp->ias=ias; ocp->ias->handle=ocp; ocp->ias->status=iasStatus; ocp->con=0; ocp->chunkLength=0; ocp->outOfs=0; ocp->outLength=0; ocp->outBuf=0; if(!iasCltGetValueByClass(ocp->ias,"OBEX","IrDA:TinyTP:LsapSel")) { birda_log("IAS query not accepted is NYI\n"); } return &ocp->obex; }