mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-28 09:58:30 +01:00
4a57b76817
It's only needed when there's a close proc, and that's rare.
1089 lines
38 KiB
C
1089 lines
38 KiB
C
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
|
|
/*
|
|
* Copyright 2018 by Eric House (xwords@eehouse.org). All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
|
|
#include "util.h"
|
|
#include "smsproto.h"
|
|
#include "comtypes.h"
|
|
#include "strutils.h"
|
|
|
|
#define MAX_WAIT 3
|
|
// # define MAX_MSG_LEN 50 /* for testing */
|
|
#ifndef MAX_LEN_BINARY
|
|
# define MAX_LEN_BINARY 115
|
|
#endif
|
|
/* PENDING: Might want to make SEND_NOW_SIZE smaller; might as well send now
|
|
if even the smallest new message is likely to put us over. */
|
|
#define SEND_NOW_SIZE MAX_LEN_BINARY
|
|
|
|
/* To match the SMSService format */
|
|
#define SMS_PROTO_VERSION_JAVA 1
|
|
#define SMS_PROTO_VERSION_COMBO 2
|
|
|
|
#define PARTIALS_FORMAT 0
|
|
|
|
typedef struct _MsgRec {
|
|
XP_U32 createSeconds;
|
|
SMSMsgNet msgNet;
|
|
} MsgRec;
|
|
|
|
typedef struct _ToPhoneRec {
|
|
XP_UCHAR phone[32];
|
|
XP_U32 createSeconds;
|
|
XP_U16 nMsgs;
|
|
XP_U16 totalSize;
|
|
MsgRec** msgs;
|
|
} ToPhoneRec;
|
|
|
|
typedef struct _MsgIDRec {
|
|
int msgID;
|
|
int count;
|
|
struct {
|
|
XP_U16 len;
|
|
XP_U8* data;
|
|
}* parts;
|
|
} MsgIDRec;
|
|
|
|
typedef struct _FromPhoneRec {
|
|
XP_UCHAR phone[32];
|
|
int nMsgIDs;
|
|
MsgIDRec* msgIDRecs;
|
|
} FromPhoneRec;
|
|
|
|
struct SMSProto {
|
|
XW_DUtilCtxt* dutil;
|
|
pthread_t creator;
|
|
pthread_mutex_t mutex;
|
|
XP_U16 nNextID;
|
|
int lastStoredSize;
|
|
XP_U16 nToPhones;
|
|
ToPhoneRec* toPhoneRecs;
|
|
|
|
int nFromPhones;
|
|
FromPhoneRec* fromPhoneRecs;
|
|
#ifdef DEBUG
|
|
pthread_t starter;
|
|
int nestCount;
|
|
#endif
|
|
MPSLOT;
|
|
};
|
|
|
|
static int nextMsgID( SMSProto* state, XWEnv xwe );
|
|
static XWStreamCtxt* mkStream( SMSProto* state );
|
|
static void destroyStream( XWStreamCtxt* stream );
|
|
static SMSMsgArray* toNetMsgs( SMSProto* state, XWEnv xwe, ToPhoneRec* rec,
|
|
XP_Bool forceOld );
|
|
static ToPhoneRec* getForPhone( SMSProto* state, const XP_UCHAR* phone,
|
|
XP_Bool create );
|
|
static void addToOutRec( SMSProto* state, ToPhoneRec* rec, SMS_CMD cmd,
|
|
XP_U16 port, XP_U32 gameID, const XP_U8* buf,
|
|
XP_U16 buflen, XP_U32 nowSeconds );
|
|
static void addMessage( SMSProto* state, const XP_UCHAR* fromPhone, int msgID,
|
|
int indx, int count, const XP_U8* data, XP_U16 len );
|
|
static SMSMsgArray* completeMsgs( SMSProto* state, SMSMsgArray* arr,
|
|
const XP_UCHAR* fromPhone, XP_U16 wantPort,
|
|
int msgID );
|
|
static void savePartials( SMSProto* state, XWEnv xwe );
|
|
static void restorePartials( SMSProto* state, XWEnv xwe );
|
|
static void rmFromPhoneRec( SMSProto* state, int fromPhoneIndex );
|
|
static void freeMsgIDRec( SMSProto* state, MsgIDRec* rec, int fromPhoneIndex,
|
|
int msgIDIndex );
|
|
static void freeForPhone( SMSProto* state, const XP_UCHAR* phone );
|
|
static void freeMsg( SMSProto* state, MsgRec** msg );
|
|
static void freeRec( SMSProto* state, ToPhoneRec* rec );
|
|
#if defined DEBUG && defined COMMS_CHECKSUM
|
|
static void logResult( const SMSProto* state, XWEnv xwe,
|
|
const SMSMsgArray* result, const char* caller );
|
|
#else
|
|
# define logResult( state, xwe, result, caller )
|
|
#endif
|
|
|
|
SMSProto*
|
|
smsproto_init( MPFORMAL XWEnv xwe, XW_DUtilCtxt* dutil )
|
|
{
|
|
SMSProto* state = (SMSProto*)XP_CALLOC( mpool, sizeof(*state) );
|
|
pthread_mutex_init( &state->mutex, NULL );
|
|
state->dutil = dutil;
|
|
MPASSIGN( state->mpool, mpool );
|
|
|
|
XP_U32 siz = sizeof(state->nNextID);
|
|
const XP_UCHAR* keys[] = { KEY_NEXTID, NULL, };
|
|
dutil_loadPtr( state->dutil, xwe, keys, &state->nNextID, &siz );
|
|
XP_LOGF( "%s(): loaded nextMsgID: %d", __func__, state->nNextID );
|
|
|
|
restorePartials( state, xwe );
|
|
|
|
return state;
|
|
}
|
|
|
|
void
|
|
smsproto_free( SMSProto* state )
|
|
{
|
|
if ( NULL != state ) {
|
|
XP_ASSERT( state->creator == 0 || state->creator == pthread_self() );
|
|
|
|
for ( XP_U16 ii = 0; ii < state->nToPhones; ++ii ) {
|
|
freeRec( state, &state->toPhoneRecs[ii] );
|
|
}
|
|
XP_FREEP( state->mpool, &state->toPhoneRecs );
|
|
|
|
if ( 0 < state->nFromPhones ) {
|
|
XP_LOGF( "%s(): freeing undelivered partial messages", __func__ );
|
|
}
|
|
while (0 < state->nFromPhones) {
|
|
FromPhoneRec* ffr = &state->fromPhoneRecs[0];
|
|
while ( 0 < ffr->nMsgIDs ) {
|
|
freeMsgIDRec( state, &ffr->msgIDRecs[0], 0, 0 );
|
|
}
|
|
}
|
|
XP_ASSERT( !state->fromPhoneRecs ); /* above nulls this once empty */
|
|
|
|
pthread_mutex_destroy( &state->mutex );
|
|
|
|
XP_FREEP( state->mpool, &state );
|
|
}
|
|
}
|
|
|
|
static void
|
|
headerToStream( XWStreamCtxt* stream, SMS_CMD cmd, XP_U16 port, XP_U32 gameID )
|
|
{
|
|
// XP_LOGF( "%s(cmd: %d; gameID: %d)", __func__, cmd, gameID );
|
|
stream_putU8( stream, SMS_PROTO_VERSION_JAVA );
|
|
stream_putU16( stream, port );
|
|
stream_putU8( stream, cmd );
|
|
switch ( cmd ) {
|
|
case NONE:
|
|
XP_ASSERT(0);
|
|
break;
|
|
case INVITE:
|
|
break;
|
|
default:
|
|
stream_putU32( stream, gameID );
|
|
}
|
|
}
|
|
|
|
static XP_Bool
|
|
headerFromStream( XWStreamCtxt* stream, SMS_CMD* cmd, XP_U16* port, XP_U32* gameID )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
XP_U8 tmp;
|
|
|
|
if ( stream_gotU8( stream, &tmp )
|
|
&& tmp == SMS_PROTO_VERSION_JAVA
|
|
&& stream_gotU16( stream, port )
|
|
&& stream_gotU8( stream, &tmp ) ) {
|
|
*cmd = tmp;
|
|
switch( *cmd ) {
|
|
case INVITE:
|
|
success = XP_TRUE;
|
|
break;
|
|
default:
|
|
success = stream_gotU32( stream, gameID );
|
|
break;
|
|
}
|
|
}
|
|
// XP_LOGF( "%s() => cmd: %d; gameID: %d", __func__, *cmd, *gameID );
|
|
return success;
|
|
}
|
|
|
|
/* Maintain a list of pending messages per phone number. When called and it's
|
|
* been at least some amount of time since we last added something, or at
|
|
* least some longer time since the oldest message was added, return an array
|
|
* of messages ready to send via the device's raw SMS (i.e. respecting its
|
|
* size limits.)
|
|
|
|
* Pass in the current time, as that's easier than keeping an instance of
|
|
* UtilCtxt around.
|
|
*/
|
|
SMSMsgArray*
|
|
smsproto_prepOutbound( SMSProto* state, XWEnv xwe, SMS_CMD cmd, XP_U32 gameID,
|
|
const void* buf, XP_U16 buflen, const XP_UCHAR* toPhone,
|
|
int toPort, XP_Bool forceOld, XP_U16* waitSecsP )
|
|
{
|
|
XP_USE( toPort );
|
|
SMSMsgArray* result = NULL;
|
|
pthread_mutex_lock( &state->mutex );
|
|
|
|
#if defined DEBUG && defined COMMS_CHECKSUM
|
|
XP_UCHAR* checksum = dutil_md5sum( state->dutil, xwe, buf, buflen );
|
|
XP_LOGFF( "(cmd=%d, gameID=%d): len=%d, sum=%s, toPhone=%s", cmd,
|
|
gameID, buflen, checksum, toPhone );
|
|
XP_FREEP( state->mpool, &checksum );
|
|
#endif
|
|
|
|
ToPhoneRec* rec = getForPhone( state, toPhone, cmd != NONE );
|
|
|
|
/* First, add the new message (if present) to the array */
|
|
XP_U32 nowSeconds = dutil_getCurSeconds( state->dutil, xwe );
|
|
if ( cmd != NONE ) {
|
|
addToOutRec( state, rec, cmd, toPort, gameID, buf, buflen, nowSeconds );
|
|
}
|
|
|
|
/* rec will be non-null if there's something in it */
|
|
XP_Bool doSend = XP_FALSE;
|
|
if ( rec != NULL ) {
|
|
doSend = forceOld
|
|
|| rec->totalSize > SEND_NOW_SIZE
|
|
|| MAX_WAIT <= nowSeconds - rec->createSeconds;
|
|
/* other criteria? */
|
|
}
|
|
|
|
if ( doSend ) {
|
|
result = toNetMsgs( state, xwe, rec, forceOld );
|
|
freeForPhone( state, toPhone );
|
|
}
|
|
|
|
XP_U16 waitSecs = 0;
|
|
if ( !result && !!rec && (rec->nMsgs > 0) ) {
|
|
waitSecs = MAX_WAIT - (nowSeconds - rec->createSeconds);
|
|
}
|
|
*waitSecsP = waitSecs;
|
|
|
|
XP_LOGF( "%s() => %p (count=%d, *waitSecs=%d)", __func__, result,
|
|
!!result ? result->nMsgs : 0, *waitSecsP );
|
|
|
|
pthread_mutex_unlock( &state->mutex );
|
|
logResult( state, xwe, result, __func__ );
|
|
return result;
|
|
} /* smsproto_prepOutbound */
|
|
|
|
static SMSMsgArray*
|
|
appendLocMsg( SMSProto* XP_UNUSED_DBG(state), SMSMsgArray* arr, SMSMsgLoc* msg )
|
|
{
|
|
if ( NULL == arr ) {
|
|
arr = XP_CALLOC( state->mpool, sizeof(*arr) );
|
|
arr->format = FORMAT_LOC;
|
|
} else {
|
|
XP_ASSERT( arr->format == FORMAT_LOC );
|
|
}
|
|
|
|
arr->u.msgsLoc = XP_REALLOC( state->mpool, arr->u.msgsLoc,
|
|
(arr->nMsgs + 1) * sizeof(*arr->u.msgsLoc) );
|
|
arr->u.msgsLoc[arr->nMsgs++] = *msg;
|
|
return arr;
|
|
}
|
|
|
|
static SMSMsgArray*
|
|
appendNetMsg( SMSProto* XP_UNUSED_DBG(state), SMSMsgArray* arr, SMSMsgNet* msg )
|
|
{
|
|
if ( NULL == arr ) {
|
|
arr = XP_CALLOC( state->mpool, sizeof(*arr) );
|
|
arr->format = FORMAT_NET;
|
|
} else {
|
|
XP_ASSERT( arr->format == FORMAT_NET );
|
|
}
|
|
|
|
arr->u.msgsNet = XP_REALLOC( state->mpool, arr->u.msgsNet,
|
|
(arr->nMsgs + 1) * sizeof(*arr->u.msgsNet) );
|
|
arr->u.msgsNet[arr->nMsgs++] = *msg;
|
|
return arr;
|
|
}
|
|
|
|
SMSMsgArray*
|
|
smsproto_prepInbound( SMSProto* state, XWEnv xwe, const XP_UCHAR* fromPhone,
|
|
XP_U16 wantPort, const XP_U8* data, XP_U16 len )
|
|
{
|
|
XP_LOGFF( "len=%d, fromPhone=%s", len, fromPhone );
|
|
|
|
#if defined DEBUG && defined COMMS_CHECKSUM
|
|
XP_UCHAR* checksum = dutil_md5sum( state->dutil, xwe, data, len );
|
|
XP_LOGFF( "(fromPhone=%s, len=%d); sum=%s", fromPhone, len, checksum );
|
|
XP_FREEP( state->mpool, &checksum );
|
|
#endif
|
|
|
|
SMSMsgArray* result = NULL;
|
|
pthread_mutex_lock( &state->mutex );
|
|
|
|
XWStreamCtxt* stream = mkStream( state );
|
|
stream_putBytes( stream, data, len );
|
|
|
|
XP_U8 proto;
|
|
if ( stream_gotU8( stream, &proto ) ) {
|
|
switch ( proto ) {
|
|
case SMS_PROTO_VERSION_JAVA: {
|
|
XP_U8 msgID, indx, count;
|
|
if ( stream_gotU8( stream, &msgID )
|
|
&& stream_gotU8( stream, &indx )
|
|
&& stream_gotU8( stream, &count )
|
|
&& indx < count ) {
|
|
XP_U16 len = stream_getSize( stream );
|
|
XP_U8 buf[len];
|
|
stream_getBytes( stream, buf, len );
|
|
addMessage( state, fromPhone, msgID, indx, count, buf, len );
|
|
result = completeMsgs( state, result, fromPhone, wantPort, msgID );
|
|
savePartials( state, xwe );
|
|
}
|
|
}
|
|
break;
|
|
case SMS_PROTO_VERSION_COMBO: {
|
|
XP_U8 oneLen, msgID;
|
|
while ( stream_gotU8( stream, &oneLen )
|
|
&& stream_gotU8( stream, &msgID ) ) {
|
|
XP_U8 tmp[oneLen];
|
|
stream_getBytes( stream, tmp, oneLen );
|
|
|
|
XWStreamCtxt* msgStream = mkStream( state );
|
|
stream_putBytes( msgStream, tmp, oneLen );
|
|
|
|
XP_U32 gameID;
|
|
XP_U16 port;
|
|
SMS_CMD cmd;
|
|
if ( headerFromStream( msgStream, &cmd, &port, &gameID ) ) {
|
|
XP_U16 msgLen = stream_getSize( msgStream );
|
|
XP_U8 buf[msgLen];
|
|
if ( stream_gotBytes( msgStream, buf, msgLen ) ) {
|
|
if ( port == wantPort ) {
|
|
SMSMsgLoc msg = { .len = msgLen,
|
|
.cmd = cmd,
|
|
.gameID = gameID,
|
|
.data = XP_MALLOC( state->mpool, msgLen ),
|
|
};
|
|
XP_MEMCPY( msg.data, buf, msgLen );
|
|
result = appendLocMsg( state, result, &msg );
|
|
} else {
|
|
XP_LOGF( "%s(): expected port %d, got %d", __func__,
|
|
wantPort, port );
|
|
}
|
|
}
|
|
}
|
|
destroyStream( msgStream );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
/* Don't assert! happens all the time */
|
|
XP_LOGF( "%s(): unexpected proto %d", __func__, proto );
|
|
break;
|
|
}
|
|
}
|
|
|
|
destroyStream( stream );
|
|
|
|
XP_LOGFF( "=> %p (len=%d)", result, (!!result) ? result->nMsgs : 0 );
|
|
logResult( state, xwe, result, __func__ );
|
|
|
|
pthread_mutex_unlock( &state->mutex );
|
|
return result;
|
|
}
|
|
|
|
void
|
|
smsproto_freeMsgArray( SMSProto* state, SMSMsgArray* arr )
|
|
{
|
|
pthread_mutex_lock( &state->mutex );
|
|
|
|
for ( int ii = 0; ii < arr->nMsgs; ++ii ) {
|
|
XP_U8** ptr = arr->format == FORMAT_LOC
|
|
? &arr->u.msgsLoc[ii].data : &arr->u.msgsNet[ii].data;
|
|
XP_FREEP( state->mpool, ptr );
|
|
}
|
|
|
|
void** ptr;
|
|
switch( arr->format ) {
|
|
case FORMAT_LOC:
|
|
ptr = (void**)&arr->u.msgsLoc;
|
|
break;
|
|
case FORMAT_NET:
|
|
ptr = (void**)&arr->u.msgsNet;
|
|
break;
|
|
default:
|
|
XP_ASSERT(0);
|
|
ptr = NULL;
|
|
}
|
|
XP_FREEP( state->mpool, ptr );
|
|
XP_FREEP( state->mpool, &arr );
|
|
pthread_mutex_unlock( &state->mutex );
|
|
}
|
|
|
|
#if defined DEBUG && defined COMMS_CHECKSUM
|
|
static void
|
|
logResult( const SMSProto* state, XWEnv xwe, const SMSMsgArray* result,
|
|
const char* caller )
|
|
{
|
|
if ( !!result ) {
|
|
for ( int ii = 0; ii < result->nMsgs; ++ii ) {
|
|
XP_U8* data;
|
|
XP_U16 len = 0;
|
|
switch ( result->format ) {
|
|
case FORMAT_LOC: {
|
|
SMSMsgLoc* msgsLoc = &result->u.msgsLoc[ii];
|
|
data = msgsLoc->data;
|
|
len = msgsLoc->len;
|
|
}
|
|
break;
|
|
case FORMAT_NET: {
|
|
SMSMsgNet* msgsNet = &result->u.msgsNet[ii];
|
|
data = msgsNet->data;
|
|
len = msgsNet->len;
|
|
}
|
|
break;
|
|
default:
|
|
XP_ASSERT(0);
|
|
}
|
|
if ( 0 == len ) {
|
|
XP_LOGFF( "%s() => datum[%d] len: 0", caller, ii );
|
|
} else {
|
|
XP_UCHAR* checksum = dutil_md5sum( state->dutil, xwe, data, len );
|
|
XP_LOGFF( "%s() => datum[%d] sum: %s, len: %d", caller, ii, checksum, len );
|
|
XP_FREEP( state->mpool, &checksum );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
freeMsg( SMSProto* XP_UNUSED_DBG(state), MsgRec** msgp )
|
|
{
|
|
XP_FREEP( state->mpool, &(*msgp)->msgNet.data );
|
|
XP_FREEP( state->mpool, msgp );
|
|
}
|
|
|
|
static void
|
|
freeRec( SMSProto* state, ToPhoneRec* rec )
|
|
{
|
|
for ( XP_U16 jj = 0; jj < rec->nMsgs; ++jj ) {
|
|
freeMsg( state, &rec->msgs[jj] );
|
|
}
|
|
XP_FREEP( state->mpool, &rec->msgs );
|
|
}
|
|
|
|
static ToPhoneRec*
|
|
getForPhone( SMSProto* state, const XP_UCHAR* phone, XP_Bool create )
|
|
{
|
|
ToPhoneRec* rec = NULL;
|
|
for ( XP_U16 ii = 0; !rec && ii < state->nToPhones; ++ii ) {
|
|
if ( 0 == XP_STRCMP( state->toPhoneRecs[ii].phone, phone ) ) {
|
|
rec = &state->toPhoneRecs[ii];
|
|
}
|
|
}
|
|
|
|
if ( !rec && create ) {
|
|
state->toPhoneRecs = XP_REALLOC( state->mpool, state->toPhoneRecs,
|
|
(1 + state->nToPhones) * sizeof(*state->toPhoneRecs) );
|
|
rec = &state->toPhoneRecs[state->nToPhones++];
|
|
XP_MEMSET( rec, 0, sizeof(*rec) );
|
|
XP_STRCAT( rec->phone, phone );
|
|
}
|
|
|
|
return rec;
|
|
}
|
|
|
|
static void
|
|
freeForPhone( SMSProto* state, const XP_UCHAR* phone )
|
|
{
|
|
for ( XP_U16 ii = 0; ii < state->nToPhones; ++ii ) {
|
|
if ( 0 == XP_STRCMP( state->toPhoneRecs[ii].phone, phone ) ) {
|
|
freeRec( state, &state->toPhoneRecs[ii] );
|
|
|
|
XP_U16 nAbove = state->nToPhones - ii - 1;
|
|
XP_ASSERT( nAbove >= 0 );
|
|
if ( nAbove > 0 ) {
|
|
XP_MEMMOVE( &state->toPhoneRecs[ii], &state->toPhoneRecs[ii+1],
|
|
nAbove * sizeof(*state->toPhoneRecs) );
|
|
}
|
|
--state->nToPhones;
|
|
if ( 0 == state->nToPhones ) {
|
|
XP_FREEP( state->mpool, &state->toPhoneRecs );
|
|
} else {
|
|
state->toPhoneRecs = XP_REALLOC( state->mpool, state->toPhoneRecs,
|
|
state->nToPhones * sizeof(*state->toPhoneRecs) );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
addToOutRec( SMSProto* state, ToPhoneRec* rec, SMS_CMD cmd,
|
|
XP_U16 port, XP_U32 gameID, const XP_U8* buf, XP_U16 buflen,
|
|
XP_U32 nowSeconds )
|
|
{
|
|
XWStreamCtxt* stream = mkStream( state );
|
|
headerToStream( stream, cmd, port, gameID );
|
|
stream_putBytes( stream, buf, buflen );
|
|
|
|
MsgRec* mRec = XP_CALLOC( state->mpool, sizeof(*rec) );
|
|
XP_U16 len = stream_getSize( stream );
|
|
mRec->msgNet.len = len;
|
|
mRec->msgNet.data = XP_MALLOC( state->mpool, len );
|
|
XP_MEMCPY( mRec->msgNet.data, stream_getPtr(stream), len );
|
|
destroyStream( stream );
|
|
|
|
mRec->createSeconds = nowSeconds;
|
|
|
|
rec->msgs = XP_REALLOC( state->mpool, rec->msgs, (1 + rec->nMsgs) * sizeof(*rec->msgs) );
|
|
rec->msgs[rec->nMsgs++] = mRec;
|
|
rec->totalSize += len;
|
|
XP_LOGFF( "added msg to %s of len %d; total now %d", rec->phone, len,
|
|
rec->totalSize );
|
|
|
|
if ( rec->nMsgs == 1 ) {
|
|
rec->createSeconds = nowSeconds;
|
|
}
|
|
}
|
|
|
|
static MsgIDRec*
|
|
getMsgIDRec( SMSProto* state, const XP_UCHAR* fromPhone, int msgID,
|
|
XP_Bool addMissing, int* fromPhoneIndex, int* msgIDIndex )
|
|
{
|
|
MsgIDRec* result = NULL;
|
|
|
|
FromPhoneRec* fromPhoneRec = NULL;
|
|
for ( int ii = 0; ii < state->nFromPhones; ++ii ) {
|
|
if ( 0 == XP_STRCMP( state->fromPhoneRecs[ii].phone, fromPhone ) ) {
|
|
fromPhoneRec = &state->fromPhoneRecs[ii];
|
|
*fromPhoneIndex = ii;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create and add if not found
|
|
if ( NULL == fromPhoneRec && addMissing ) {
|
|
state->fromPhoneRecs =
|
|
XP_REALLOC( state->mpool, state->fromPhoneRecs,
|
|
(state->nFromPhones + 1) * sizeof(*state->fromPhoneRecs) );
|
|
*fromPhoneIndex = state->nFromPhones;
|
|
fromPhoneRec = &state->fromPhoneRecs[state->nFromPhones++];
|
|
XP_MEMSET( fromPhoneRec, 0, sizeof(*fromPhoneRec) );
|
|
XP_STRCAT( fromPhoneRec->phone, fromPhone );
|
|
}
|
|
|
|
// Now find msgID record
|
|
if ( NULL != fromPhoneRec ) {
|
|
for ( int ii = 0; ii < fromPhoneRec->nMsgIDs; ++ii ) {
|
|
if ( fromPhoneRec->msgIDRecs[ii].msgID == msgID ) {
|
|
result = &fromPhoneRec->msgIDRecs[ii];
|
|
*msgIDIndex = ii;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// create and add if not found
|
|
if ( NULL == result && addMissing ) {
|
|
fromPhoneRec->msgIDRecs = XP_REALLOC( state->mpool, fromPhoneRec->msgIDRecs,
|
|
(fromPhoneRec->nMsgIDs + 1)
|
|
* sizeof(*fromPhoneRec->msgIDRecs) );
|
|
MsgIDRec newRec = { .msgID = msgID };
|
|
*msgIDIndex = fromPhoneRec->nMsgIDs;
|
|
result = &fromPhoneRec->msgIDRecs[fromPhoneRec->nMsgIDs];
|
|
fromPhoneRec->msgIDRecs[fromPhoneRec->nMsgIDs++] = newRec;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Messages that are split gather here until complete
|
|
*/
|
|
static void
|
|
addMessage( SMSProto* state, const XP_UCHAR* fromPhone, int msgID, int indx,
|
|
int count, const XP_U8* data, XP_U16 len )
|
|
{
|
|
// XP_LOGFF( "phone=%s, msgID=%d, %d/%d", fromPhone, msgID, indx, count );
|
|
XP_ASSERT( 0 < len );
|
|
MsgIDRec* msgIDRec;
|
|
for ( ; ; ) {
|
|
int fromPhoneIndex;
|
|
int msgIDIndex;
|
|
msgIDRec = getMsgIDRec( state, fromPhone, msgID, XP_TRUE,
|
|
&fromPhoneIndex, &msgIDIndex );
|
|
|
|
/* sanity check... */
|
|
if ( msgIDRec->count == 0 || msgIDRec->count == count ) {
|
|
break;
|
|
}
|
|
freeMsgIDRec( state, msgIDRec, fromPhoneIndex, msgIDIndex );
|
|
}
|
|
|
|
/* if it's new, fill in missing fields */
|
|
if ( msgIDRec->count == 0 ) {
|
|
msgIDRec->count = count; /* in case it's new */
|
|
msgIDRec->parts = XP_CALLOC( state->mpool, count * sizeof(*msgIDRec->parts));
|
|
}
|
|
|
|
XP_ASSERT( msgIDRec->parts[indx].len == 0
|
|
|| msgIDRec->parts[indx].len == len ); /* replace with same ok */
|
|
msgIDRec->parts[indx].len = len;
|
|
XP_FREEP( state->mpool, &msgIDRec->parts[indx].data ); /* in case non-null (replacement) */
|
|
msgIDRec->parts[indx].data = XP_MALLOC( state->mpool, len );
|
|
XP_MEMCPY( msgIDRec->parts[indx].data, data, len );
|
|
}
|
|
|
|
static void
|
|
rmFromPhoneRec( SMSProto* state, int fromPhoneIndex )
|
|
{
|
|
FromPhoneRec* fromPhoneRec = &state->fromPhoneRecs[fromPhoneIndex];
|
|
XP_ASSERT( fromPhoneRec->nMsgIDs == 0 );
|
|
XP_FREEP( state->mpool, &fromPhoneRec->msgIDRecs );
|
|
|
|
if ( --state->nFromPhones == 0 ) {
|
|
XP_FREEP( state->mpool, &state->fromPhoneRecs );
|
|
} else {
|
|
XP_U16 nAbove = state->nFromPhones - fromPhoneIndex;
|
|
XP_ASSERT( nAbove >= 0 );
|
|
if ( nAbove > 0 ) {
|
|
XP_MEMMOVE( &state->fromPhoneRecs[fromPhoneIndex], &state->fromPhoneRecs[fromPhoneIndex+1],
|
|
nAbove * sizeof(*state->fromPhoneRecs) );
|
|
}
|
|
state->fromPhoneRecs = XP_REALLOC( state->mpool, state->fromPhoneRecs,
|
|
state->nFromPhones * sizeof(*state->fromPhoneRecs));
|
|
}
|
|
}
|
|
|
|
static void
|
|
freeMsgIDRec( SMSProto* state, MsgIDRec* XP_UNUSED_DBG(rec), int fromPhoneIndex, int msgIDIndex )
|
|
{
|
|
FromPhoneRec* fromPhoneRec = &state->fromPhoneRecs[fromPhoneIndex];
|
|
MsgIDRec* msgIDRec = &fromPhoneRec->msgIDRecs[msgIDIndex];
|
|
XP_ASSERT( msgIDRec == rec );
|
|
|
|
for ( int ii = 0; ii < msgIDRec->count; ++ii ) {
|
|
XP_FREEP( state->mpool, &msgIDRec->parts[ii].data );
|
|
}
|
|
XP_FREEP( state->mpool, &msgIDRec->parts );
|
|
|
|
if ( --fromPhoneRec->nMsgIDs > 0 ) {
|
|
XP_U16 nAbove = fromPhoneRec->nMsgIDs - msgIDIndex;
|
|
XP_ASSERT( nAbove >= 0 );
|
|
if ( nAbove > 0 ) {
|
|
XP_MEMMOVE( &fromPhoneRec->msgIDRecs[msgIDIndex], &fromPhoneRec->msgIDRecs[msgIDIndex+1],
|
|
nAbove * sizeof(*fromPhoneRec->msgIDRecs) );
|
|
}
|
|
fromPhoneRec->msgIDRecs = XP_REALLOC( state->mpool, fromPhoneRec->msgIDRecs,
|
|
fromPhoneRec->nMsgIDs
|
|
* sizeof(*fromPhoneRec->msgIDRecs));
|
|
} else {
|
|
rmFromPhoneRec( state, fromPhoneIndex );
|
|
}
|
|
}
|
|
|
|
static void
|
|
savePartials( SMSProto* state, XWEnv xwe )
|
|
{
|
|
XWStreamCtxt* stream = mkStream( state );
|
|
stream_putU8( stream, PARTIALS_FORMAT );
|
|
|
|
stream_putU8( stream, state->nFromPhones );
|
|
for ( int ii = 0; ii < state->nFromPhones; ++ii ) {
|
|
const FromPhoneRec* rec = &state->fromPhoneRecs[ii];
|
|
stringToStream( stream, rec->phone );
|
|
stream_putU8( stream, rec->nMsgIDs );
|
|
for ( int jj = 0; jj < rec->nMsgIDs; ++jj ) {
|
|
MsgIDRec* mir = &rec->msgIDRecs[jj];
|
|
stream_putU16( stream, mir->msgID );
|
|
stream_putU8( stream, mir->count );
|
|
|
|
/* There's an array here. It may be sparse. Save a len of 0 */
|
|
for ( int kk = 0; kk < mir->count; ++kk ) {
|
|
int len = mir->parts[kk].len;
|
|
stream_putU8( stream, len );
|
|
stream_putBytes( stream, mir->parts[kk].data, len );
|
|
}
|
|
}
|
|
}
|
|
|
|
XP_U16 newSize = stream_getSize( stream );
|
|
if ( state->lastStoredSize == 2 && newSize == 2 ) {
|
|
XP_LOGFF( "not storing empty again" );
|
|
} else {
|
|
const XP_UCHAR* keys[] = { KEY_PARTIALS, NULL, };
|
|
dutil_storeStream( state->dutil, xwe, keys, stream );
|
|
state->lastStoredSize = newSize;
|
|
}
|
|
|
|
destroyStream( stream );
|
|
|
|
LOG_RETURN_VOID();
|
|
} /* savePartials */
|
|
|
|
static void
|
|
restorePartials( SMSProto* state, XWEnv xwe )
|
|
{
|
|
XWStreamCtxt* stream = mkStream( state );
|
|
|
|
const XP_UCHAR* keys[] = { KEY_PARTIALS, NULL, };
|
|
dutil_loadStream( state->dutil, xwe, keys, stream );
|
|
if ( stream_getSize( stream ) >= 1
|
|
&& PARTIALS_FORMAT == stream_getU8( stream ) ) {
|
|
int nFromPhones = stream_getU8( stream );
|
|
for ( int ii = 0; ii < nFromPhones; ++ii ) {
|
|
XP_UCHAR phone[32];
|
|
(void)stringFromStreamHere( stream, phone, VSIZE(phone) );
|
|
int nMsgIDs = stream_getU8( stream );
|
|
/* XP_LOGF( "%s(): got %d message records for phone %s", __func__, */
|
|
/* nMsgIDs, phone ); */
|
|
for ( int jj = 0; jj < nMsgIDs; ++jj ) {
|
|
XP_U16 msgID = stream_getU16( stream );
|
|
int count = stream_getU8( stream );
|
|
/* XP_LOGF( "%s(): got %d records for msgID %d", __func__, count, msgID ); */
|
|
for ( int kk = 0; kk < count; ++kk ) {
|
|
int len = stream_getU8( stream );
|
|
if ( 0 < len ) {
|
|
XP_U8 buf[len];
|
|
stream_getBytes( stream, buf, len );
|
|
addMessage( state, phone, msgID, kk, count, buf, len );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
destroyStream( stream );
|
|
}
|
|
|
|
static SMSMsgArray*
|
|
completeMsgs( SMSProto* state, SMSMsgArray* arr, const XP_UCHAR* fromPhone,
|
|
XP_U16 wantPort, int msgID )
|
|
{
|
|
int fromPhoneIndex, msgIDIndex;
|
|
MsgIDRec* rec = getMsgIDRec( state, fromPhone, msgID, XP_FALSE,
|
|
&fromPhoneIndex, &msgIDIndex);
|
|
if ( !rec ) {
|
|
XP_LOGFF( "no rec for phone %s, msgID %d", fromPhone, msgID );
|
|
XP_ASSERT( 0 );
|
|
}
|
|
|
|
int len = 0;
|
|
XP_Bool haveAll = XP_TRUE;
|
|
for ( int ii = 0; ii < rec->count; ++ii ) {
|
|
if ( rec->parts[ii].len == 0 ) {
|
|
haveAll = XP_FALSE;
|
|
break;
|
|
} else {
|
|
len += rec->parts[ii].len;
|
|
}
|
|
}
|
|
|
|
if ( haveAll ) {
|
|
XWStreamCtxt* stream = mkStream( state );
|
|
for ( int ii = 0; ii < rec->count; ++ii ) {
|
|
stream_putBytes( stream, rec->parts[ii].data, rec->parts[ii].len );
|
|
}
|
|
|
|
XP_U32 gameID;
|
|
XP_U16 port;
|
|
SMS_CMD cmd;
|
|
if ( headerFromStream( stream, &cmd, &port, &gameID ) ) {
|
|
XP_U16 len = stream_getSize( stream );
|
|
SMSMsgLoc msg = { .len = len,
|
|
.cmd = cmd,
|
|
.gameID = gameID,
|
|
.data = XP_MALLOC( state->mpool, len ),
|
|
};
|
|
if ( stream_gotBytes( stream, msg.data, len ) && port == wantPort ) {
|
|
arr = appendLocMsg( state, arr, &msg );
|
|
} else {
|
|
XP_LOGFF( "expected port %d, got %d", wantPort, port );
|
|
XP_FREEP( state->mpool, &msg.data );
|
|
}
|
|
}
|
|
destroyStream( stream );
|
|
|
|
freeMsgIDRec( state, rec, fromPhoneIndex, msgIDIndex );
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
|
|
static SMSMsgArray*
|
|
toNetMsgs( SMSProto* state, XWEnv xwe, ToPhoneRec* rec, XP_Bool forceOld )
|
|
{
|
|
SMSMsgArray* result = NULL;
|
|
|
|
for ( XP_U16 ii = 0; ii < rec->nMsgs; ) {
|
|
// XP_LOGF( "%s(): looking at msg %d of %d", __func__, ii, rec->nMsgs );
|
|
XP_U16 count = (rec->msgs[ii]->msgNet.len + (MAX_LEN_BINARY-1)) / MAX_LEN_BINARY;
|
|
|
|
/* First, see if this message and some number of its neighbors can be
|
|
combined */
|
|
int last = ii;
|
|
int sum = 0;
|
|
if ( count == 1 && !forceOld ) {
|
|
for ( ; last < rec->nMsgs; ++last ) {
|
|
int nextLen = rec->msgs[last]->msgNet.len;
|
|
if ( sum + nextLen > MAX_LEN_BINARY ) {
|
|
break;
|
|
}
|
|
sum += nextLen;
|
|
}
|
|
}
|
|
|
|
if ( last > ii ) {
|
|
int nMsgs = last - ii;
|
|
if ( nMsgs > 1 ) {
|
|
XP_LOGFF( "combining %d through %d (%d msgs)", ii, last - 1, nMsgs );
|
|
}
|
|
int len = 1 + sum + (nMsgs * 2); /* 1: len & msgID */
|
|
SMSMsgNet newMsg = { .len = len,
|
|
.data = XP_MALLOC( state->mpool, len )
|
|
};
|
|
int indx = 0;
|
|
newMsg.data[indx++] = SMS_PROTO_VERSION_COMBO;
|
|
for ( int jj = ii; jj < last; ++jj ) {
|
|
const SMSMsgNet* msg = &rec->msgs[jj]->msgNet;
|
|
newMsg.data[indx++] = msg->len;
|
|
newMsg.data[indx++] = nextMsgID( state, xwe );
|
|
XP_MEMCPY( &newMsg.data[indx], msg->data, msg->len ); /* bad! */
|
|
indx += msg->len;
|
|
}
|
|
result = appendNetMsg( state, result, &newMsg );
|
|
ii = last;
|
|
} else {
|
|
int msgID = nextMsgID( state, xwe );
|
|
const SMSMsgNet* msg = &rec->msgs[ii]->msgNet;
|
|
XP_U8* nextStart = msg->data;
|
|
XP_U16 lenLeft = msg->len;
|
|
for ( XP_U16 indx = 0; indx < count; ++indx ) {
|
|
XP_ASSERT( lenLeft > 0 );
|
|
XP_U16 useLen = lenLeft;
|
|
if ( useLen >= MAX_LEN_BINARY ) {
|
|
useLen = MAX_LEN_BINARY;
|
|
}
|
|
lenLeft -= useLen;
|
|
|
|
SMSMsgNet newMsg = { .len = useLen + 4,
|
|
.data = XP_MALLOC( state->mpool, useLen + 4 )
|
|
};
|
|
newMsg.data[0] = SMS_PROTO_VERSION_JAVA;
|
|
newMsg.data[1] = msgID;
|
|
newMsg.data[2] = indx;
|
|
newMsg.data[3] = count;
|
|
XP_MEMCPY( newMsg.data + 4, nextStart, useLen );
|
|
nextStart += useLen;
|
|
|
|
result = appendNetMsg( state, result, &newMsg );
|
|
}
|
|
++ii;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
} /* toMsgs */
|
|
|
|
static int
|
|
nextMsgID( SMSProto* state, XWEnv xwe )
|
|
{
|
|
int result = ++state->nNextID % 0x000000FF;
|
|
const XP_UCHAR* keys[] = { KEY_NEXTID, NULL, };
|
|
dutil_storePtr( state->dutil, xwe, keys, &state->nNextID,
|
|
sizeof(state->nNextID) );
|
|
LOG_RETURNF( "%d", result );
|
|
return result;
|
|
}
|
|
|
|
static XWStreamCtxt*
|
|
mkStream( SMSProto* state )
|
|
{
|
|
XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(state->mpool)
|
|
dutil_getVTManager(state->dutil) );
|
|
return stream;
|
|
}
|
|
|
|
static void
|
|
destroyStream( XWStreamCtxt* stream )
|
|
{
|
|
stream_destroy( stream );
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
smsproto_runTests( MPFORMAL XWEnv xwe, XW_DUtilCtxt* dutil )
|
|
{
|
|
LOG_FUNC();
|
|
SMSProto* state = smsproto_init( mpool, xwe, dutil );
|
|
|
|
const int smallSiz = 20;
|
|
const char* phones[] = {"1234", "3456", "5467", "9877"};
|
|
const char* buf = "asoidfaisdfoausdf aiousdfoiu asodfu oiuasdofi oiuaosiduf oaisudf oiasd f"
|
|
";oiaisdjfljiojaklj asdlkjalskdjf laksjd flkjasdlfkj aldsjkf lsakdjf lkjsad flkjsd fl;kj"
|
|
"asdifaoaosidfoiauosidufoaus doifuoaiusdoifu aoisudfoaisd foia sdoifuasodfu aosiud foiuas odfiu asd"
|
|
"aosdoiaosdoiisidfoiosi isoidufoisu doifuoisud oiuoi98a90iu-asjdfoiasdfij"
|
|
;
|
|
const XP_Bool forceOld = XP_TRUE;
|
|
|
|
SMSMsgArray* arrs[VSIZE(phones)];
|
|
for ( int ii = 0; ii < VSIZE(arrs); ++ii ) {
|
|
arrs[ii] = NULL;
|
|
}
|
|
|
|
/* Loop until all the messages are ready. */
|
|
const XP_U32 gameID = 12344321;
|
|
const int port = 1;
|
|
for ( XP_Bool firstTime = XP_TRUE; ; firstTime = XP_FALSE) {
|
|
XP_Bool allDone = XP_TRUE;
|
|
for ( int ii = 0; ii < VSIZE(arrs); ++ii ) {
|
|
XP_U16 waitSecs;
|
|
if ( firstTime ) {
|
|
XP_U16 len = (ii + 1) * 30;
|
|
arrs[ii] = smsproto_prepOutbound( state, xwe, DATA, gameID, buf, len, phones[ii],
|
|
port, forceOld, &waitSecs );
|
|
} else if ( NULL == arrs[ii]) {
|
|
arrs[ii] = smsproto_prepOutbound( state, xwe, DATA, gameID, NULL, 0, phones[ii],
|
|
port, forceOld, &waitSecs );
|
|
} else {
|
|
continue;
|
|
}
|
|
allDone = allDone & (waitSecs == 0 && !!arrs[ii]);
|
|
}
|
|
if ( allDone ) {
|
|
break;
|
|
} else {
|
|
(void)sleep( 2 );
|
|
}
|
|
}
|
|
|
|
for ( int indx = 0; ; ++indx ) {
|
|
XP_Bool haveOne = XP_FALSE;
|
|
for ( int ii = 0; ii < VSIZE(arrs); ++ii ) {
|
|
if (!!arrs[ii] && indx < arrs[ii]->nMsgs) {
|
|
XP_ASSERT( arrs[ii]->format == FORMAT_NET );
|
|
haveOne = XP_TRUE;
|
|
SMSMsgArray* outArr = smsproto_prepInbound( state, xwe, phones[ii], port,
|
|
arrs[ii]->u.msgsNet[indx].data,
|
|
arrs[ii]->u.msgsNet[indx].len );
|
|
if ( !!outArr ) {
|
|
XP_ASSERT( outArr->format == FORMAT_LOC );
|
|
SMSMsgLoc* msg = &outArr->u.msgsLoc[0];
|
|
XP_ASSERT( msg->gameID == gameID );
|
|
XP_ASSERT( msg->cmd == DATA );
|
|
// XP_LOGF( "%s(): got msgID %d", __func__, msg->msgID );
|
|
XP_ASSERT( outArr->nMsgs == 1 );
|
|
XP_ASSERT( 0 == memcmp(buf, msg->data, (ii + 1) * 30) );
|
|
smsproto_freeMsgArray( state, outArr );
|
|
|
|
smsproto_freeMsgArray( state, arrs[ii] );
|
|
arrs[ii] = NULL;
|
|
}
|
|
}
|
|
}
|
|
if (!haveOne) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Now let's send a bunch of small messages that should get combined */
|
|
for ( int nUsed = 0; ; ++nUsed ) {
|
|
XP_U16 waitSecs;
|
|
SMSMsgArray* sendArr = smsproto_prepOutbound( state, xwe, DATA, gameID, &buf[nUsed],
|
|
smallSiz, phones[0], port,
|
|
XP_FALSE, &waitSecs );
|
|
if ( sendArr == NULL ) {
|
|
XP_LOGF( "%s(): msg[%d] of len %d sent; still not ready", __func__, nUsed, smallSiz );
|
|
continue;
|
|
}
|
|
|
|
XP_ASSERT( waitSecs == 0 );
|
|
XP_ASSERT( sendArr->format == FORMAT_NET );
|
|
int totalBack = 0;
|
|
for ( int jj = 0; jj < sendArr->nMsgs; ++jj ) {
|
|
SMSMsgArray* recvArr = smsproto_prepInbound( state, xwe, phones[0], port,
|
|
sendArr->u.msgsNet[jj].data,
|
|
sendArr->u.msgsNet[jj].len );
|
|
|
|
if ( !!recvArr ) {
|
|
XP_ASSERT( recvArr->format == FORMAT_LOC );
|
|
XP_LOGF( "%s(): got %d msgs (from %d)", __func__, recvArr->nMsgs, nUsed + 1 );
|
|
for ( int kk = 0; kk < recvArr->nMsgs; ++kk ) {
|
|
SMSMsgLoc* msg = &recvArr->u.msgsLoc[kk];
|
|
// XP_LOGF( "%s(): got msgID %d", __func__, msg->msgID );
|
|
XP_ASSERT( msg->gameID == gameID );
|
|
XP_ASSERT( msg->cmd == DATA );
|
|
XP_ASSERT( msg->len == smallSiz );
|
|
XP_ASSERT( 0 == memcmp( msg->data, &buf[totalBack], smallSiz ) );
|
|
++totalBack;
|
|
}
|
|
|
|
smsproto_freeMsgArray( state, recvArr );
|
|
}
|
|
}
|
|
XP_ASSERT( forceOld || totalBack == nUsed + 1 );
|
|
XP_LOGF( "%s(): %d messages checked out", __func__, totalBack );
|
|
smsproto_freeMsgArray( state, sendArr );
|
|
break;
|
|
}
|
|
|
|
/* Now let's add a too-long message and unpack only the first part. Make
|
|
sure it's cleaned up correctly */
|
|
XP_U16 waitSecs;
|
|
SMSMsgArray* arr = smsproto_prepOutbound( state, xwe, DATA, gameID, buf, 200, "33333",
|
|
port, XP_TRUE, &waitSecs );
|
|
XP_ASSERT( !!arr && arr->nMsgs > 1 );
|
|
/* add only part 1 */
|
|
SMSMsgArray* out = smsproto_prepInbound( state, xwe, "33333", port, arr->u.msgsNet[0].data,
|
|
arr->u.msgsNet[0].len );
|
|
XP_ASSERT( !out );
|
|
smsproto_freeMsgArray( state, arr );
|
|
|
|
/* Try the no-buffer messages */
|
|
XP_LOGF( "%s(): trying DEATH", __func__ );
|
|
arr = smsproto_prepOutbound( state, xwe, DEATH, gameID, NULL, 0, "33333",
|
|
port, XP_TRUE, &waitSecs );
|
|
XP_ASSERT( arr->format == FORMAT_NET );
|
|
out = smsproto_prepInbound( state, xwe, "33333", port,
|
|
arr->u.msgsNet[0].data,
|
|
arr->u.msgsNet[0].len );
|
|
XP_ASSERT( out->format == FORMAT_LOC );
|
|
XP_ASSERT( out->u.msgsLoc[0].cmd == DEATH );
|
|
XP_ASSERT( out->u.msgsLoc[0].gameID == gameID );
|
|
smsproto_freeMsgArray( state, arr );
|
|
smsproto_freeMsgArray( state, out );
|
|
XP_LOGF( "%s(): DEATH checked out", __func__ );
|
|
|
|
/* Test port mismatch */
|
|
arr = smsproto_prepOutbound( state, xwe, DEATH, gameID, NULL, 0, "33333",
|
|
port, XP_TRUE, &waitSecs );
|
|
XP_ASSERT( arr->format == FORMAT_NET );
|
|
out = smsproto_prepInbound( state, xwe, "33333", port + 1,
|
|
arr->u.msgsNet[0].data,
|
|
arr->u.msgsNet[0].len );
|
|
XP_ASSERT( out == NULL );
|
|
smsproto_freeMsgArray( state, arr );
|
|
XP_LOGF( "%s(): mismatched port test done", __func__ );
|
|
|
|
/* now a message that's unpacked across multiple sessions to test store/load */
|
|
XP_LOGF( "%s(): testing store/restore", __func__ );
|
|
arr = smsproto_prepOutbound( state, xwe, DATA, gameID, (XP_U8*)buf, 200, "33333",
|
|
port, XP_TRUE, &waitSecs );
|
|
for ( int ii = 0; ii < arr->nMsgs; ++ii ) {
|
|
SMSMsgArray* out = smsproto_prepInbound( state, xwe, "33333", port,
|
|
arr->u.msgsNet[ii].data,
|
|
arr->u.msgsNet[ii].len );
|
|
if ( !!out ) {
|
|
XP_ASSERT( out->nMsgs == 1);
|
|
XP_ASSERT( out->format == FORMAT_LOC );
|
|
XP_LOGF( "%s(): got the message on the %dth loop", __func__, ii );
|
|
XP_ASSERT( out->u.msgsLoc[0].len == 200 );
|
|
XP_ASSERT( 0 == memcmp( out->u.msgsLoc[0].data, buf, 200 ) );
|
|
smsproto_freeMsgArray( state, out );
|
|
break;
|
|
}
|
|
smsproto_free( state ); /* give it a chance to store state */
|
|
state = smsproto_init( mpool, xwe, dutil );
|
|
}
|
|
|
|
/* Really bad to pass a different state than was created with, but now
|
|
since only mpool is used and it's the same for all states, let it
|
|
go. */
|
|
smsproto_freeMsgArray( state, arr ); /* give it a chance to store state */
|
|
|
|
smsproto_free( state );
|
|
LOG_RETURN_VOID();
|
|
}
|
|
#endif
|