/* IrDA dock program */ /* loosely based on the test code in openobex apps */ /* Accepts OBEX data on your IrDA connection, converts it to a MIME-format mail message, and drops it into a mailbox. */ /* currently takes no parameters, so if you want to change the default drop location, you'll need to change the mbox define below. */ /* Things that need work: - allow mbox to be specified on command line - remove nasty chdir hack - do some file locking on mbox - see if it's possible to dig the IR connection info out of the connection, so we can fake up Received headers. */ /* note, this links against openssl because I'm too damned lazy to reimplement base64 encoding. Here's the Makefile: LDFLAGS=-lopenobex `pkg-config --libs openssl` CFLAGS=-Wall `pkg-config --cflags openssl` -DDEBUG all: irmail */ #include #include #include #include #include #include #include /* for b64 encoding */ #include #include struct context { uint8_t *name; uint8_t *desc; int expected; int len; uint8_t *data; uint8_t *type; int serverdone; int clientdone; int dumped; }; struct context global_context = { 0, }; #ifdef DEBUG #define dprintf(x...) fprintf( stderr, x ) #else #define dprintf(x...) #endif #define mbox "Mail/irda" /* * Dump the gc object out as a MIME mail message */ int dump_as_mime( struct context *gc ) { int binary = 0; int i; time_t now = time( NULL ); char datestamp[41]; FILE *file; if ( !gc->len ) { /* no point */ return 1; } if ( gc->dumped ) { return 1; } /* very crude */ chdir( getenv( "HOME" )); if (( file = fopen( mbox, "r+" )) == NULL ) { fprintf( stderr, "can't open %s: %s\n", mbox, strerror( errno )); return 1; } fprintf( stderr, "Dumping obex object\n" ); gc->dumped = 1; i = strftime( datestamp, 40, "%a %b %d %H:%M:%S %Y", localtime( &now )); fprintf( file, "From IrDA %s\n", datestamp ); fprintf( file, "From: IrDA subsytem \n" ); i = strftime( datestamp, 40, "%a, %d %b %Y %H:%M:%S %z", localtime( &now )); if ( gc->name ) { fprintf( file, "Subject: %s\n", gc->name ); } fprintf( file, "Date: %s\n", datestamp ); fprintf( file, "MIME-Version: 1.0\n" ); if ( gc->type ) { /* vm/sagem hack */ if ( strcmp( gc->type, "image/jpg" ) == 0 ) { fprintf( file, "Content-Type: image/jpeg\n" ); } else { fprintf( file, "Content-Type: %s\n", gc->type ); } binary = strncmp( gc->type, "text", 4 ); free( gc->type ); gc->type = NULL; } if ( gc->name ) { /* should escape gc->name in case it's got dubious chars */ fprintf( file, "Content-Disposition: inline; filename=\"%s\"\n", gc->name ); free( gc->name ); gc->name = NULL; } if ( gc->desc ) { fprintf( file, "Content-Description: %s\n", gc->desc ); free( gc->desc ); gc->desc = NULL; } if ( binary ) { BIO *bio, *b64; /* we need to calculate the content length */ /* base64 bulks up your data by 4/3, then breaks it into 64 char lines with a LF */ float bulk = ((float)gc->len) * 4.0 / 3.0; int lines; if ( bulk - (int)bulk ) bulk = (int)bulk + 3; lines = bulk / 64.0 + 0.5; fprintf( file, "Content-Length: %d\n", (int)bulk + lines ); fprintf( file, "Content-Transfer-Encoding: base64\n\n" ); b64 = BIO_new( BIO_f_base64()); bio = BIO_new_fp( file, BIO_NOCLOSE ); bio = BIO_push( b64, bio ); BIO_write( bio, gc->data, gc->len ); BIO_flush( bio ); BIO_free_all( bio ); } else { fprintf( file, "Content-Length: %d\n\n", gc->len ); for ( i = 0; i < gc->len; i++ ) { fprintf( file, "%c", gc->data[i] ); } } /* make sure the mailbox doesn't get jammed up */ fprintf( file, "\n" ); fclose( file ); return 0; } void obex_event( obex_t *handle, obex_object_t *object, int mode, int event, int obex_cmd, int obex_rsp ) { char obex_cmd_s[20]; struct context *gc = OBEX_GetUserData( handle ); obex_headerdata_t hv; uint8_t hi; int hlen; #if 0 int rc; uint8_t *nhd; #endif switch( obex_cmd ) { case OBEX_CMD_CONNECT: strcpy( obex_cmd_s, "connect" ); break; case OBEX_CMD_DISCONNECT: strcpy( obex_cmd_s, "disconnect" ); break; case OBEX_CMD_PUT: strcpy( obex_cmd_s, "put" ); break; default: sprintf( obex_cmd_s, "unknown (%02x)", obex_cmd ); break; } #if 0 /* no idea what this stuff is */ rc = OBEX_ObjectGetNonHdrData( object, &nhd ); if ( rc ) { int i; dprintf( "found non-header data\n" ); for ( i = 0; i < rc; i++ ) { dprintf( "%02x ", nhd[i] ); } dprintf( "\n" ); } #endif /* this is an attempt at providing a real indication of progress, but it's mostly useless, since we can't get any info on the data block until it's complete */ OBEX_ObjectReParseHeaders( handle, object ); while ( OBEX_ObjectGetNextHeader( handle, object, &hi, &hv, &hlen )) { switch( hi ) { case OBEX_HDR_NAME: break; case OBEX_HDR_BODY: break; case OBEX_HDR_LENGTH: gc->expected = hv.bq4; break; case OBEX_HDR_TYPE: break; case OBEX_HDR_DESCRIPTION: break; default: dprintf( "unknown header %d available\n", hi ); } } switch( event ) { case OBEX_EV_PROGRESS: /* check obex for some way of getting the current data length */ dprintf( "." ); break; case OBEX_EV_ABORT: dprintf( "abort!\n" ); break; case OBEX_EV_LINKERR: dprintf( "link error\n" ); break; case OBEX_EV_REQHINT: dprintf( "%s request hint\n", obex_cmd_s ); /* in theory, can switch this based on cmd */ OBEX_ObjectSetRsp(object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS); break; case OBEX_EV_REQ: dprintf( "%s request\n", obex_cmd_s ); switch( obex_cmd ) { case OBEX_CMD_CONNECT: OBEX_ObjectSetRsp( object, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS ); break; case OBEX_CMD_DISCONNECT: OBEX_ObjectSetRsp( object, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS ); break; case OBEX_CMD_PUT: OBEX_ObjectSetRsp( object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS ); break; default: dprintf( "rejecting command\n" ); OBEX_ObjectSetRsp( object, OBEX_RSP_NOT_IMPLEMENTED, OBEX_RSP_NOT_IMPLEMENTED ); } break; case OBEX_EV_REQDONE: dprintf( "%s request done\n", obex_cmd_s ); /* for disconnect request, close our handle */ if ( !strcmp( obex_cmd_s, "disconnect" )) { OBEX_TransportDisconnect( handle ); gc->clientdone = 1; } if ( !strcmp( obex_cmd_s, "connect" )) { gc->clientdone = 0; } if ( !strcmp( obex_cmd_s, "put" )) { OBEX_ObjectReParseHeaders( handle, object ); while( OBEX_ObjectGetNextHeader( handle, object, &hi, &hv, &hlen )) { switch( hi ) { case OBEX_HDR_NAME: gc->name = malloc( hlen ); if ( gc->name != NULL ) { OBEX_UnicodeToChar( gc->name, hv.bs, hlen ); } else { fprintf( stderr, "can't allocate memory for name\n" ); } break; case OBEX_HDR_BODY: if (( gc->data = malloc( hlen )) != NULL ) { memcpy( gc->data, hv.bs, hlen ); gc->len = hlen; } else { gc->data = NULL; } break; case OBEX_HDR_LENGTH: dprintf( "Found length %u\n", hv.bq4 ); gc->len = hv.bq4; break; case OBEX_HDR_TYPE: if (( gc->type = malloc( hlen + 1 )) != NULL ) { memcpy( gc->type, hv.bs, hlen ); gc->type[hlen] = '\0'; } else { gc->type = NULL; } break; case OBEX_HDR_DESCRIPTION: gc->desc = malloc( hlen ); if ( gc->desc != NULL ) { OBEX_UnicodeToChar( gc->desc, hv.bs, hlen ); } else { fprintf( stderr, "can't allocate memory for description\n" ); } break; default: fprintf( stderr, "found header type %02x\n", hi ); break; } } gc->clientdone = 1; } break; default: dprintf( "unknown event %02x\n", event ); break; } if ( gc->clientdone ) { dump_as_mime( gc ); } #if 0 printf( "handle: %p object: %p mode: %02x event: %02x cmd: %s rsp: %02x\n", handle, object, mode, event, obex_cmd_s, obex_rsp ); #endif } obex_t *obex_startup( void ) { obex_t *handle; if (( handle = OBEX_Init( OBEX_TRANS_IRDA, obex_event, 0 )) == NULL ) { perror( "OBEX_Init failed" ); exit( 1 ); } OBEX_SetUserData( handle, &global_context ); return handle; } int main( int argc, char *argv[] ) { obex_t *handle; handle = obex_startup(); dprintf( "Entering server loop\n" ); while( 1 ) { struct context *gt; /* set up server */ if ( IrOBEX_ServerRegister( handle, "OBEX" ) < 0 ) { perror( "Server register error\n" ); exit( 1 ); } gt = OBEX_GetUserData( handle ); gt->serverdone = 0; while( !gt->serverdone ) { int i; if (( i = OBEX_HandleInput( handle, 0 )) < 0 ) { /* some clients appear to just drop the connection instead of disconnecting gracefully */ if ( errno == ECONNRESET ) { OBEX_TransportDisconnect( handle ); } else if ( errno ) { /* errno = 0 means we disconnected ourselves */ perror( "Error doing OBEX_HandleInput" ); } break; } #if 0 if ( i ) dprintf( "Obex Input handled (%d)\n", i ); #endif } } return 0; }