+#define WANT_DECLARATIONS 1
#include "config.h"
#ifndef lint
static char rcsid[] ATTR_UNUSED = "$Id$";
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "getopt.h"
#endif
+
+/* Always override possible system defintions as it is safe (used by glib) */
+#undef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#undef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
#define NELEM(x) (sizeof((x))/sizeof(*(x)))
#ifndef DEBUG
static char *logoname,*gsmnet;
/* --ring-send specific */
static char *ringname;
+/* --picture-send specific */
+static char *picturename;
static enum modenum {
MODE_UNKNOWN=0,
MODE_SEND_MOBILDOCK=MODE_FIRST+1, /* --send-mobildock in before readtimen is set */
MODE_RECEIVE =MODE_FIRST+2, /* --receive */
MODE_LOGO_SEND =MODE_FIRST+3, /* --logo-send */
- MODE_RING_SEND =MODE_FIRST+4 /* --ring-send */
+ MODE_RING_SEND =MODE_FIRST+4, /* --ring-send */
+ MODE_PICTURE_SEND =MODE_FIRST+5 /* --picture-send */
} mode=MODE_UNKNOWN;
#define MODE_ORDER(x) ((x)-MODE_FIRST)
#define MODE_NAME(x) (longopts[MODE_ORDER((x))].name)
<dest. phone> <logo filename> [<GSMnet id>]\n\
--ring-send:\n\
<dest. phone> <ring filename>\n\
+ --picture-send:\n\
+ <dest. phone> <picture .ras filename>\n\
\n\
-c, --config\tRead this additional config file\n\
\t\t(def. \"%s\" and \"$HOME%s\")\n\
{"receive" ,0,0,MODE_RECEIVE},
{"logo-send" ,0,0,MODE_LOGO_SEND},
{"ring-send" ,0,0,MODE_RING_SEND},
+{"picture-send" ,0,0,MODE_PICTURE_SEND},
/* Mode aliases may follow in no particular order *
* as long as no non-mode options is between them */
{"send-md" ,0,0,MODE_SEND_MOBILDOCK},
{"recv" ,0,0,MODE_RECEIVE},
{"logo" ,0,0,MODE_LOGO_SEND},
{"ring" ,0,0,MODE_RING_SEND},
+{"picture" ,0,0,MODE_PICTURE_SEND},
{"config" ,1,0,'c'},
{"device" ,1,0,'d'},
{"log" ,1,0,'L'},
case MODE_RECEIVE:
case MODE_LOGO_SEND:
case MODE_RING_SEND:
+ case MODE_PICTURE_SEND:
if (mode_stamp && mode_stamp!=seq) break;
mode=optc;
mode_stamp=seq;
readbody++;
break;
case 'v':
+#ifndef DEBUG /* prevent suppositious overflow */
verbose++;
+#endif
break;
case 'V':
fprintf(stderr,version);
} nullcheck[]={
{&phone,MODE_BIT(MODE_SEND)|MODE_BIT(MODE_SEND_MOBILDOCK)|MODE_BIT(MODE_LOGO_SEND),
N_("destination phone number")},
- {&logoname,MODE_BIT(MODE_LOGO_SEND),N_("logo filename")},
- {&ringname,MODE_BIT(MODE_RING_SEND),N_("ring filename")},
+ {& logoname,MODE_BIT( MODE_LOGO_SEND),N_( "logo filename")},
+ {& ringname,MODE_BIT( MODE_RING_SEND),N_( "ring filename")},
+ {&picturename,MODE_BIT(MODE_PICTURE_SEND),N_("picture filename")},
{&body,MODE_BIT(MODE_RECEIVE),N_("body text")}, /* we allow empty bodies for SENDs */
#if 0
{&gsmnet,MODE_BIT(MODE_LOGO_SEND),N_("GSM operator network code")},
if (!ringname) ringname=nextargstack();
}
+static inline void cmdline_picture_send(void)
+{
+ cmdline_phone();
+ if (!picturename) picturename=nextargstack();
+}
+
static void lockclose(int fd)
{
if (close(fd))
error(_("Error closing lockfile \"%s\""),lockreal);
}
-static inline int lockdevice(int attempt)
+static int lockdevice(int attempt)
{
int fd=-1;
char buf[64];
static char *catchdata;
static size_t catchdatal,catchdatasiz;
-static char *reform(const char *s,int slot);
+static const char *reform(const char *s,int slot);
static void catched(const char *end,char edata)
{
size_t len;
if (verbose>=2) error(_(".Retrying phase, %d out of %ld.."),retrycnt,maxretryn);
}
-static char *reform(const char *s,int slot)
+static const char *reform(const char *s,int slot)
{
static struct formslot {
char *s;
return(fs->s);
}
+static char devcmd_empty_return='\0'; /* returned as catch when only newlines found */
+
static char *devcmd(const char *term,const char *catch,const char *send,...) ATTR_PRINTFORMAT(3,4);
static char *devcmd(const char *term,const char *catch,const char *send,...)
{
got=read(devfd,buf+bufl,1);
}
if (got<=0) {
+ buf[bufl]='\0'; /* for strspn() below */
+ if (!strcmp(term,"\n") && catch /* written to trap on 'devcmd("\n","+CMT:"," ")' */
+ && bufl==strspn(buf,"\n" /* accept */)) { /* only newlines found */
+ if (verbose>=2)
+ error(_(".Blank (%d newlines) input read, ignoring it"),bufl);
+ bufl=0; /* discard newlines */
+ return(&devcmd_empty_return);
+ }
if (wasalarm) error(_("Maximum response timeout (%lds) exceeded"),alarmtime);
else error(_("^Couldn't read device data (ret=%d)"),got);
goto err;
0x05, /* IEI */
0x04, /* IEDL */
0x15, 0x83, /* dest port (group gfx) */
- 0x00, 0x00 /* src port (unused) */
+ 0x15, 0x83 /* src port (unused) */
};
size_t got,r=0 /* GCC happiness */,w;
ssize_t chars,bits;
#undef WORD
}
+#define PICTURE_WIDTH (72)
+#define PICTURE_HEIGHT (28)
+
+static inline void pictureread(void)
+{
+FILE *f;
+unsigned char bin1[140]={
+ 6, /* UDH length */
+ 0x05, /* IEI */
+ 0x04, /* IEDL */
+ 0x15, 0x8A, /* dest port (ring tones) */
+ 0x15, 0x8A /* src port (unused) */
+#define BIN1_PAYLOAD (140-7)
+ };
+unsigned char binn[140]={
+ 11, /* UDH length */
+ 0x05, /* IEI */
+ 0x04, /* IEDL */
+ 0x15, 0x8A, /* dest port (ring tones) */
+ 0x15, 0x8A, /* src port (unused) */
+ 0x00, 0x03, /* multipart */
+ /* 0x??, unique serial ID */
+ /* 0x??, total messages */
+ /* 0x??, message number (# from 1) */
+#define BINN_PAYLOAD (140-12)
+ };
+unsigned char header[]={
+ 0x30, /* version string '0' */
+ 0x02, /* item=OTA bitmap */
+#define PICTURE_BYTES (((PICTURE_WIDTH+7)/8)*PICTURE_HEIGHT)
+#define PICTURE_BYTES_INCL_HEADER (PICTURE_BYTES +4/*header*/)
+ PICTURE_BYTES_INCL_HEADER>>8,PICTURE_BYTES_INCL_HEADER&0xFF, /* picture size in bytes incl. header */
+ 0x00, /* animation pictures - 0=static picture */
+ PICTURE_WIDTH,PICTURE_HEIGHT, /* picture size in pixels */
+ 0x01, /* picture depth - B/W */
+ };
+size_t got,want;
+int totn,fragn;
+long size;
+
+#define WORD(n) (((unsigned char)buf[(n)])|(((unsigned char)buf[(n)+1])<<8))
+
+ if (!(f=fopen(picturename,"rb")))
+ error(_("^!Cannot open picture file \"%s\" for r/o"),picturename);
+ if ((size=getfilesize(f,picturename))==-1)
+ error(_("!File size determination is essential to continue operation"));
+ if (size!=PICTURE_BYTES)
+ error(_("!File \"%s\" size %ld doesn't match .res size for %dx%d picture"),
+ picturename,size,PICTURE_WIDTH,PICTURE_HEIGHT);
+ if (size<=BIN1_PAYLOAD-sizeof(header)) {
+ memcpy(bin1+7,header,sizeof(header));
+ if ((got=fread(bin1+7+sizeof(header),1,size,f))!=size)
+ error(_("^Read error on \"%s\", wanted %ld, got %d"),picturename,size,got);
+ error(_("\nSending picture \"%s\" as single SMS (size %ld, max %d)"),
+ picturename,size,BIN1_PAYLOAD-sizeof(header));
+ nokiaprep(bin1,7+sizeof(header)+size);
+ }
+ else {
+ memcpy(binn+12,header,sizeof(header));
+ totn=(sizeof(header)+size+BINN_PAYLOAD-1)/BINN_PAYLOAD;
+ if (totn>0xFF)
+ error(_("!File size %ld too large even for multi-SMS picture upload (max=%d)"),
+ size,BINN_PAYLOAD*0xFF-sizeof(header));
+ binn[10]=totn;
+ if (verbose>=1)
+ error(_("\nSending picture \"%s\" as %d multi-SMSes (size %ld, max %d, frag %d, header %d)"),
+ picturename,totn,size,BIN1_PAYLOAD,BINN_PAYLOAD,sizeof(header));
+ binn[9]=time(NULL)&0x100; /* rand() would be better but it is a compatibility pain */
+ if (verbose>=1)
+ error(_("\nUsing unique multi-SMS ID 0x%02X"),(unsigned)binn[9]);
+ for (fragn=1;fragn<=totn;fragn++) {
+size_t isheader=(fragn==1 ? sizeof(header) : 0);
+
+ binn[11]=fragn;
+ want=MIN(size,BINN_PAYLOAD-isheader);
+ if ((got=fread(binn+12+isheader,1,want,f))!=want)
+ error(_("^Read error on \"%s\", wanted %d, got %d"),picturename,want,got);
+ nokiaprep(binn,12+isheader+want);
+ size-=want;
+ }
+ }
+ chkfclose(f,picturename);
+#undef WORD
+}
+
static inline void genpdu(void)
{
static unsigned char pdu[64+MAXNUMLEN/2+(MAXBODYLEN*7)/8];
assert(devfd>=0);
retry:
- errno=0;
-
+ if (!immed && verbose>=2)
+ error(_(".Waiting for device incoming data.."));
#ifdef HAVE_POLL
ufd.fd=devfd;
ufd.events=POLLIN;
ufd.revents=0;
+ errno=0;
i=poll(&ufd,1,(immed?0:-1));
#else /* HAVE_POLL */
#ifdef HAVE_FD_SETSIZE
#endif /* HAVE_FD_SETSIZE */
FD_ZERO(&rfds); FD_SET(devfd,&rfds);
FD_ZERO(&xfds); FD_SET(devfd,&xfds);
+ errno=0;
i=select(devfd+1,&rfds,NULL,&xfds,NULL);
#endif /* HAVE_POLL */
if (immed && i==0) return(0);
+ if (i==-1 && errno==EINTR)
+ goto retry; /* silent retry, for example SIGCHLD could occur */
if (i!=1)
error(_("^Failed (retval %d) while waiting for data, ignoring"),i);
static char *check_format(const char *fmt,const char *string)
{
static char err[LINE_MAX],sub[50];
-char *subp,cf,cs;
-const char *sf,*ss;
+char cf,cs;
+const char *sf,*ss,*subp;
for (sf=fmt,ss=string;(cf=*sf) && (cs=*ss);sf++,ss++) {
subp=NULL;
memset(&tm,0,sizeof(tm)); /* may be redundant */
for (i=0;i<NELEM(timeparse);i++)
GETTIME(i)=DIGIT2ASC(s+timeparse[i].strpos);
- if (tm.tm_year<70) tm.tm_year+=100;
- tm.tm_mon--;
maketime(s+2);
return(1);
#undef DIGIT2ASC
}
+static void signal_chld(int signo)
+{
+int status;
+pid_t pid;
+
+ signal(SIGCHLD,(RETSIGTYPE (*)(int))signal_chld);
+ /* we don't care about siginterrupt(3) as it doesn't matter how it is set */
+
+ d2("signal_chld: signo=%d\n",signo);
+ while (0<(pid=waitpid(-1 /* ANY process */,&status,WNOHANG /* options */))) {
+ if (verbose>=2)
+ error(_(".Child process w/PID %d has exited, %s, status=%d"),
+ pid,(WIFEXITED(status)? _("normally") : _("abnormally")),(WIFEXITED(status) ? WEXITSTATUS(status) : -1));
+ }
+}
+
static void receive_text(char *bodyline)
{
char *buf,*s,*s1,*s2,*s3;
FILE *f;
d2("receive_text: %s\n",bodyline);
+ signal(SIGCHLD,(RETSIGTYPE (*)(int))signal_chld);
#if RECEIVE_TEST
pid=0;
#else
pid=fork();
#endif
- if (pid>0) return; /* parent context */
+ if (pid>0) {
+ if (verbose>=2)
+ error(_(".Spawned child receive-SMS process w/PID %d"),pid);
+ return; /* parent context */
+ }
if (pid==-1) {
error(_("Can't fork(2), process spawning may block receive"));
}
switch (mode) {
case MODE_SEND: /* FALLTHRU */
- case MODE_SEND_MOBILDOCK: cmdline_send (); break;
- case MODE_LOGO_SEND: cmdline_logo_send(); break;
- case MODE_RING_SEND: cmdline_ring_send(); break;
- case MODE_RECEIVE: cmdline_receive (); break;
+ case MODE_SEND_MOBILDOCK: cmdline_send (); break;
+ case MODE_LOGO_SEND: cmdline_logo_send (); break;
+ case MODE_RING_SEND: cmdline_ring_send (); break;
+ case MODE_PICTURE_SEND: cmdline_picture_send(); break;
+ case MODE_RECEIVE: cmdline_receive (); break;
default: assert(0);
}
cmdline_done();
case MODE_RING_SEND:
ringread();
break;
+ case MODE_PICTURE_SEND:
+ pictureread();
+ break;
case MODE_RECEIVE: break;
default: assert(0);
}
if ((end-=start)>LOCKREPORT)
logmsg(_("Device lock succeeded after %ld seconds"),(long)end);
}
+
+retryopen:
+
if (verbose>=1) error(_(".Opening device \"%s\".."),device);
if ((devfd=open(device,O_RDWR|O_NDELAY))<0)
error(_("^!Cannot open device \"%s\" for r/w access"),device);
- if (tcgetattr(devfd,&restios))
- error(_("^Unable to get termios settings"));
- else {
- restios.c_cflag=(restios.c_cflag&~(CBAUD|CBAUDEX))|B0|HUPCL;
- restios_yes=1;
- }
- tios.c_iflag=IGNBRK|IGNPAR|(handshake_rtscts ? 0 : IXON|IXOFF);
- tios.c_oflag=0;
- tios.c_cflag=CS8|CREAD|CLOCAL|HUPCL|portbaud|(handshake_rtscts ? CRTSCTS : 0);
- tios.c_lflag=IEXTEN|NOFLSH;
- memset(tios.c_cc,_POSIX_VDISABLE,sizeof(tios.c_cc));
- tios.c_cc[VTIME]=0;
- tios.c_cc[VMIN ]=1;
- cfsetispeed(&tios,portbaud);
- if (cfsetospeed(&tios,portbaud)|cfsetispeed(&tios,portbaud))
- error(_("^Error setting termios baudrate on device"));
- if (tcflush(devfd,TCIOFLUSH))
- error(_("^Error flushing termios (TCIOFLUSH) on device"));
- if (tcsetattr(devfd,TCSANOW,&tios))
- error(_("^!Unable to set initial termios device settings"));
+retryall:
- setalarm();
+ if (tcgetattr(devfd,&restios))
+ error(_("^Unable to get termios settings"));
+ else {
+ restios.c_cflag=(restios.c_cflag&~(CBAUD|CBAUDEX))|B0|HUPCL;
+ restios_yes=1;
+ }
+ tios.c_iflag=IGNBRK|IGNPAR|(handshake_rtscts ? 0 : IXON|IXOFF);
+ tios.c_oflag=0;
+ tios.c_cflag=CS8|CREAD|CLOCAL|HUPCL|portbaud|(handshake_rtscts ? CRTSCTS : 0);
+ tios.c_lflag=IEXTEN|NOFLSH;
+ memset(tios.c_cc,_POSIX_VDISABLE,sizeof(tios.c_cc));
+ tios.c_cc[VTIME]=0;
+ tios.c_cc[VMIN ]=1;
+ cfsetispeed(&tios,portbaud);
+ if (cfsetospeed(&tios,portbaud)|cfsetispeed(&tios,portbaud))
+ error(_("^Error setting termios baudrate on device"));
+ if (tcflush(devfd,TCIOFLUSH))
+ error(_("^Error flushing termios (TCIOFLUSH) on device"));
+ if (tcsetattr(devfd,TCSANOW,&tios))
+ error(_("^!Unable to set initial termios device settings"));
+
+ setalarm();
-retryall:
devcmd("",NULL,"\r\nAT\033\032"); /* ESCAPE, CTRL-Z */
devcmd(NULL,NULL,"\r\nAT");
smscset();
}
break;
case MODE_LOGO_SEND:
- case MODE_RING_SEND: {
+ case MODE_RING_SEND:
+ case MODE_PICTURE_SEND: {
struct hexdata *hd;
restore="\r\nAT+CSMP=17,,0,0";
restore="\r\nAT+CNMI=,0";
devcmd(NULL,NULL,"\r\nAT+CNMI=,2");
devcmd(NULL,NULL,"\r\nAT+CSDH=0");
- unlockdevice(0);
- /* Never bail-out when we got up to this point */
- if (maxretryn!=-1 && verbose>=1)
- error(_(".Initialization successful, infinite retry count set"));
- maxretryn=-1;
+ for (;;) {
+continue_receive:
+ unlockdevice(0);
+ /* Never bail-out when we got up to this point */
+ if (maxretryn!=-1 && verbose>=1)
+ error(_(".Initialization successful, infinite retry count set"));
+ maxretryn=-1;
#if RECEIVE_TEST
receive_headerparse(" \"+420602123456\",,\"99/10/25,03:21:03-00\"");
receive_accept("TESTBODY");
exit(EXIT_SUCCESS);
#endif
- datawait(0);
- if (!lockdevice(1)) {
- if (verbose>=1)
- error(_(".Dialout detected, waiting for lock.."));
- lockdevice(0);
- goto retryall;
- }
- d1("Lock-device succeeded\n");
- do {
- d1("Reading a message for us...\n");
- if (!(s=devcmd("\n","+CMT:"," ")))
- goto retryall;
- if (cmgf && !(i=receive_headerparse(s)))
- error(_("Receive-header parsing failed on: %s"),s);
- if (!(s=devcmd("\n","@"," ")))
- goto retryall;
- if (cmgf) {
- if (i) receive_text(s);
+ datawait(0);
+ if (!lockdevice(1)) {
+ if (verbose>=1)
+ error(_(".Dialout detected, waiting for lock.."));
+ if (verbose>=1) error(_(".Closing device \"%s\".."),device);
+ if (close(devfd))
+ error(_("Error closing device \"%s\""),device);
+ lockdevice(0);
+ goto retryopen;
}
- else receive_pdu(s);
- } while (datawait(1));
- goto retryall;
+ d1("Lock-device succeeded\n");
+ do {
+ d1("Reading a message for us...\n");
+ if (!(s=devcmd("\n","+CMT:"," ")))
+ goto retryall;
+ if (s==&devcmd_empty_return) /* only newlines found */
+ goto continue_receive;
+ if (cmgf && !(i=receive_headerparse(s)))
+ error(_("Receive-header parsing failed on: %s"),s);
+ if (!(s=devcmd("\n","@"," ")))
+ goto retryall;
+ if (cmgf) {
+ if (i) receive_text(s);
+ }
+ else receive_pdu(s);
+ } while (datawait(1));
+ } /* return to 'continue receive' point */
break;
default: assert(0);
}
}
devcmd(NULL,NULL,"\r\nAT");
- logmsg(_("SMS sent (after %d retries), %d part(s)"),retrycnt,parts);
+ if (parts)
+ logmsg(_("SMS sent (after %d retries), %d part(s)"),retrycnt,parts);
+ else
+ logmsg(_("SMS sent (after %d retries)"),retrycnt);
return(EXIT_SUCCESS);
}