diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java index 17cf0f21e..1716ad1e7 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/SMSService.java @@ -41,6 +41,8 @@ import org.eehouse.android.xw4.MultiService.MultiEvent; import org.eehouse.android.xw4.jni.CommsAddrRec; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.XwJNI; +import org.eehouse.android.xw4.jni.XwJNI.SMSProtoMsg; +import org.eehouse.android.xw4.jni.XwJNI.SMS_CMD; import org.eehouse.android.xw4.loc.LocUtils; import java.io.ByteArrayInputStream; @@ -82,10 +84,6 @@ public class SMSService extends XWService { private static Boolean s_showToasts = null; - // All messages are base64-encoded byte arrays. The first byte is - // always one of these. What follows depends. - private enum SMS_CMD { NONE, INVITE, DATA, DEATH, ACK, }; - private BroadcastReceiver m_sentReceiver; private BroadcastReceiver m_receiveReceiver; private OnSharedPreferenceChangeListener m_prefsListener; @@ -360,7 +358,7 @@ public class SMSService extends XWService { break; case RESEND: phone = intent.getStringExtra( PHONE ); - resendFor( phone, null, false ); + resendFor( phone ); } } @@ -377,98 +375,62 @@ public class SMSService extends XWService { private void inviteRemote( String phone, String nliData ) { - Log.i( TAG, "inviteRemote()" ); - ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 ); - DataOutputStream dos = new DataOutputStream( bas ); try { - dos.writeUTF( nliData ); - dos.flush(); - - send( SMS_CMD.INVITE, bas.toByteArray(), phone ); - } catch ( java.io.IOException ioe ) { - Log.ex( TAG, ioe ); + byte[] asBytes = nliData.getBytes( "UTF-8" ); + resendFor( phone, SMS_CMD.INVITE, 0, asBytes, true ); + } catch ( java.io.UnsupportedEncodingException uee ) { + Log.ex( TAG, uee ); } } private void ackInvite( String phone, int gameID ) { - ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 ); - DataOutputStream dos = new DataOutputStream( bas ); - try { - dos.writeInt( gameID ); - dos.flush(); - - send( SMS_CMD.ACK, bas.toByteArray(), phone ); - } catch ( java.io.IOException ioe ) { - Log.ex( TAG, ioe ); - } + resendFor( phone, SMS_CMD.ACK_INVITE, gameID, null ); } private void sendDiedPacket( String phone, int gameID ) { if ( !s_sentDied.contains(gameID) ) { - ByteArrayOutputStream bas = new ByteArrayOutputStream( 32 ); - DataOutputStream dos = new DataOutputStream( bas ); - try { - dos.writeInt( gameID ); - dos.flush(); - send( SMS_CMD.DEATH, bas.toByteArray(), phone ); - s_sentDied.add( gameID ); - } catch ( java.io.IOException ioe ) { - Log.ex( TAG, ioe ); - } + resendFor( phone, SMS_CMD.DEATH, gameID, null ); } } public int sendPacket( String phone, int gameID, byte[] bytes ) { - int nSent = -1; - ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 ); - DataOutputStream dos = new DataOutputStream( bas ); - try { - dos.writeInt( gameID ); - dos.write( bytes, 0, bytes.length ); - dos.flush(); - send( SMS_CMD.DATA, bas.toByteArray(), phone ); - nSent = bytes.length; - } catch ( java.io.IOException ioe ) { - Log.ex( TAG, ioe ); - } - return nSent; + resendFor( phone, SMS_CMD.DATA, gameID, bytes ); + return bytes.length; } - private void send( SMS_CMD cmd, byte[] bytes, String phone ) - throws java.io.IOException + private void sendOrRetry( byte[][] msgs, String toPhone, int waitSecs ) { - Log.d( TAG, "send(%s, len=%d)", cmd, bytes.length ); - ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 ); - DataOutputStream dos = new DataOutputStream( bas ); - dos.writeByte( SMS_PROTO_VERSION ); - if ( SMS_PROTO_VERSION_WITHPORT <= SMS_PROTO_VERSION ) { - dos.writeShort( getNBSPort() ); + if ( null != msgs ) { + sendBuffers( msgs, toPhone ); } - dos.writeByte( cmd.ordinal() ); - dos.write( bytes, 0, bytes.length ); - dos.flush(); - - byte[] data = bas.toByteArray(); + if ( waitSecs > 0 ) { + postResend( toPhone, waitSecs ); + } + } + private void resendFor( String toPhone, SMS_CMD cmd, int gameID, byte[] data ) + { boolean newSMSEnabled = XWPrefs.getSMSProtoEnabled( this ); boolean forceNow = !newSMSEnabled; // || cmd == SMS_CMD.INVITE; - resendFor( phone, data, forceNow ); + resendFor( toPhone, cmd, gameID, data, forceNow ); } - private void resendFor( String phone, byte[] data, boolean forceNow ) + private void resendFor( String toPhone ) + { + resendFor( toPhone, SMS_CMD.NONE, 0, null, false ); + } + + private void resendFor( String toPhone, SMS_CMD cmd, int gameID, byte[] data, + boolean forceNow ) { int[] waitSecs = { 0 }; - byte[][] msgs = XwJNI.smsproto_prepOutbound( data, phone, forceNow, waitSecs ); - if ( null != msgs ) { - sendBuffers( msgs, phone ); - } - if ( waitSecs[0] > 0 ) { - Assert.assertFalse( forceNow ); - postResend( phone, waitSecs[0] ); - } + byte[][] msgs = XwJNI.smsproto_prepOutbound( cmd, gameID, data, toPhone, + getNBSPort(), forceNow, + waitSecs ); + sendOrRetry( msgs, toPhone, waitSecs[0] ); } private void postResend( final String phone, final int waitSecs ) @@ -492,51 +454,38 @@ public class SMSService extends XWService { } ).start(); } - private void receive( SMS_CMD cmd, byte[] data, String phone ) + private void receive( SMSProtoMsg msg, String phone ) { - Log.i( TAG, "receive(cmd=%s)", cmd.toString() ); - DataInputStream dis = - new DataInputStream( new ByteArrayInputStream(data) ); - try { - switch( cmd ) { - case INVITE: - String nliData = dis.readUTF(); - makeForInvite( phone, new NetLaunchInfo( this, nliData ) ); - break; - case DATA: - int gameID = dis.readInt(); - byte[] rest = new byte[dis.available()]; - dis.readFully( rest ); - if ( feedMessage( gameID, rest, new CommsAddrRec( phone ) ) ) { - SMSResendReceiver.resetTimer( this ); - } - break; - case DEATH: - gameID = dis.readInt(); - postEvent( MultiEvent.MESSAGE_NOGAME, gameID ); - break; - case ACK: - gameID = dis.readInt(); - postEvent( MultiEvent.NEWGAME_SUCCESS, - gameID ); - break; - default: - Log.w( TAG, "unexpected cmd %s", cmd.toString() ); - break; + Log.i( TAG, "receive(cmd=%s)", msg.cmd ); + switch( msg.cmd ) { + case INVITE: + NetLaunchInfo nli = new NetLaunchInfo( this, new String(msg.data) ); + makeForInvite( phone, nli ); + break; + case DATA: + if ( feedMessage( msg.gameID, msg.data, new CommsAddrRec( phone ) ) ) { + SMSResendReceiver.resetTimer( this ); } - } catch ( java.io.IOException ioe ) { - Log.ex( TAG, ioe ); + break; + case DEATH: + postEvent( MultiEvent.MESSAGE_NOGAME, msg.gameID ); + break; + case ACK_INVITE: + postEvent( MultiEvent.NEWGAME_SUCCESS, msg.gameID ); + break; + default: + Log.w( TAG, "unexpected cmd %s", msg.cmd ); + Assert.assertFalse( BuildConfig.DEBUG ); + break; } } private void receiveBuffer( byte[] buffer, String senderPhone ) { - byte[][] msgs = XwJNI.smsproto_prepInbound( buffer, senderPhone ); + SMSProtoMsg[] msgs = XwJNI.smsproto_prepInbound( buffer, senderPhone ); if ( null != msgs ) { - for ( byte[] msg : msgs ) { - if ( !disAssemble( senderPhone, msg ) ) { - Log.e( TAG, "failed on message from %s", senderPhone ); - } + for ( SMSProtoMsg msg : msgs ) { + receive( msg, senderPhone ); } postEvent( MultiEvent.SMS_RECEIVE_OK ); } else { @@ -545,44 +494,6 @@ public class SMSService extends XWService { } } - private boolean disAssemble( String senderPhone, byte[] fullMsg ) - { - boolean success = false; - DataInputStream dis = - new DataInputStream( new ByteArrayInputStream(fullMsg) ); - try { - byte proto = dis.readByte(); - short myPort = getNBSPort(); - short gotPort; - if ( SMS_PROTO_VERSION_WITHPORT > proto ) { - gotPort = myPort; - } else { - gotPort = dis.readShort(); - } - if ( SMS_PROTO_VERSION < proto ) { - Log.w( TAG, "SMSService.disAssemble: bad proto %d from %s;" - + " dropping", proto, senderPhone ); - postEvent( MultiEvent.BAD_PROTO_SMS, senderPhone ); - } else if ( gotPort != myPort ) { - Log.d( TAG, "disAssemble(): received on port %d" - + " but expected %d", gotPort, myPort ); - } else { - SMS_CMD cmd = SMS_CMD.values()[dis.readByte()]; - byte[] rest = new byte[dis.available()]; - dis.readFully( rest ); - receive( cmd, rest, senderPhone ); - success = true; - } - } catch ( java.io.IOException ioe ) { - Log.ex( TAG, ioe ); - } catch ( ArrayIndexOutOfBoundsException oob ) { - // enum this older code doesn't know about, or just another app's - // message; drop it - Log.w( TAG, "disAssemble: dropping message with too-new enum" ); - } - return success; - } - @Override protected void postNotification( String phone, int gameID, long rowid ) { diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java index a2a701bcd..61f0a2a75 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/XwJNI.java @@ -403,15 +403,22 @@ public class XwJNI { public static native boolean comms_getAddrDisabled( GamePtr gamePtr, CommsConnType typ, boolean send ); - public static byte[][] smsproto_prepOutbound( byte[] buf, String phone, boolean forceNow, - /*out*/ int[] waitSecs ) - { - int nowSeconds = (int)(System.currentTimeMillis() / 1000); - return smsproto_prepOutbound( getJNI().m_ptr, buf, phone, nowSeconds, - forceNow, waitSecs ); + public enum SMS_CMD { NONE, INVITE, DATA, DEATH, ACK_INVITE, }; + public static class SMSProtoMsg { + public SMS_CMD cmd; + public int gameID; + public byte[] data; // other cases } - public static byte[][] smsproto_prepInbound( byte[] data, String fromPhone ) + public static byte[][] + smsproto_prepOutbound( SMS_CMD cmd, int gameID, byte[] buf, String phone, + int port, boolean forceNow, /*out*/ int[] waitSecs ) + { + return smsproto_prepOutbound( getJNI().m_ptr, cmd, gameID, buf, phone, + port, forceNow, waitSecs ); + } + + public static SMSProtoMsg[] smsproto_prepInbound( byte[] data, String fromPhone ) { return smsproto_prepInbound( getJNI().m_ptr, data, fromPhone ); } @@ -502,13 +509,14 @@ public class XwJNI { private static native int dict_iter_init( int jniState, byte[] dict, String name, String path ); - private static native byte[][] smsproto_prepOutbound( int jniState, byte[] buf, - String phone, int nowSeconds, - boolean forceNow, - /*out*/int[] waitSecs ); + private static native byte[][] + smsproto_prepOutbound( int jniState, SMS_CMD cmd, int gameID, byte[] buf, + String phone, int port, boolean forceNow, + /*out*/int[] waitSecs ); - private static native byte[][] smsproto_prepInbound( int jniState, byte[] data, - String fromPhone ); + private static native SMSProtoMsg[] smsproto_prepInbound( int jniState, + byte[] data, + String fromPhone ); private static native boolean haveEnv( int jniState ); } diff --git a/xwords4/android/jni/xwjni.c b/xwords4/android/jni/xwjni.c index f3413a878..d1057beb9 100644 --- a/xwords4/android/jni/xwjni.c +++ b/xwords4/android/jni/xwjni.c @@ -772,10 +772,11 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getTileValue static jobjectArray msgArrayToByteArrays( JNIEnv* env, const SMSMsgArray* arr ) { + XP_ASSERT( arr->format == FORMAT_NET ); jclass clas = (*env)->FindClass( env, "[B" ); jobjectArray result = (*env)->NewObjectArray( env, arr->nMsgs, clas, NULL ); for ( int ii = 0; ii < arr->nMsgs; ++ii ) { - SMSMsg* msg = &arr->msgs[ii]; + SMSMsgNet* msg = &arr->u.msgsNet[ii]; jbyteArray arr = makeByteArray( env, msg->len, (const jbyte*)msg->data ); (*env)->SetObjectArrayElement( env, result, ii, arr ); deleteLocalRef( env, arr ); @@ -783,15 +784,43 @@ msgArrayToByteArrays( JNIEnv* env, const SMSMsgArray* arr ) return result; } +static jobjectArray +msgArrayToJMsgArray( JNIEnv* env, const SMSMsgArray* arr ) +{ + XP_ASSERT( arr->format == FORMAT_LOC ); + jclass clas = (*env)->FindClass( env, PKG_PATH("jni/XwJNI$SMSProtoMsg") ); + jobjectArray result = (*env)->NewObjectArray( env, arr->nMsgs, clas, NULL ); + + jmethodID initId = (*env)->GetMethodID( env, clas, "", "()V" ); + for ( int ii = 0; ii < arr->nMsgs; ++ii ) { + jobject jmsg = (*env)->NewObject( env, clas, initId ); + + const SMSMsgLoc* msgsLoc = &arr->u.msgsLoc[ii]; + intToJenumField( env, jmsg, msgsLoc->cmd, "cmd", PKG_PATH("jni/XwJNI$SMS_CMD") ); + setInt( env, jmsg, "gameID", msgsLoc->gameID ); + + jbyteArray arr = makeByteArray( env, msgsLoc->len, + (const jbyte*)msgsLoc->data ); + setObject( env, jmsg, "data", "[B", arr ); + deleteLocalRef( env, arr ); + + (*env)->SetObjectArrayElement( env, result, ii, jmsg ); + deleteLocalRef( env, jmsg ); + } + return result; +} + JNIEXPORT jobjectArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepOutbound -( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jData, - jstring jToPhone, jint jNow, jboolean jForce, jintArray jWaitSecsArr ) +( JNIEnv* env, jclass C, jint jniGlobalPtr, jobject jCmd, jint jGameID, + jbyteArray jData, jstring jToPhone, jint jPort, jboolean jForce, + jintArray jWaitSecsArr ) { jobjectArray result = NULL; JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr; map_thread( &globalState->ti, env ); + SMS_CMD cmd = jEnumToInt( env, jCmd ); jbyte* data = NULL; int len = 0; if ( NULL != jData ) { @@ -801,8 +830,9 @@ Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepOutbound const char* toPhone = (*env)->GetStringUTFChars( env, jToPhone, NULL ); XP_U16 waitSecs; - SMSMsgArray* arr = smsproto_prepOutbound( globalState->smsProto, (const XP_U8*)data, - len, toPhone, jForce, &waitSecs ); + SMSMsgArray* arr = smsproto_prepOutbound( globalState->smsProto, cmd, jGameID, + (const XP_U8*)data, len, + toPhone, jPort, jForce, &waitSecs ); if ( !!arr ) { result = msgArrayToByteArrays( env, arr ); smsproto_freeMsgArray( globalState->smsProto, arr ); @@ -820,14 +850,14 @@ Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepOutbound JNIEXPORT jobjectArray JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepInbound -( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jData, - jstring jFromPhone ) +( JNIEnv* env, jclass C, jint jniGlobalPtr, jbyteArray jData, jstring jFromPhone ) { jobjectArray result = NULL; - JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr; - map_thread( &globalState->ti, env ); if ( !!jData ) { + JNIGlobalState* globalState = (JNIGlobalState*)jniGlobalPtr; + map_thread( &globalState->ti, env ); + int len = (*env)->GetArrayLength( env, jData ); jbyte* data = (*env)->GetByteArrayElements( env, jData, NULL ); const char* fromPhone = (*env)->GetStringUTFChars( env, jFromPhone, NULL ); @@ -835,7 +865,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_smsproto_1prepInbound SMSMsgArray* arr = smsproto_prepInbound( globalState->smsProto, fromPhone, (XP_U8*)data, len ); if ( !!arr ) { - result = msgArrayToByteArrays( env, arr ); + result = msgArrayToJMsgArray( env, arr ); smsproto_freeMsgArray( globalState->smsProto, arr ); } diff --git a/xwords4/android/scripts/getsigs.sh b/xwords4/android/scripts/getsigs.sh index ae7eaba39..24fdc6942 100755 --- a/xwords4/android/scripts/getsigs.sh +++ b/xwords4/android/scripts/getsigs.sh @@ -3,24 +3,31 @@ set -e -u NODE=xw4 +VARIANT=xw4 CLASSPATH=${CLASSPATH:-""} usage() { [ $# -gt 0 ] && echo "Error: $1" - echo "usage: $0 " + echo "usage: $0 [--variant xw4|xw4d]" exit 1 } while [ $# -gt 0 ]; do case $1 in - --help) usage - ;; - *) usage "unexpected flag $1" - ;; + --help) + usage + ;; + --variant) + VARIANT=$2 + shift + ;; + *) + usage "unexpected flag $1" + ;; esac shift done -cd $(dirname $0)/../app/build/intermediates/classes/${NODE}/debug +cd $(dirname $0)/../app/build/intermediates/classes/${VARIANT}/debug javah -o /tmp/javah$$.txt org.eehouse.android.${NODE}.jni.XwJNI javap -s org.eehouse.android.${NODE}.jni.XwJNI diff --git a/xwords4/common/nli.h b/xwords4/common/nli.h index 47f572585..f54ba3094 100644 --- a/xwords4/common/nli.h +++ b/xwords4/common/nli.h @@ -77,6 +77,7 @@ void nli_makeAddrRec( const NetLaunchInfo* invit, CommsAddrRec* addr ); void nli_setDevID( NetLaunchInfo* invit, XP_U32 devID ); void nli_setInviteID( NetLaunchInfo* invit, const XP_UCHAR* inviteID ); +void nli_setGameName( NetLaunchInfo* invit, const XP_UCHAR* gameName ); #endif diff --git a/xwords4/common/smsproto.c b/xwords4/common/smsproto.c index 19958772a..e8d9a0fba 100644 --- a/xwords4/common/smsproto.c +++ b/xwords4/common/smsproto.c @@ -38,7 +38,7 @@ typedef struct _MsgRec { XP_U32 createSeconds; - SMSMsg msg; + SMSMsgNet msgNet; } MsgRec; typedef struct _ToPhoneRec { @@ -82,11 +82,13 @@ struct SMSProto { #define KEY_NEXTID PERSIST_KEY("nextID") static int nextMsgID( SMSProto* state ); -static SMSMsgArray* toMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld ); +static XWStreamCtxt* mkStream( SMSProto* state ); +static SMSMsgArray* toNetMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld ); static ToPhoneRec* getForPhone( SMSProto* state, const XP_UCHAR* phone, XP_Bool create ); -static void addToRec( SMSProto* state, ToPhoneRec* rec, const XP_U8* buf, - XP_U16 buflen, XP_U32 nowSeconds ); +static void addToOutRec( SMSProto* state, ToPhoneRec* rec, SMS_CMD cmd, + 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, @@ -149,6 +151,43 @@ smsproto_free( SMSProto* state ) } } +static void +headerToStream( XWStreamCtxt* stream, SMS_CMD cmd, XP_U32 gameID ) +{ + // XP_LOGF( "%s(cmd: %d; gameID: %d)", __func__, cmd, gameID ); + 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_U32* gameID ) +{ + XP_Bool success = XP_FALSE; + XP_U8 tmp; + + if ( 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 @@ -159,26 +198,27 @@ smsproto_free( SMSProto* state ) * UtilCtxt around. */ SMSMsgArray* -smsproto_prepOutbound( SMSProto* state, const XP_U8* buf, - XP_U16 buflen, const XP_UCHAR* toPhone, - XP_Bool forceOld, XP_U16* waitSecsP ) +smsproto_prepOutbound( SMSProto* state, 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; #ifdef DEBUG XP_UCHAR* checksum = dutil_md5sum( state->dutil, buf, buflen ); - XP_LOGF( "%s(): len=%d, sum=%s, toPhone=%s", __func__, buflen, - checksum, toPhone ); + XP_LOGF( "%s(cmd=%d, gameID=%d): len=%d, sum=%s, toPhone=%s", __func__, cmd, + gameID, buflen, checksum, toPhone ); XP_FREEP( state->mpool, &checksum ); #endif checkThread( state ); - ToPhoneRec* rec = getForPhone( state, toPhone, !!buf ); + ToPhoneRec* rec = getForPhone( state, toPhone, cmd != NONE ); /* First, add the new message (if present) to the array */ XP_U32 nowSeconds = dutil_getCurSeconds( state->dutil ); - if ( !!buf ) { - addToRec( state, rec, buf, buflen, nowSeconds ); + if ( cmd != NONE ) { + addToOutRec( state, rec, cmd, gameID, buf, buflen, nowSeconds ); } /* rec will be non-null if there's something in it */ @@ -191,7 +231,7 @@ smsproto_prepOutbound( SMSProto* state, const XP_U8* buf, } if ( doSend ) { - result = toMsgs( state, rec, forceOld ); + result = toNetMsgs( state, rec, forceOld ); freeForPhone( state, toPhone ); } @@ -207,15 +247,34 @@ smsproto_prepOutbound( SMSProto* state, const XP_U8* buf, } static SMSMsgArray* -appendMsg( SMSProto* state, SMSMsgArray* arr, SMSMsg* msg ) +appendLocMsg( SMSProto* 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->msgs = XP_REALLOC( state->mpool, arr->msgs, - (arr->nMsgs + 1) * sizeof(*arr->msgs) ); - arr->msgs[arr->nMsgs++] = *msg; + 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* 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; } @@ -226,8 +285,7 @@ smsproto_prepInbound( SMSProto* state, const XP_UCHAR* fromPhone, XP_LOGF( "%s(): len=%d, fromPhone=%s", __func__, len, fromPhone ); checkThread( state ); - XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(state->mpool) - dutil_getVTManager(state->dutil) ); + XWStreamCtxt* stream = mkStream( state ); stream_putBytes( stream, data, len ); SMSMsgArray* result = NULL; @@ -253,20 +311,34 @@ smsproto_prepInbound( SMSProto* state, const XP_UCHAR* fromPhone, XP_U8 oneLen, msgID; while ( stream_gotU8( stream, &oneLen ) && stream_gotU8( stream, &msgID ) ) { - XP_U8 buf[oneLen]; - if ( stream_gotBytes( stream, buf, oneLen ) ) { - SMSMsg msg = { .len = oneLen, - .msgID = msgID, - .data = XP_MALLOC( state->mpool, oneLen ), - }; - XP_MEMCPY( msg.data, buf, oneLen ); - result = appendMsg( state, result, &msg ); + XP_U8 tmp[oneLen]; + stream_getBytes( stream, tmp, oneLen ); + + XWStreamCtxt* msgStream = mkStream( state ); + stream_putBytes( msgStream, tmp, oneLen ); + + XP_U32 gameID; + SMS_CMD cmd; + if ( headerFromStream( msgStream, &cmd, &gameID ) ) { + XP_U16 msgLen = stream_getSize( msgStream ); + XP_U8 buf[msgLen]; + if ( stream_gotBytes( msgStream, buf, msgLen ) ) { + 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 ); + } } + stream_destroy( msgStream ); } } break; default: XP_LOGF( "%s(): unexpected proto %d", __func__, proto ); + XP_ASSERT( 0 ); break; } } @@ -283,17 +355,31 @@ smsproto_freeMsgArray( SMSProto* state, SMSMsgArray* arr ) checkThread( state ); for ( int ii = 0; ii < arr->nMsgs; ++ii ) { - XP_FREEP( state->mpool, &arr->msgs[ii].data ); + XP_U8** ptr = arr->format == FORMAT_LOC + ? &arr->u.msgsLoc[ii].data : &arr->u.msgsNet[ii].data; + XP_FREEP( state->mpool, ptr ); } - XP_FREEP( state->mpool, &arr->msgs ); + 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 ); } static void freeMsg( SMSProto* state, MsgRec** msgp ) { - XP_FREEP( state->mpool, &(*msgp)->msg.data ); + XP_FREEP( state->mpool, &(*msgp)->msgNet.data ); XP_FREEP( state->mpool, msgp ); } @@ -353,20 +439,28 @@ freeForPhone( SMSProto* state, const XP_UCHAR* phone ) } static void -addToRec( SMSProto* state, ToPhoneRec* rec, const XP_U8* buf, XP_U16 buflen, - XP_U32 nowSeconds ) +addToOutRec( SMSProto* state, ToPhoneRec* rec, SMS_CMD cmd, + XP_U32 gameID, const XP_U8* buf, XP_U16 buflen, + XP_U32 nowSeconds ) { + XWStreamCtxt* stream = mkStream( state ); + headerToStream( stream, cmd, gameID ); + stream_putBytes( stream, buf, buflen ); + MsgRec* mRec = XP_CALLOC( state->mpool, sizeof(*rec) ); - mRec->msg.len = buflen; - mRec->msg.data = XP_MALLOC( state->mpool, buflen ); - XP_MEMCPY( mRec->msg.data, buf, buflen ); + 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 ); + stream_destroy( 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 += buflen; + rec->totalSize += len; XP_LOGF( "%s(): added msg to %s of len %d; total now %d", __func__, rec->phone, - buflen, rec->totalSize ); + len, rec->totalSize ); if ( rec->nMsgs == 1 ) { rec->createSeconds = nowSeconds; @@ -513,9 +607,7 @@ savePartials( SMSProto* state ) { checkThread( state ); - XWStreamCtxt* stream - = mem_stream_make_raw( MPPARM(state->mpool) - dutil_getVTManager(state->dutil) ); + XWStreamCtxt* stream = mkStream( state ); stream_putU8( stream, PARTIALS_FORMAT ); stream_putU8( stream, state->nFromPhones ); @@ -553,8 +645,8 @@ savePartials( SMSProto* state ) static void restorePartials( SMSProto* state ) { - XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(state->mpool) - dutil_getVTManager(state->dutil) ); + XWStreamCtxt* stream = mkStream( state ); + dutil_loadStream( state->dutil, KEY_PARTIALS, stream ); if ( stream_getSize( stream ) >= 1 && PARTIALS_FORMAT == stream_getU8( stream ) ) { @@ -607,16 +699,24 @@ completeMsgs( SMSProto* state, SMSMsgArray* arr, const XP_UCHAR* fromPhone, } if ( haveAll ) { - SMSMsg msg = { .len = len, - .msgID = msgID, - .data = XP_MALLOC( state->mpool, len ), - }; - XP_U8* ptr = msg.data; + XWStreamCtxt* stream = mkStream( state ); for ( int ii = 0; ii < rec->count; ++ii ) { - XP_MEMCPY( ptr, rec->parts[ii].data, rec->parts[ii].len ); - ptr += rec->parts[ii].len; + stream_putBytes( stream, rec->parts[ii].data, rec->parts[ii].len ); } - arr = appendMsg( state, arr, &msg ); + + XP_U32 gameID; + SMS_CMD cmd; + if ( headerFromStream( stream, &cmd, &gameID ) ) { + XP_U16 len = stream_getSize( stream ); + SMSMsgLoc msg = { .len = len, + .cmd = cmd, + .gameID = gameID, + .data = XP_MALLOC( state->mpool, len ), + }; + stream_getBytes( stream, msg.data, len ); + arr = appendLocMsg( state, arr, &msg ); + } + stream_destroy( stream ); freeMsgIDRec( state, rec, fromPhoneIndex, msgIDIndex ); } @@ -625,13 +725,13 @@ completeMsgs( SMSProto* state, SMSMsgArray* arr, const XP_UCHAR* fromPhone, } static SMSMsgArray* -toMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld ) +toNetMsgs( SMSProto* state, 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]->msg.len + (MAX_LEN_BINARY-1)) / MAX_LEN_BINARY; + 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 */ @@ -639,7 +739,7 @@ toMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld ) int sum = 0; if ( count == 1 && !forceOld ) { for ( ; last < rec->nMsgs; ++last ) { - int nextLen = rec->msgs[last]->msg.len; + int nextLen = rec->msgs[last]->msgNet.len; if ( sum + nextLen > MAX_LEN_BINARY ) { break; } @@ -654,23 +754,23 @@ toMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld ) last - 1, nMsgs ); } int len = 1 + sum + (nMsgs * 2); /* 1: len & msgID */ - SMSMsg newMsg = { .len = len, - .data = XP_MALLOC( state->mpool, len ) + 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 SMSMsg* msg = &rec->msgs[jj]->msg; + const SMSMsgNet* msg = &rec->msgs[jj]->msgNet; newMsg.data[indx++] = msg->len; newMsg.data[indx++] = nextMsgID( state ); XP_MEMCPY( &newMsg.data[indx], msg->data, msg->len ); /* bad! */ indx += msg->len; } - result = appendMsg( state, result, &newMsg ); + result = appendNetMsg( state, result, &newMsg ); ii = last; } else { int msgID = nextMsgID( state ); - const SMSMsg* msg = &rec->msgs[ii]->msg; + 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 ) { @@ -681,8 +781,8 @@ toMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld ) } lenLeft -= useLen; - SMSMsg newMsg = { .len = useLen + 4, - .data = XP_MALLOC( state->mpool, useLen + 4 ) + SMSMsgNet newMsg = { .len = useLen + 4, + .data = XP_MALLOC( state->mpool, useLen + 4 ) }; newMsg.data[0] = SMS_PROTO_VERSION; newMsg.data[1] = msgID; @@ -691,7 +791,7 @@ toMsgs( SMSProto* state, ToPhoneRec* rec, XP_Bool forceOld ) XP_MEMCPY( newMsg.data + 4, nextStart, useLen ); nextStart += useLen; - result = appendMsg( state, result, &newMsg ); + result = appendNetMsg( state, result, &newMsg ); } ++ii; } @@ -710,6 +810,14 @@ nextMsgID( SMSProto* state ) return result; } +static XWStreamCtxt* +mkStream( SMSProto* state ) +{ + XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(state->mpool) + dutil_getVTManager(state->dutil) ); + return stream; +} + #ifdef DEBUG static void checkThread( SMSProto* state ) @@ -745,17 +853,19 @@ smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil ) } /* 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, (XP_U8*)buf, len, phones[ii], - forceOld, &waitSecs ); + arrs[ii] = smsproto_prepOutbound( state, DATA, gameID, buf, len, phones[ii], + port, forceOld, &waitSecs ); } else if ( NULL == arrs[ii]) { - arrs[ii] = smsproto_prepOutbound( state, NULL, 0, phones[ii], - forceOld, &waitSecs ); + arrs[ii] = smsproto_prepOutbound( state, DATA, gameID, NULL, 0, phones[ii], + port, forceOld, &waitSecs ); } else { continue; } @@ -772,13 +882,17 @@ smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil ) 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, phones[ii], - arrs[ii]->msgs[indx].data, - arrs[ii]->msgs[indx].len ); + arrs[ii]->u.msgsNet[indx].data, + arrs[ii]->u.msgsNet[indx].len ); if ( !!outArr ) { - SMSMsg* msg = &outArr->msgs[0]; - XP_LOGF( "%s(): got msgID %d", __func__, msg->msgID ); + 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 ); @@ -796,8 +910,8 @@ smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil ) /* 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, (XP_U8*)&buf[nUsed], - smallSiz, phones[0], + SMSMsgArray* sendArr = smsproto_prepOutbound( state, 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 ); @@ -805,17 +919,21 @@ smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil ) } 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, phones[0], - sendArr->msgs[jj].data, - sendArr->msgs[jj].len ); + 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 ) { - SMSMsg* msg = &recvArr->msgs[kk]; - XP_LOGF( "%s(): got msgID %d", __func__, msg->msgID ); + 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; @@ -833,25 +951,44 @@ smsproto_runTests( MPFORMAL XW_DUtilCtxt* dutil ) /* 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, (XP_U8*)buf, 200, "33333", XP_TRUE, &waitSecs ); + SMSMsgArray* arr = smsproto_prepOutbound( state, DATA, gameID, buf, 200, "33333", + port, XP_TRUE, &waitSecs ); XP_ASSERT( !!arr && arr->nMsgs > 1 ); /* add only part 1 */ - SMSMsgArray* out = smsproto_prepInbound( state, "33333", arr->msgs[0].data, arr->msgs[0].len ); + SMSMsgArray* out = smsproto_prepInbound( state, "33333", 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, DEATH, gameID, NULL, 0, "33333", + port, XP_TRUE, &waitSecs ); + XP_ASSERT( arr->format == FORMAT_NET ); + out = smsproto_prepInbound( state, "33333", + 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__ ); /* now a message that's unpacked across multiple sessions to test store/load */ XP_LOGF( "%s(): testing store/restore", __func__ ); - arr = smsproto_prepOutbound( state, (XP_U8*)buf, 200, "33333", XP_TRUE, &waitSecs ); + arr = smsproto_prepOutbound( state, DATA, gameID, (XP_U8*)buf, 200, "33333", + port, XP_TRUE, &waitSecs ); for ( int ii = 0; ii < arr->nMsgs; ++ii ) { - SMSMsgArray* out = smsproto_prepInbound( state, "33333", arr->msgs[ii].data, - arr->msgs[ii].len ); + SMSMsgArray* out = smsproto_prepInbound( state, "33333", + 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->msgs[0].len == 200 ); - XP_ASSERT( 0 == memcmp( out->msgs[0].data, buf, 200 ) ); + XP_ASSERT( out->u.msgsLoc[0].len == 200 ); + XP_ASSERT( 0 == memcmp( out->u.msgsLoc[0].data, buf, 200 ) ); smsproto_freeMsgArray( state, out ); break; } diff --git a/xwords4/common/smsproto.h b/xwords4/common/smsproto.h index 76ad7c88f..71b7054e6 100644 --- a/xwords4/common/smsproto.h +++ b/xwords4/common/smsproto.h @@ -44,23 +44,44 @@ #include "xptypes.h" #include "mempool.h" /* debug only */ +#include "nli.h" typedef struct SMSProto SMSProto; -typedef struct _SMSMsg { +typedef enum { NONE, INVITE, DATA, DEATH, ACK_INVITE, } SMS_CMD; + +/* Unpacked/local format with relevant fields exposed */ +typedef struct _SMSMsgLoc { + // XP_U16 msgID; + SMS_CMD cmd; + XP_U32 gameID; + XP_U16 len; + XP_U8* data; // will be NetLaunchInfo* if cmd == INVITE +} SMSMsgLoc; + +/* Flattened format suitable for transmission over wire. Encapsulates Loc + format data */ +typedef struct _SMSMsgNet { + // XP_U16 msgID; XP_U16 len; - XP_U16 msgID; XP_U8* data; -} SMSMsg; +} SMSMsgNet; + +typedef enum { FORMAT_NONE, FORMAT_LOC, FORMAT_NET } SMS_FORMAT; typedef struct _SMSMsgArray { XP_U16 nMsgs; - SMSMsg* msgs; + SMS_FORMAT format; + union { + SMSMsgNet* msgsNet; + SMSMsgLoc* msgsLoc; + } u; } SMSMsgArray; struct SMSProto* smsproto_init( MPFORMAL XW_DUtilCtxt* dutil ); void smsproto_free( SMSProto* state ); + /* Return ptr to structure if one's ready to be sent, otherwise null. Caller * * should interpret null as meaning it's meant to call again. To support that, * null buf is legit. @@ -68,8 +89,10 @@ void smsproto_free( SMSProto* state ); * When send() returns a non-null value, that value must be passed to * freeMsgArray() or there will be leakage. */ -SMSMsgArray* smsproto_prepOutbound( SMSProto* state, const XP_U8* buf, - XP_U16 buflen, const XP_UCHAR* toPhone, + +SMSMsgArray* smsproto_prepOutbound( SMSProto* state, SMS_CMD cmd, + XP_U32 gameID, const void* buf, XP_U16 buflen, + const XP_UCHAR* toPhone, int port, XP_Bool forceOld, XP_U16* waitSecs ); /* When a message is received, pass it in for reassambly. Non-null return diff --git a/xwords4/common/strutils.c b/xwords4/common/strutils.c index 7ad645b8d..550381198 100644 --- a/xwords4/common/strutils.c +++ b/xwords4/common/strutils.c @@ -189,6 +189,26 @@ stream_gotU8( XWStreamCtxt* stream, XP_U8* ptr ) return success; } +XP_Bool +stream_gotU16( XWStreamCtxt* stream, XP_U16* ptr ) +{ + XP_Bool success = sizeof(*ptr) <= stream_getSize( stream ); + if ( success ) { + *ptr = stream_getU16( stream ); + } + return success; +} + +XP_Bool +stream_gotU32( XWStreamCtxt* stream, XP_U32* ptr ) +{ + XP_Bool success = sizeof(*ptr) <= stream_getSize( stream ); + if ( success ) { + *ptr = stream_getU32( stream ); + } + return success; +} + XP_Bool stream_gotBytes( XWStreamCtxt* stream, void* ptr, XP_U16 len ) { diff --git a/xwords4/common/strutils.h b/xwords4/common/strutils.h index f35a92630..631fa8c15 100644 --- a/xwords4/common/strutils.h +++ b/xwords4/common/strutils.h @@ -57,6 +57,8 @@ XP_U16 stringFromStreamHere( XWStreamCtxt* stream, XP_UCHAR* buf, XP_U16 len ); void stringToStream( XWStreamCtxt* stream, const XP_UCHAR* str ); XP_Bool stream_gotU8( XWStreamCtxt* stream, XP_U8* ptr ); +XP_Bool stream_gotU32( XWStreamCtxt* stream, XP_U32* ptr ); +XP_Bool stream_gotU16( XWStreamCtxt* stream, XP_U16* ptr ); XP_Bool stream_gotBytes( XWStreamCtxt* stream, void* ptr, XP_U16 len ); XP_UCHAR* p_copyString( MPFORMAL const XP_UCHAR* instr diff --git a/xwords4/linux/gtkboard.c b/xwords4/linux/gtkboard.c index 1849cdbc7..a55093990 100644 --- a/xwords4/linux/gtkboard.c +++ b/xwords4/linux/gtkboard.c @@ -1659,8 +1659,7 @@ send_invites( CommonGlobals* cGlobals, XP_U16 nPlayers, gchar gameName[64]; snprintf( gameName, VSIZE(gameName), "Game %d", cGlobals->gi->gameID ); - linux_sms_invite( cGlobals->params, cGlobals->gi, &addr, gameName, - nPlayers, forceChannel, + linux_sms_invite( cGlobals->params, &nli, addrs->u.sms.phone, addrs->u.sms.port ); } if ( 0 != devID || !!relayID ) { diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index 453931ba2..24648dc43 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -746,26 +746,29 @@ gtkNoticeRcvd( void* closure ) } static void -smsInviteReceived( void* closure, const XP_UCHAR* XP_UNUSED_DBG(gameName), - XP_U32 gameID, XP_U16 dictLang, const XP_UCHAR* dictName, - XP_U16 nPlayers, XP_U16 nHere, XP_U16 forceChannel, +/* smsInviteReceived( void* closure, const XP_UCHAR* XP_UNUSED_DBG(gameName), */ +/* XP_U32 gameID, XP_U16 dictLang, const XP_UCHAR* dictName, */ +/* XP_U16 nPlayers, XP_U16 nHere, XP_U16 forceChannel, */ +/* const CommsAddrRec* returnAddr ) */ +smsInviteReceived( void* closure, const NetLaunchInfo* nli, const CommsAddrRec* returnAddr ) { GtkAppGlobals* apg = (GtkAppGlobals*)closure; LaunchParams* params = apg->params; XP_LOGF( "%s(gameName=%s, gameID=%d, dictName=%s, nPlayers=%d, " - "nHere=%d, forceChannel=%d)", __func__, gameName, gameID, dictName, - nPlayers, nHere, forceChannel ); + "nHere=%d, forceChannel=%d)", __func__, nli->gameName, + nli->gameID, nli->dict, nli->nPlayersT, + nli->nPlayersH, nli->forceChannel ); CurGameInfo gi = {0}; gi_copy( MPPARM(params->mpool) &gi, ¶ms->pgi ); - gi_setNPlayers( &gi, nPlayers, nHere ); - gi.gameID = gameID; - gi.dictLang = dictLang; - gi.forceChannel = forceChannel; + gi_setNPlayers( &gi, nli->nPlayersT, nli->nPlayersH ); + gi.gameID = nli->gameID; + gi.dictLang = nli->lang; + gi.forceChannel = nli->forceChannel; gi.serverRole = SERVER_ISCLIENT; /* recipient of invitation is client */ - replaceStringIfDifferent( params->mpool, &gi.dictName, dictName ); + replaceStringIfDifferent( params->mpool, &gi.dictName, nli->dict ); GtkGameGlobals* globals = malloc( sizeof(*globals) ); params->needsNewGame = XP_FALSE; diff --git a/xwords4/linux/linuxsms.c b/xwords4/linux/linuxsms.c index 8d0693129..2ddb8f84d 100644 --- a/xwords4/linux/linuxsms.c +++ b/xwords4/linux/linuxsms.c @@ -68,12 +68,10 @@ typedef struct _LinSMSData { SMSProto* protoState; } LinSMSData; -static void doSend( LaunchParams* params, const XP_U8* buf, - XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port, - XP_U32 gameID ); static gboolean retrySend( gpointer data ); -static void sendOrRetry( LaunchParams* params, SMSMsgArray* arr, XP_U16 waitSecs, - const XP_UCHAR* phone, XP_U16 port, XP_U32 gameID ); +static void sendOrRetry( LaunchParams* params, SMSMsgArray* arr, SMS_CMD cmd, + XP_U16 waitSecs, const XP_UCHAR* phone, XP_U16 port, + XP_U32 gameID ); static gint check_for_files( gpointer data ); static gint check_for_files_once( gpointer data ); @@ -85,13 +83,7 @@ formatQueuePath( const XP_UCHAR* phone, XP_U16 port, XP_UCHAR* path, snprintf( path, pathlen, "%s/%s_%d", SMS_DIR, phone, port ); } -typedef enum { NONE, INVITE, DATA, DEATH, ACK, } SMS_CMD; -#define SMS_PROTO_VERSION 0 - - static LinSMSData* getStorage( LaunchParams* params ); -static void writeHeader( XWStreamCtxt* stream, SMS_CMD cmd ); - static void lock_queue( LinSMSData* storage ) @@ -114,7 +106,7 @@ unlock_queue( LinSMSData* storage ) } static XP_S16 -write_fake_sms( LaunchParams* params, XWStreamCtxt* stream, +write_fake_sms( LaunchParams* params, const void* buf, XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port ) { XP_S16 nSent; @@ -122,13 +114,11 @@ write_fake_sms( LaunchParams* params, XWStreamCtxt* stream, XP_Bool skipWrite = pct < params->smsSendFailPct; if ( skipWrite ) { - nSent = stream_getSize( stream ); + nSent = buflen; XP_LOGF( "%s(): dropping sms msg of len %d to phone %s", __func__, nSent, phone ); } else { LinSMSData* storage = getStorage( params ); - const XP_U8* buf = stream_getPtr( stream ); - XP_U16 buflen = stream_getSize( stream ); XP_LOGF( "%s(phone=%s, port=%d, len=%d)", __func__, phone, port, buflen ); @@ -236,81 +226,38 @@ decodeAndDelete( LinSMSData* storage, const gchar* name, return nRead; } /* decodeAndDelete */ -static void -dispatch_invite( LinSMSData* storage, XP_U16 XP_UNUSED(proto), - XWStreamCtxt* stream, CommsAddrRec* addr ) -{ - XP_UCHAR gameName[256]; - XP_UCHAR dictName[256]; - - XP_U32 gameID = stream_getU32( stream ); - XP_LOGF( "%s: got gameID %d", __func__, gameID ); - stringFromStreamHere( stream, gameName, VSIZE(gameName) ); - XP_U32 dictLang = stream_getU32( stream ); - stringFromStreamHere( stream, dictName, VSIZE(dictName) ); - XP_U8 nMissing = stream_getU8( stream ); - XP_U8 nPlayers = stream_getU8( stream ); - XP_U16 forceChannel = stream_getU8( stream ); - - addrFromStream( addr, stream ); - - (*storage->procs->inviteReceived)( storage->procClosure, gameName, - gameID, dictLang, dictName, nPlayers, - nMissing, forceChannel, addr ); -} - -static void -dispatch_data( LinSMSData* storage, XP_U16 XP_UNUSED(proto), - XWStreamCtxt* stream, const CommsAddrRec* addr ) -{ - LOG_FUNC(); - - XP_U32 gameID = stream_getU32( stream ); - XP_U16 len = stream_getSize( stream ); - XP_U8 data[len]; - stream_getBytes( stream, data, len ); - - const XP_UCHAR* fromPhone = addr->u.sms.phone; - SMSMsgArray* arr = smsproto_prepInbound( storage->protoState, fromPhone, - data, len ); - if ( NULL != arr ) { - for ( XP_U16 ii = 0; ii < arr->nMsgs; ++ii ) { - SMSMsg* msg = &arr->msgs[ii]; - (*storage->procs->msgReceived)( storage->procClosure, addr, gameID, - msg->data, msg->len ); - } - smsproto_freeMsgArray( storage->protoState, arr ); - } -} - static void parseAndDispatch( LaunchParams* params, uint8_t* buf, int len, CommsAddrRec* addr ) { LinSMSData* storage = getStorage( params ); - XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(params->mpool) - params->vtMgr ); - stream_setVersion( stream, CUR_STREAM_VERS ); - stream_putBytes( stream, buf, len ); - - XP_U8 proto = stream_getU8( stream ); - XP_ASSERT( SMS_PROTO_VERSION == proto ); - XP_U8 cmd = stream_getU8( stream ); - switch( cmd ) { - case INVITE: - dispatch_invite( storage, proto, stream, addr ); - break; - case DATA: - dispatch_data( storage, proto, stream, addr ); - break; - case DEATH: - case ACK: - break; - default: - XP_ASSERT( 0 ); + const XP_UCHAR* fromPhone = addr->u.sms.phone; + SMSMsgArray* arr = smsproto_prepInbound( storage->protoState, fromPhone, + buf, len ); + if ( NULL != arr ) { + XP_ASSERT( arr->format == FORMAT_LOC ); + for ( XP_U16 ii = 0; ii < arr->nMsgs; ++ii ) { + SMSMsgLoc* msg = &arr->u.msgsLoc[ii]; + switch ( msg->cmd ) { + case DATA: + (*storage->procs->msgReceived)( storage->procClosure, addr, + msg->gameID, + msg->data, msg->len ); + break; + case INVITE: + (*storage->procs->inviteReceived)( storage->procClosure, + (NetLaunchInfo*)msg->data, addr ); + /* gameName, */ + /* gameID, dictLang, dictName, nPlayers, */ + /* nMissing, forceChannel, addr ); */ + break; + default: + XP_ASSERT(0); /* implement me!! */ + break; + } + } + smsproto_freeMsgArray( storage->protoState, arr ); } - - stream_destroy( stream ); } void @@ -341,28 +288,18 @@ linux_sms_init( LaunchParams* params, const gchar* myPhone, XP_U16 myPort, } /* linux_sms_init */ void -linux_sms_invite( LaunchParams* params, const CurGameInfo* gi, - const CommsAddrRec* addr, const gchar* gameName, - XP_U16 nMissing, int forceChannel, +linux_sms_invite( LaunchParams* params, const NetLaunchInfo* nli, const gchar* toPhone, int toPort ) { LOG_FUNC(); - XWStreamCtxt* stream; - stream = mem_stream_make_raw( MPPARM(params->mpool) params->vtMgr ); - writeHeader( stream, INVITE ); - stream_putU32( stream, gi->gameID ); - stringToStream( stream, gameName ); - stream_putU32( stream, gi->dictLang ); - stringToStream( stream, gi->dictName ); - stream_putU8( stream, nMissing ); - stream_putU8( stream, gi->nPlayers ); - stream_putU8( stream, forceChannel ); + LinSMSData* storage = getStorage( params ); - addrToStream( stream, addr ); - - write_fake_sms( params, stream, toPhone, toPort ); - - stream_destroy( stream ); + XP_U16 waitSecs; + SMSMsgArray* arr + = smsproto_prepOutbound( storage->protoState, INVITE, nli->gameID, nli, + sizeof(*nli), toPhone, toPort, XP_FALSE, + &waitSecs ); + sendOrRetry( params, arr, INVITE, waitSecs, toPhone, toPort, nli->gameID ); } XP_S16 @@ -372,43 +309,32 @@ linux_sms_send( LaunchParams* params, const XP_U8* buf, { LinSMSData* storage = getStorage( params ); XP_U16 waitSecs; - SMSMsgArray* arr = smsproto_prepOutbound( storage->protoState, buf, buflen, - phone, XP_TRUE, &waitSecs ); - sendOrRetry( params, arr, waitSecs, phone, port, gameID ); + SMSMsgArray* arr = smsproto_prepOutbound( storage->protoState, DATA, gameID, + buf, buflen, phone, port, + XP_TRUE, &waitSecs ); + sendOrRetry( params, arr, DATA, waitSecs, phone, port, gameID ); return buflen; } -static void -doSend( LaunchParams* params, const XP_U8* buf, - XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port, - XP_U32 gameID ) -{ - XP_LOGF( "%s(len=%d)", __func__, buflen ); - XWStreamCtxt* stream = mem_stream_make_raw( MPPARM(params->mpool) - params->vtMgr ); - writeHeader( stream, DATA ); - stream_putU32( stream, gameID ); - stream_putBytes( stream, buf, buflen ); - - (void)write_fake_sms( params, stream, phone, port ); - stream_destroy( stream ); -} - typedef struct _RetryClosure { LaunchParams* params; + SMS_CMD cmd; XP_U16 port; XP_U32 gameID; XP_UCHAR phone[32]; } RetryClosure; static void -sendOrRetry( LaunchParams* params, SMSMsgArray* arr, XP_U16 waitSecs, - const XP_UCHAR* phone, XP_U16 port, XP_U32 gameID ) +sendOrRetry( LaunchParams* params, SMSMsgArray* arr, SMS_CMD cmd, + XP_U16 waitSecs, const XP_UCHAR* phone, XP_U16 port, + XP_U32 gameID ) { if ( !!arr ) { for ( XP_U16 ii = 0; ii < arr->nMsgs; ++ii ) { - SMSMsg* msg = &arr->msgs[ii]; - doSend( params, msg->data, msg->len, phone, port, gameID ); + const SMSMsgNet* msg = &arr->u.msgsNet[ii]; + // doSend( params, msg->data, msg->len, phone, port, gameID ); + (void)write_fake_sms( params, msg->data, msg->len, + phone, port ); } LinSMSData* storage = getStorage( params ); @@ -420,6 +346,7 @@ sendOrRetry( LaunchParams* params, SMSMsgArray* arr, XP_U16 waitSecs, XP_STRCAT( closure->phone, phone ); closure->port = port; closure->gameID = gameID; + closure->cmd = cmd; g_timeout_add_seconds( 5, retrySend, closure ); } } @@ -430,9 +357,11 @@ retrySend( gpointer data ) RetryClosure* closure = (RetryClosure*)data; LinSMSData* storage = getStorage( closure->params ); XP_U16 waitSecs; - SMSMsgArray* arr = smsproto_prepOutbound( storage->protoState, NULL, 0, - closure->phone, XP_TRUE, &waitSecs ); - sendOrRetry( closure->params, arr, waitSecs, closure->phone, + SMSMsgArray* arr = smsproto_prepOutbound( storage->protoState, closure->cmd, + closure->gameID, NULL, 0, + closure->phone, closure->port, + XP_TRUE, &waitSecs ); + sendOrRetry( closure->params, arr, closure->cmd, waitSecs, closure->phone, closure->port, closure->gameID ); XP_FREEP( closure->params->mpool, &closure ); return FALSE; @@ -538,11 +467,4 @@ getStorage( LaunchParams* params ) return storage; } -static void -writeHeader( XWStreamCtxt* stream, SMS_CMD cmd ) -{ - stream_putU8( stream, SMS_PROTO_VERSION ); - stream_putU8( stream, cmd ); -} - #endif /* XWFEATURE_SMS */ diff --git a/xwords4/linux/linuxsms.h b/xwords4/linux/linuxsms.h index dd96d76fb..323b3e504 100644 --- a/xwords4/linux/linuxsms.h +++ b/xwords4/linux/linuxsms.h @@ -23,12 +23,10 @@ #ifdef XWFEATURE_SMS #include "main.h" +#include "nli.h" typedef struct _SMSProcs { - void (*inviteReceived)( void* closure, const XP_UCHAR* gameName, - XP_U32 gameID, XP_U16 dictLang, - const XP_UCHAR* dictName, XP_U16 nPlayers, - XP_U16 nHere, XP_U16 forceChannel, + void (*inviteReceived)( void* closure, const NetLaunchInfo* nli, const CommsAddrRec* returnAddr ); void (*msgReceived)( void* closure, const CommsAddrRec* from, XP_U32 gameID, const XP_U8* buf, XP_U16 len ); @@ -45,9 +43,7 @@ void linux_sms_init( LaunchParams* params, const gchar* phone, XP_S16 linux_sms_send( LaunchParams* params, const XP_U8* buf, XP_U16 buflen, const XP_UCHAR* phone, XP_U16 port, XP_U32 gameID ); -void linux_sms_invite( LaunchParams* params, const CurGameInfo* info, - const CommsAddrRec* addr, const gchar* gameName, - XP_U16 nMissing, int forceChannel, +void linux_sms_invite( LaunchParams* params, const NetLaunchInfo* nli, const gchar* phone, int port ); void linux_sms_cleanup( LaunchParams* params );