mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-27 09:58:45 +01:00
dc82fc6bff
too and assert output is the same.
267 lines
7.6 KiB
C
267 lines
7.6 KiB
C
/* -*-mode: C; compile-command: "make -j MEMDEBUG=TRUE";-*- */
|
|
/*
|
|
* Copyright 2006-2009 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.
|
|
*/
|
|
|
|
#ifdef XWFEATURE_SMS
|
|
|
|
#include <glib.h>
|
|
#include <glib/gstdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/inotify.h>
|
|
|
|
#include "linuxsms.h"
|
|
#include "strutils.h"
|
|
|
|
#define SMS_DIR "/tmp/xw_sms"
|
|
#define LOCK_FILE ".lock"
|
|
|
|
/* The idea here is to mimic an SMS-based transport using files. We'll use a
|
|
* directory in /tmp to hold "messages", in files with names based on phone
|
|
* number and port. Servers and clients will listen for changes in that
|
|
* directory and "receive" messages when a change is noted to a file with
|
|
* their phone number and on the port they listen to.
|
|
*
|
|
* Server's phone number is known to both server and client and passed on the
|
|
* commandline. Client's "phone number" is the PID of the client process.
|
|
* When the client sends to the server the phone number is passed in and its
|
|
* return (own) number is included in the "message".
|
|
*
|
|
* If I'm the server, I create an empty file for each port I listen on. (Only
|
|
* one now....). Clients will append to that. Likewise, a client creates a
|
|
* file that it will listen on.
|
|
*
|
|
* Data is encoded using the same mechanism so size constraints can be
|
|
* checked.
|
|
*/
|
|
|
|
struct LinSMSData {
|
|
XP_UCHAR myPhone[MAX_PHONE_LEN+1];
|
|
XP_UCHAR myQueue[256];
|
|
FILE* lock;
|
|
int wd, fd; /* for inotify */
|
|
void* storage;
|
|
XP_U16 count;
|
|
XP_U16 port;
|
|
XP_Bool amServer;
|
|
};
|
|
|
|
static void
|
|
makeQueuePath( const XP_UCHAR* phone, XP_U16 port,
|
|
XP_UCHAR* path, XP_U16 pathlen )
|
|
{
|
|
snprintf( path, pathlen, "%s/%s_%d", SMS_DIR, phone, port );
|
|
}
|
|
|
|
static void
|
|
lock_queue( LinSMSData* data )
|
|
{
|
|
char lock[256];
|
|
snprintf( lock, sizeof(lock), "%s/%s", data->myQueue, LOCK_FILE );
|
|
FILE* fp = fopen( lock, "w" );
|
|
XP_ASSERT( NULL == data->lock );
|
|
data->lock = fp;
|
|
}
|
|
|
|
static void
|
|
unlock_queue( LinSMSData* data )
|
|
{
|
|
XP_ASSERT( NULL != data->lock );
|
|
fclose( data->lock );
|
|
data->lock = NULL;
|
|
}
|
|
|
|
void
|
|
linux_sms_init( CommonGlobals* globals, const CommsAddrRec* addr )
|
|
{
|
|
LinSMSData* data = globals->smsData;
|
|
if ( !data ) {
|
|
data = XP_MALLOC( globals->params->util->mpool, sizeof(*data) );
|
|
XP_ASSERT( !!data );
|
|
XP_MEMSET( data, 0, sizeof(*data) );
|
|
globals->smsData = data;
|
|
|
|
data->amServer = comms_getIsServer( globals->game.comms );
|
|
|
|
if ( data->amServer ) {
|
|
XP_STRNCPY( data->myPhone, addr->u.sms.phone,
|
|
sizeof(data->myPhone) );
|
|
} else {
|
|
snprintf( data->myPhone, sizeof(data->myPhone), "%.6d", getpid() );
|
|
}
|
|
|
|
makeQueuePath( data->myPhone, addr->u.sms.port,
|
|
data->myQueue, sizeof(data->myQueue) );
|
|
data->port = addr->u.sms.port;
|
|
|
|
(void)g_mkdir_with_parents( data->myQueue, 0777 );
|
|
|
|
int fd = inotify_init();
|
|
data->fd = fd;
|
|
data->wd = inotify_add_watch( fd, data->myQueue, IN_MODIFY );
|
|
(*globals->socketChanged)( globals, -1, fd, &data->storage );
|
|
}
|
|
} /* linux_sms_init */
|
|
|
|
void
|
|
linux_sms_close( CommonGlobals* globals )
|
|
{
|
|
LinSMSData* data = globals->smsData;
|
|
if ( !!data ) {
|
|
XP_FREE( globals->params->util->mpool, data );
|
|
globals->smsData = NULL;
|
|
}
|
|
} /* linux_sms_close */
|
|
|
|
XP_S16
|
|
linux_sms_send( CommonGlobals* globals,
|
|
const XP_U8* buf, XP_U16 buflen,
|
|
const XP_UCHAR* phone, XP_U16 port )
|
|
{
|
|
XP_S16 nSent = -1;
|
|
LinSMSData* data = globals->smsData;
|
|
XP_ASSERT( !!data );
|
|
char path[256];
|
|
|
|
lock_queue( data );
|
|
|
|
#ifdef DEBUG
|
|
gchar* str64 = g_base64_encode( buf, buflen );
|
|
#endif
|
|
|
|
makeQueuePath( phone, port, path, sizeof(path) );
|
|
g_mkdir_with_parents( path, 0777 ); /* just in case */
|
|
int len = strlen( path );
|
|
snprintf( &path[len], sizeof(path)-len, "/%d", ++data->count );
|
|
|
|
XP_UCHAR sms[buflen*2]; /* more like (buflen*4/3) */
|
|
XP_U16 smslen = sizeof(sms);
|
|
binToSms( sms, &smslen, buf, buflen );
|
|
XP_ASSERT( smslen == strlen(sms) );
|
|
|
|
#ifdef DEBUG
|
|
XP_ASSERT( !strcmp( str64, sms ) );
|
|
g_free( str64 );
|
|
|
|
XP_U8 testout[buflen];
|
|
XP_U16 lenout = sizeof( testout );
|
|
XP_ASSERT( smsToBin( testout, &lenout, sms, smslen ) );
|
|
XP_ASSERT( lenout == buflen );
|
|
XP_ASSERT( XP_MEMCMP( testout, buf, smslen ) );
|
|
#endif
|
|
|
|
FILE* fp = fopen( path, "w" );
|
|
XP_ASSERT( !!fp );
|
|
(void)fprintf( fp, "from: %s\n", data->myPhone );
|
|
(void)fprintf( fp, "%s\n", sms );
|
|
fclose( fp );
|
|
sync();
|
|
|
|
unlock_queue( data );
|
|
|
|
nSent = buflen;
|
|
|
|
return nSent;
|
|
} /* linux_sms_send */
|
|
|
|
static XP_S16
|
|
decodeAndDelete( LinSMSData* data, const gchar* name,
|
|
XP_U8* buf, XP_U16 buflen, CommsAddrRec* addr )
|
|
{
|
|
XP_S16 nRead = -1;
|
|
char path[256];
|
|
snprintf( path, sizeof(path), "%s/%s", data->myQueue, name );
|
|
|
|
gchar* contents;
|
|
gsize length;
|
|
#ifdef DEBUG
|
|
gboolean success =
|
|
#endif
|
|
g_file_get_contents( path, &contents, &length, NULL );
|
|
XP_ASSERT( success );
|
|
unlink( path );
|
|
|
|
if ( 0 == strncmp( "from: ", contents, 6 ) ) {
|
|
gchar* eol = strstr( contents, "\n" );
|
|
*eol = '\0';
|
|
XP_STRNCPY( addr->u.sms.phone, &contents[6], sizeof(addr->u.sms.phone) );
|
|
XP_ASSERT( !*eol );
|
|
++eol; /* skip NULL */
|
|
*strstr(eol, "\n" ) = '\0';
|
|
|
|
XP_U16 inlen = strlen(eol); /* skip \n */
|
|
XP_U8 out[inlen];
|
|
XP_U16 outlen = sizeof(out);
|
|
XP_Bool valid = smsToBin( out, &outlen, eol, inlen );
|
|
|
|
if ( valid && outlen <= buflen ) {
|
|
XP_MEMCPY( buf, out, outlen );
|
|
nRead = outlen;
|
|
addr->conType = COMMS_CONN_SMS;
|
|
addr->u.sms.port = data->port;
|
|
}
|
|
}
|
|
|
|
g_free( contents );
|
|
|
|
return nRead;
|
|
} /* decodeAndDelete */
|
|
|
|
XP_S16
|
|
linux_sms_receive( CommonGlobals* globals, int sock,
|
|
XP_U8* buf, XP_U16 buflen, CommsAddrRec* addr )
|
|
{
|
|
XP_S16 nRead = -1;
|
|
LinSMSData* data = globals->smsData;
|
|
|
|
XP_ASSERT( sock == data->fd );
|
|
|
|
lock_queue( data );
|
|
|
|
/* read required or we'll just get the event again. But we don't care
|
|
about the result or the buffer contents. */
|
|
XP_U8 buffer[sizeof(struct inotify_event) + 16];
|
|
if ( 0 > read( sock, buffer, sizeof(buffer) ) ) {
|
|
}
|
|
char shortest[256] = { '\0' };
|
|
GDir* dir = g_dir_open( data->myQueue, 0, NULL );
|
|
for ( ; ; ) {
|
|
const gchar* name = g_dir_read_name( dir );
|
|
if ( NULL == name ) {
|
|
break;
|
|
} else if ( 0 == strcmp( name, LOCK_FILE ) ) {
|
|
continue;
|
|
}
|
|
if ( !shortest[0] || 0 < strcmp( shortest, name ) ) {
|
|
snprintf( shortest, sizeof(shortest), "%s", name );
|
|
}
|
|
}
|
|
g_dir_close( dir );
|
|
|
|
if ( !!shortest[0] ) {
|
|
nRead = decodeAndDelete( data, shortest, buf, buflen, addr );
|
|
}
|
|
|
|
unlock_queue( data );
|
|
|
|
return nRead;
|
|
} /* linux_sms_receive */
|
|
|
|
#endif /* XWFEATURE_SMS */
|