From b56358ce77537daa7d462cb45030b71117452f5c Mon Sep 17 00:00:00 2001 From: short <> Date: Wed, 26 May 1999 13:06:26 +0000 Subject: [PATCH] First alpha release. --- AUTHORS | 15 ++ Makefile.am | 31 +++ NEWS | 10 + README | 28 ++ acconfig.h | 71 +++++ autogen.sh | 59 +++++ configure.in | 164 ++++++++++++ manfmt.c | 55 ++++ mdsms.1.c | 12 + mdsms.1.in | 223 ++++++++++++++++ mdsms.c | 832 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.h | 36 +++ strdup.c | 27 ++ 13 files changed, 1563 insertions(+) create mode 100644 AUTHORS create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 acconfig.h create mode 100755 autogen.sh create mode 100644 configure.in create mode 100644 manfmt.c create mode 100644 mdsms.1.c create mode 100644 mdsms.1.in create mode 100644 mdsms.c create mode 100644 setup.h create mode 100644 strdup.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..cffe83a --- /dev/null +++ b/AUTHORS @@ -0,0 +1,15 @@ +Author of MobilDock SMS Sender: +------------------------------- + +Name: Jan Kratochvil + +e-Mail: short@ucw.cz + +Phone: +420603431329 or + +420602431329 + +Homepage: http://atrey.karlin.mff.cuni.cz/~short/ + +Snail-mail: K ovcinu 44 + 182 00, Praha 8 + Czech Republic diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..3fa544b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,31 @@ +# $Id$ +# +# $Log$ +# Revision 1.1.1.1 1999/05/26 13:06:26 short +# First alpha release. +# + +EXTRA_DIST = autogen.sh mdsms.1.in ChangeLog + +CLEANFILES = ChangeLog + +bin_PROGRAMS = mdsms +man_MANS = mdsms.1 +noinst_PROGRAMS = manfmt mdsms.1 +noinst_HEADERS = setup.h getopt.h + +manfmt_SOURCES = manfmt.c + +mdsms_SOURCES = mdsms.c +mdsms_LDADD = @LIBOBJS@ + +mdsms.1.o: mdsms.1.c mdsms.1.in config.h setup.h + $(CPP) > mdsms.1.o mdsms.1.c + +mdsms.1: manfmt mdsms.1.o + ./manfmt > mdsms.1 < mdsms.1.o + +.PHONY: ChangeLog + +ChangeLog: + rcs2log >ChangeLog diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..7817928 --- /dev/null +++ b/NEWS @@ -0,0 +1,10 @@ +MobilDock SMS sender NEWS +========================= + +$Id$ + +1.0: Initial release. + Currently unimplemented: + Logging + Several timeouts during communication + Giveup on too long lock-wait diff --git a/README b/README new file mode 100644 index 0000000..d9665d9 --- /dev/null +++ b/README @@ -0,0 +1,28 @@ +MobilDock SMS sender +==================== + +$Id$ + + Development platform / device test passed on: + + Linux [i386] (kernel 2.2.7, RedHat based) + + Supported platforms regarding successful compilation. Real functionality +expected although testing not possible: + + Digital UNIX 4.0E [alpha] + Linux [sparc] (kernel 2.1.77, RedHat based) + Sun SunOS 5.5.1 [sparc/sun4m] + SGI IRIX 6.5 [mips] + + For quick installation type: + + ./configure + make install + + For full installation options see file INSTALL. + + Documentation available in 'man' format, type "man mdsms" after +installation. + + Contact to author available in file AUTHORS. diff --git a/acconfig.h b/acconfig.h new file mode 100644 index 0000000..39bf846 --- /dev/null +++ b/acconfig.h @@ -0,0 +1,71 @@ +/* acconfig.h Generated manually for use by autoheader. */ +/* + * $Id$ + * + * $Log$ + * Revision 1.1.1.1 1999/05/26 13:06:26 short + * First alpha release. + * + */ + +/* __NORETURN define - usually a GCC extension. */ +#undef __NORETURN + +/* __NORETURN2 define - usually a GCC extension. */ +#undef __NORETURN2 + +/* Turn on debugging to stderr. */ +#undef DEBUG + +/* Disable runtime assertion checks. */ +#undef NDEBUG + +/* Provide stub for MAX-function if needed. */ +#undef HAVE_MAX +#ifndef HAVE_MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + +/* Provide stub for MIN-function if needed. */ +#undef HAVE_MIN +#ifndef HAVE_MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +/* LINE_MAX from , provide default if not defined. */ +#undef HAVE_LINE_MAX +#undef LINE_MAX + +/* CBAUD define, may be missing. */ +#undef HAVE_CBAUD +#undef CBAUD + +/* CBAUDEX define, may be missing. */ +#undef HAVE_CBAUDEX +#undef CBAUDEX + +/* snprintf(3) unsafe emulation */ +#undef HAVE_SNPRINTF +#ifdef HAVE_SNPRINTF +#define VARPRINTF(v,f,d) snprintf((v),sizeof((v)),f,(d)) +#else +#define VARPRINTF(v,f,d) sprintf((v),f,(d)) +#endif + +/* vsnprintf(3) unsafe emulation */ +#undef HAVE_VSNPRINTF +#ifdef HAVE_VSNPRINTF +#define VARVPRINTF(v,f,d) vsnprintf((v),sizeof((v)),f,(d)) +#else +#define VARVPRINTF(v,f,d) vsprintf((v),f,(d)) +#endif + +/* printf family accepts %m */ +#undef PRINTF_WORKS_PM + +/* found lockfile directory */ +#undef DEF_LOCKFILE + +/* how to declare __atribute__ ((__unused__)) */ +#undef ATTR_UNUSED + diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..fc033fd --- /dev/null +++ b/autogen.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +# $Id$ +# +# $Log$ +# Revision 1.1.1.1 1999/05/26 13:06:26 short +# First alpha release. +# + +CONFDEFS="--enable-maintainer-mode --enable-debug" + +if [ -f Makefile ];then touch ChangeLog;make maintainer-clean;fi +VER="`sed ], [LINE_MAX;], + AC_DEFINE(HAVE_LINE_MAX) AC_MSG_RESULT(yes), + AC_DEFINE(LINE_MAX, 4096) AC_MSG_RESULT(no)) + +AC_MSG_CHECKING([for CBAUD]) +AC_TRY_COMPILE([#include ], [CBAUD;], + AC_DEFINE(HAVE_CBAUD) AC_MSG_RESULT(yes), + AC_DEFINE(CBAUD, 0) AC_MSG_RESULT(no)) + +AC_MSG_CHECKING([for CBAUDEX]) +AC_TRY_COMPILE([#include ], [CBAUDEX;], + AC_DEFINE(HAVE_CBAUDEX) AC_MSG_RESULT(yes), + AC_DEFINE(CBAUDEX, 0) AC_MSG_RESULT(no)) + +AC_MSG_CHECKING([for unused attribute]) +uns=true +for un in __unused__ unused;do + if $uns;then + unx="__attribute__ (($un))" + AC_TRY_COMPILE([#include ],[char untest $unx;], + AC_DEFINE_UNQUOTED(ATTR_UNUSED, $unx) + AC_MSG_RESULT($un) + uns=false) + fi + done +if $uns;then AC_DEFINE(ATTR_UNUSED,) AC_MSG_RESULT(no);fi + +AC_CHECK_FUNC(snprintf, AC_DEFINE(HAVE_SNPRINTF)) +AC_CHECK_FUNC(vsnprintf, AC_DEFINE(HAVE_VSNPRINTF)) + +AC_TYPE_SIGNAL +AC_TYPE_PID_T + +AC_REPLACE_FUNCS(strdup) +dnl AC_MSG_CHECKING([for strdup]) +dnl AC_TRY_COMPILE([#include ], [strdup("");], +dnl AC_DEFINE(HAVE_STRDUP) AC_MSG_RESULT(yes), +dnl LIBOBJS="$LIBOBJS strdup.o" AC_MSG_RESULT(no)) + +AC_MSG_CHECKING([for working %m in printf]) +AC_TRY_RUN([int main() { char s[100]; + sprintf(s,"%m"); + return(!(strcmp(s,"m") && strcmp(s,"%m"))); + }],AC_DEFINE(PRINTF_WORKS_PM) AC_MSG_RESULT(yes),AC_MSG_RESULT(no),AC_MSG_RESULT(avoiding)) + +# Final output. + +AC_SUBST(LIBOBJS) + +AC_OUTPUT(Makefile) diff --git a/manfmt.c b/manfmt.c new file mode 100644 index 0000000..344a56c --- /dev/null +++ b/manfmt.c @@ -0,0 +1,55 @@ +#include "config.h" +#ifndef lint +static char rcsid[] ATTR_UNUSED = "$Id$"; +#endif + +/* + * $Log$ + * Revision 1.1.1.1 1999/05/26 13:06:26 short + * First alpha release. + * + */ + +#include +#include +#include +#include + +static char buf[LINE_MAX]; + +int main(void) +{ +char *s,*s2,*d; +char f; + + while (fgets(buf,sizeof(buf),stdin)) { + if (*buf=='#') continue; + for (s=buf;*s==' ';s++); + if (*s=='\n' || !*s) continue; + f=0; + for (d=s=buf;*s;s++) { + if (*s=='\t') { + if ((f=!f)) { +/*fprintf(stderr,"DURING TURN-ON:%s",s);*/ + if (s[1]=='"') s++; + } + else { +/*fprintf(stderr,"DURING TURN-OFF:%s",s);*/ + while (d>buf && (d[-1]=='"' || d[-1]==' ')) d--; + } + continue; + } + s2=s; + if (*s=='~' ) *s='"'; + else if (f && s[0]=='"' && s[1]==' ') { + while (*++s==' '); + if (*s=='"') continue; + s=s2; + } + *d++=*s; + } + *d='\0'; + fputs(buf,stdout); + } + return(EXIT_SUCCESS); +} diff --git a/mdsms.1.c b/mdsms.1.c new file mode 100644 index 0000000..64240a4 --- /dev/null +++ b/mdsms.1.c @@ -0,0 +1,12 @@ +/* + * $Id$ + * + * $Log$ + * Revision 1.1.1.1 1999/05/26 13:06:26 short + * First alpha release. + * + */ + +#include "config.h" +#include "setup.h" +#include "mdsms.1.in" diff --git a/mdsms.1.in b/mdsms.1.in new file mode 100644 index 0000000..44d3088 --- /dev/null +++ b/mdsms.1.in @@ -0,0 +1,223 @@ +.TH MDSMS 1 "17 May 1999" ~ VERSION ~ "SendSMS Manual" + +.\~ $Id$ +.\~ +.\~ $Log$ +.\~ Revision 1.1.1.1 1999/05/26 13:06:26 short +.\~ First alpha release. +.\~ + +.SH NAME +mdsms \- send SMSes through MobilDock + +.SH SYNOPSIS +.B mdsms +.RB [ "-c " ] +.RB [ "-d " ] +.RB [ "-l " ] +.RB [ "-s " ] +.RB [ "-t " ] +.RB [ "-T " ] +.RB [ "\-vhV" ] +.RB [ "" ] +.RB [ "" ] + +.SH DESCRIPTION +Program sends one SMS message through MobilDock device. + +.SH OPTIONS +.TP +.BR -c | --config\ < cfgfile > +Process recursively this file and read all options from it. See the section +.B CONFIGURATION +for more information. +.TP +.BR -d | --device\ < device > +Specify serial device to communicate with MobilDock. If only bare name is +specified, "\fB/dev/\fP" is prepended automatically. This device name is used +subsequently with +.BR -l | --lockfile +option, see below. +.TP +.BR -l | --lockfile\ < lockfile > +Prior to accessing serial device specified above the lockfile should be +acquired for correct concurrent processes behaviour. Although this name +can be given as direct filename more common method is to use pattern +with embedded "\fB%s\fP" where this mark is replaced by actual basename +(last component of pathname with all preceding directory names stripped) +of serial device used. +Default name for lockfile is ~\fB DEF_LOCKFILE \fP~. +.TP +.BR -s | --smsc\ < smsc\ # > +Specify custom SMS center number. If not specified (or overriden as empty +string by \fB-s ""\fP) +.B mdsms +asks by "\fBAT+CSCA?\fP" +Siemens A1 for its default SMS center. Situation with undeterminable SMS center +is unrecoverable and casues immediate fail. It is a common practice to use +plus sign ("\fB+\fP") to indicate international number type. +Known SMS centers as of May 1999: +.RS +.TP +.BR "CZ Paegas " ( "230 01" "): " +420603052000 +.TP +.BR "CZ EuroTel " ( "230 02" "): " +420602909909 +.RE +.TP +.BR -t | --chartime\ < msec > +Although the fixed used baudrate of +.B 19200 +is pretty low, MobilDock/Siemens A1 couple +.RB "aren't" +able to accept steady +stream of data at this speed. Even the used XON/XOFF handshaking is just not +enough. The only possible workaround is to slowdown the communication by +waiting a bit after character sent to give relax time to these devices. +When full rate communication was used, occasional longer SMS data corruption +was observed. Argument is given in milliseconds and its default value is +.BR DEF_CHARTIME ms. +.TP +.BR -T | --cmdtime\ < msec > +This delay is given before sending any +.B AT-style +command to MobilDock/Siemens A1. +Its primary purpose is to let any previous entered commands to finish and +to clear any input before actually sending our own command. Also all Siemens +devices are known that they strongly dislike fast edge-to-edge communication +and to satisfy these requirements this delay was considered as the best +approach. The default value is +.BR DEF_CMDTIME ms. +.TP +.BR -v | --verbose +Increase verbosity level by one. Currently the maximum defined level is +.BR 3 , +the default value is +.BR 0 . +.TP +.BR -h | --help +Give short parameters description to +.I stderr +(standard error output stream). +.TP +.BR -v | --version +Print the version number and exit. +.TP +.RB < "dest. phone" > +This mandatory parameter specifies the telephone number of the recipient +of SMS message. International prefix character plus +.RB ( + ) +is supported the national mode without plus +.RB ( + ) +prefix is supported and the meaning is specific to the GSM operator currently +being roamed in (NOT the native +.RB "'home'" +operator of the SIM card!). +This number can be made default in system configuration files, see below +for section +.BR CONFIGURATION . +.TP +.RB < "msg text" > +Here you write the exact body of the message. This parameter should be +specified only as one component, although if more found they are concatenated +with separating space (" "). But this practice is discouraged as +your shell will probably remove any multiple spaces found and also other +metacharacters may be incorrectly interpreted. To prevent any escaping +mess, you may prefer to omit this parameter and the the message text is +then read from +.I stdin +(standard input stream). + +.SH CONFIGURATION +.B mdsms +reads ~\fB CONFIG_MAIN \fP~ followed by ~\fB$HOME CONFIG_HOME \fP~ +configuration files. The content of these files (and also any file read by +.BR -c | --config +option) +has the usual +.BR getopt (3) +syntax with dashes. Newlines are taken as whitespace, both double ("\fB~\fP") +and single ("\fB'\fP") +quoting is supported. Embedded quote characters can be escaped by backslash +("\fB\\\fP"). +Arguments are processes in order as specified in configuration files and +finally the command-line of the program itself, overriding any previous +values. Although the order of +.BR -c | --config +options on one line is preserved the first file is read AFTER all of the +current options are processed. Recursive use of +.B -c +is permitted and the files are read in LIFO (Last-In First-Out, AKA stack) +order. You should use "\fB-v -v\fP" +to see details of option processing with more complex configuration setups. + +.SH OPERATION +Upon startup +.B mdsms +locks the port (see option +.BR -l | --lockfile +for details) and opens the serial device with hardcoded parameters of +19200 baud, software handshaking (XON/XOFF style), 8 bits, no parity. +Then issues the following commands: +.TP +.BR AT +.B AT +is used to fool MobilDock and pass the following +.B +(ascii decimal code 26) character to Siemens A1 and break it from eventual +.B AT+CMGS +mode in which it may errorneously remain from previous sessions. +.TP +.B AT +Test the responsiveness of Siemens A1. +.TP +\fBAT+CSCA="\fPsmc # from user\fB"\fP +This command is omitted if +.B "smsc #" +is not specified by user (or specified/overriden as empty string \fB""\fP +.TP +\fBAT+CSCA?\fP +Query the currently set SMC center number to include it later to the +header of SMS PDU format where it is required. If \fBAT+CSCA="\fP...\fB"\fP +was issued before, this number should match it but no sanity checks are +currently do so. +.TP +\fBAT+CMGF=0\fP +Set the default SMS message format to PDU type. Currently Siemens A1 +.RB "doesn't" +support any other SMS format anyway. +.TP +\fBAT+CMGS=\fP# chars +This command physically sends the message and the resulting "\fB+CMGS:\fP" output +is catched and returned as +.B MR +(message reference) number to the user. +.TP +\fBAT\fP +Check that Siemens A1 survived the sending of the message. + +.SH SEE ALSO +.TP +.B GSM 03.40 +ETSI documentation for SMS messages in GSM networks +.TP +.B "Developers'" Guide: SMS with the A1 +Tech note on PDU SMS format etc: +.B http:/ /www.siemens.se/telefoner/ovrigtgsm/fragorsvar/a1_sms.pdf +.TP +.B Technical Description of the Siemens A1 +Siemens A1 command description +.B http:/ /www.siemens.se/telefoner/ovrigtgsm/fragorsvar/a1_manual.pdf + +.SH FILES +.TP +\fB CONFIG_MAIN \fP +Main configuration file +.TP +\fB$HOME CONFIG_HOME \fP +User personalized local configuration file + +.SH AUTHOR +.B mdsms +was written by Jan Kratochvil who should be responsible for all the bugs +included. Please see the file "\fBAUTHORS\fP" +shipped with the original distribution archive for more details. diff --git a/mdsms.c b/mdsms.c new file mode 100644 index 0000000..f0837c6 --- /dev/null +++ b/mdsms.c @@ -0,0 +1,832 @@ +#include "config.h" +#ifndef lint +static char rcsid[] ATTR_UNUSED = "$Id$"; +#endif + +/* + * $Log$ + * Revision 1.1.1.1 1999/05/26 13:06:26 short + * First alpha release. + * + */ + +#include "setup.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_GETOPT_LONG +#include +#else +#include "getopt.h" +#endif + +#if 0 +char *strdup(const char *s); +int kill(pid_t pid,int sig); +int snprintf(char *str,size_t n,const char *format,...); +int vsnprintf(char *str,size_t n,const char *format,va_list ap); +void usleep(unsigned long usec); +#endif + +#define NELEM(x) (sizeof((x))/sizeof(*(x))) + +#ifndef DEBUG +#define dbg(cmd) +#else +#define dbg(cmd) cmd +#endif +/* ANSI C does not allow macro with variable arguments */ +#define dO stderr +#define dB(a) dbg(fprintf a) + +#define d1(n1) dB((dO,n1 )) +#define d2(n1,n2) dB((dO,n1,n2 )) +#define d3(n1,n2,n3) dB((dO,n1,n2,n3 )) +#define d4(n1,n2,n3,n4) dB((dO,n1,n2,n3,n4)) + +static const char version[]="This is MobilDock SMS sender (" PACKAGE " " VERSION ")\n"; + +static int verbose +#ifdef DEBUG + =0xFFFF +#endif + ; +static char *pname; +static int dis_cleanup=0,devfd=-1; + +static char *phone,*body,*device,*lockfile,*smsc,*chartime,*cmdtime; +static int readbody; +static long chartimen=DEF_CHARTIME,cmdtimen=DEF_CMDTIME; +static size_t bodylen; + +static char *devicename; /* path stripped */ +static char lockreal[512],locked; + +static struct termios restios; +static char restios_yes; + +static void error(const char *fmt,...) +{ +va_list ap; +char fatal=*fmt; +#ifndef PRINTF_WORKS_PM +char pm,*nfmt; +size_t fmtl; +#endif + + if (fatal=='!' || fatal=='.' || fatal=='\n') fmt++; + else fatal=0; + +#ifndef PRINTF_WORKS_PM + if (!(nfmt=strdup(fmt))) return; + fmtl=strlen(fmt); + if ((pm=(fmtl>=2 && !strcmp(fmt+fmtl-2,"%m")))) nfmt[fmtl-2]='\0'; +#endif + + fprintf(stderr,"%s: ",pname); + va_start(ap,fmt); + vfprintf(stderr, +#ifdef PRINTF_WORKS_PM + fmt +#else + nfmt +#endif + ,ap); + va_end(ap); + +#ifndef PRINTF_WORKS_PM + if (pm) { + fputs(strerror(errno),stderr); + free(nfmt); + } +#endif + + if (fatal!='\n') fputc((fatal=='.'?'.':'!'),stderr); + fputc('\n',stderr); + if (fatal=='!') exit(EXIT_FAILURE); +} + +static void chk(void *p) +{ + if (p) return; + error("!Virtual memory exhausted"); +} + +static void cleanup(void) +{ + d1("cleanup()\n"); + if (dis_cleanup) return; + if (restios_yes) { + if (tcsetattr(devfd,TCSANOW,&restios)) + error("Error restoring termios for device: %m"); + restios_yes=0; + } + if (locked && *lockreal) { + d2("Removing lockfile \"%s\"\n",lockreal); + if (unlink(lockreal)) + error("Error removing my device lockfile \"%s\": %m",lockreal); + locked=0; + } + dis_cleanup=1; + exit(EXIT_FAILURE); +} + +static void usage(void) +{ + fprintf(stderr,"\ +\n\ +%s\ +\n\ +Usage: " PACKAGE " [-c|--config ] [-d|--device ]\n\ + [-l|--lockfile ] [-s|--smsc ]\n\ + [-t|--chartime ] [-T|--cmdtime ]\n\ + [-f|--file] [-v|--verbose] [-h|--help] [-V|--version]\n\ + \n\ +\n\ + -c, --config\tRead this additional config file\n\ +\t\t(def. \"" CONFIG_MAIN "\" and \"$HOME" CONFIG_HOME "\")\n\ + -d, --device\tMobilDock on this serial device (def. \"" DEF_DEVICE "\")\n\ + -l, --lockfile\tLock serial port by this file, \"%%s\" is basename of device\n\ +\t\t(def. \"%s\")\n\ + -s, --smsc\tUse this SMS Center number (def. query from Siemens A1)\n\ + -t, --chartime\tMilliseconds between each char on baud 19200 (def. %dms)\n\ + -T, --cmdtime\tMilliseconds before each whole AT command (def. %dms)\n\ + -f, --file\tRead contents of message from file instead\n\ + -v, --verbose\tIncrease verbosity level, more \"-v\"s give more messages\n\ + -h, --help\tPrint a summary of the options\n\ + -V, --version\tPrint the version number\n\ +\n",version,DEF_LOCKFILE,DEF_CHARTIME,DEF_CMDTIME); + exit(EXIT_FAILURE); +} + +static const struct option longopts[]={ +{"config" ,1,0,'c'}, +{"device" ,1,0,'d'}, +{"lockfile",1,0,'l'}, +{"smsc" ,1,0,'s'}, +{"chartime",1,0,'t'}, +{"cmdtime" ,1,0,'T'}, +{"file" ,0,0,'f'}, +{"verbose" ,0,0,'v'}, +{"help" ,0,0,'h'}, +{"version" ,0,0,'V'}}; + +static void processargs(int argp,char **args,const char *from); + +static char *cfgstack[MAXCFGLOOP]; +static unsigned cfgstacki=0; + +static void chkfclose(FILE *f,const char *fname) +{ + if (fclose(f)) + error("Error closing \"%s\": %m",fname); +} + +static void readfile(const char *fname,char quiet) +{ +FILE *f; +size_t got; +char *args[MAXCFGARGS],*d,*s,blank,quote; +unsigned argp; +char *buf; +long size; +static unsigned tot=0; + + if (tot++>=MAXCFGNUM) { + if (tot==MAXCFGNUM+1) error("Too many config files to read, max is %d, break-out",MAXCFGNUM); + return; + } + if (!(f=fopen(fname,"rt"))) { + if (!quiet) error("Can't open config file \"%s\" for r/o: %m",fname); + return; + } + if (verbose>=2) error(".Reading config file \"%s\"",fname); + if (fseek(f,0,SEEK_END)) + error("Error seeking to end of \"s\": %m",fname); + if ((size=ftell(f))<0) + size=0,error("Error measuring \"%s\": %m",fname); + if (size>MAXCONFIG) + error("File \"%s\" is too long, read only %ld bytes",fname,MAXCONFIG); + chk(buf=malloc((size?size:MAXCONFIG)+1)); + rewind(f); + got=fread(buf,1,(size?size:MAXCONFIG),f); + if (size && got!=size) + error("File \"%s\" read error, got only %u bytes of %ld",fname,got,size); + chkfclose(f,fname); + buf[got]='\0'; + args[0]=pname; + for (argp=1,d=s=buf,blank=1,quote=0;s=2) error("\nConfig \"%s\": arg#%d: %s",fname,argp-1,args[argp-1]); + } + continue; + } + if (blank) { + if (argp>=NELEM(args)-1) { + error("Too many arguments in \"%s\", from offset %d ignored",fname,s-buf); + break; + } + args[argp++]=s; + d=s; + blank=0; + } + if (c=='\\') { *d++=*++s; continue; } + if (c=='"' || c=='\'') { + if (!quote ) { quote=c; continue; } + if ( quote==c) { quote=0; continue; } + /* FALLTHRU */ + } + *d++=c; + } + args[argp]=NULL; + processargs(argp,args,fname); + free(buf); +} + +static struct { + const char c; + char **const var; + unsigned stamp; + } optset[]={ + { 'd',&device }, + { 'l',&lockfile }, + { 's',&smsc }, + { 't',&chartime }, + { 'T',&cmdtime }, + }; + +static void processargs(int argp,char **args,const char *from) +{ +int optc; +static unsigned seq=0; +int i; + + seq++; + optarg=NULL; optind=0; /* FIXME: Possible portability problem. */ + while ((optc=getopt_long(argp,args,"c:d:l:s:t:T:fvhV",longopts,NULL))!=EOF) switch (optc) { + case 'c': + if (cfgstacki>=NELEM(cfgstack)) { + error("Looping (%d) during attempt to read config file \"%s\", break-out",NELEM(cfgstack),optind); + break; + } + chk(cfgstack[cfgstacki++]=strdup(optarg)); + break; + case 'd': case 'l': case 's': case 't': case 'T': + for (i=0;i=0); + readfile(s,0); + free(s); + assert(cfgstacki>=0 && cfgstacki=3) error(".Checking the lockfile \"%s\"..",lockreal); + if ((fd=open(lockreal,O_RDONLY))==-1) break; + if ((got=read(fd,buf,sizeof(buf)-1))<=0) { +isempty: + if (empty>=DEVLOCK_MAXEMPTY) { + error(".Lockfile \"%s\" is still not valid, removing it",lockreal); + goto remove; + } + empty++; + continue; + } + assert(got=1) error("Timed out"); +} + +static void blocking(char yes) +{ +static char state=-1; + if (state==yes) return; + if (fcntl(devfd,F_SETFL,(yes?0:O_NONBLOCK))) + error("!fcntl() on device for %sblocking mode: %m",(yes?"":"non-")); + state=yes; +} + +static const char *record; +static char *catchdata; +static size_t catchdatal,catchdatasiz; + +static void catched(const char *end) +{ +size_t len; +void *p; + + if (!record) return; + assert(end>=record); + if ((p=memchr(record,'\n',end-record))) end=p; + if ((len=end-record)) { + if (catchdatal+len>catchdatasiz) + chk(catchdata=realloc(catchdata, + (catchdatasiz=MAX(LINE_MAX,(catchdatal+len)*2)))); + memcpy(catchdata+catchdatal,record,len); + catchdatal+=len; + } + record=(p?NULL:end); + assert(catchdatal<=catchdatasiz); +} + +static char *devcmd(const char *term,const char *catch,const char *send,...) +{ +size_t l,bufl,terml,catchl,fragl,offs; +char buf[LINE_MAX]; +ssize_t got; +RETSIGTYPE (*origsig)(int); +char *hit,*s; +va_list ap; +char errout; + + if (!term) term="\nOK\n"; + if ((errout=(*send=='!'))) send++; + if (0) { +err: + if (errout) return(NULL); + if (verbose>=2) error(".Retrying last device command.."); + } + catchdatal=0; + va_start(ap,send); + l=VARVPRINTF(buf,send,ap); bufl=l+1; + va_end(ap); + if (bufl>=sizeof(buf)-1) error("!Command too big (%d>%d)",bufl,sizeof(buf)-1); + if (verbose>=2) error(".devcmd(send=\"%s\",term=\"%s\",catch=\"%s\")",buf,term,catch); + buf[l]='\r'; buf[l+1]='\n'; + origsig=signal(SIGALRM,(RETSIGTYPE (*)(int))sigalarm); + for (offs=0,got=0;offss && (s=memchr(s,'\r',buf+bufl-s))) *s='\n'; + catched(buf+bufl); assert(!record || record==buf+bufl); + assert(bufl=fragl) { + buf[bufl]='\0'; + assert(strlen(buf)==bufl); + /* d3(">%s|%s<\n",buf,term); */ + if (strstr(buf,ERROR_SUBSTR1) || strstr(buf,ERROR_SUBSTR2)) { + error("Found ERROR response on command \"%s\"",send); + goto err; + } + if (catch && bufl>=catchl && (hit=strstr(buf,catch))) { + record=hit+catchl; + catched(buf+bufl); assert(!record || record==buf+bufl); + } + if ( bufl>= terml && (hit=strstr(buf,term))) break; + memmove(buf,buf+bufl-(fragl-1),fragl-1); + bufl=fragl-1; + if (record) record=buf+bufl; + } + } + if (!catchdatal) { + if (!catch) return(NULL); + error("Data requested on command \"%s\" but no found after term \"%s\"",send,term); + goto err; + } + assert(!!catch); + record=buf; + buf[0]='\0'; + catched(buf+1); + if (verbose>=2) error(".Returning data \"%s\" for cmd \"%s\"",catchdata,send); + return(catchdata); +} + +static int prepaddr(unsigned char *d,const char *addr) +{ +int tot=0; +char flip=0,plus; +unsigned char n; + + if ((plus=(*addr=='+'))) addr++; + *++d=(plus?ADDR_INT:ADDR_NAT); + while (*addr) { + if (*addr<'0' || *addr>'9') + error("!Error during conversion of number at: %s",addr); + tot++; + n=(*addr++)-'0'; + if ((flip=!flip)) *++d=0xF0|n; + else *d=(*d&0x0F)|(n<<4U); + } + return(tot); +} + +static char *finalsmsc; +#define SMSCBINSIZE (1+1+(MAXNUMLEN+1)/2) +static char pdusmsc[SMSCBINSIZE*2+1]; + +static inline char tohex(unsigned char x) +{ + x&=0x0F; + if (x<10) return(x +'0'); + return(x-10+'A'); +} + +static void textconv(char *d,unsigned char *s,size_t len) +{ + while (len--) { + *d++=tohex(*s>>4U); + *d++=tohex(*s ); + s++; + } + *d='\0'; +} + +static inline void smscset(void) +{ +char *s,*t,*e,*serr; +long l; +unsigned char bin[2+(MAXNUMLEN+1)/2]; + + if (smsc) devcmd(NULL,NULL,"\r\nAT+CSCA=\"%s\"",smsc); + s=devcmd(NULL,"\n+CSCA:","\r\nAT+CSCA?"); + while (isspace(*s)) s++; + if (!*s || !strcmp(s,"EMPTY")) + error("!No SMS set in A1 found"); + if (verbose>=1) error("\nFound default SMSC in A1: %s",s); + if (*s!='"') error("!No left-quote found in: %s",s); + if (!(t=strrchr(s+1,'"'))) error("!No right-quote found in: %s",s); + e=t++; + while (isspace(*t)) t++; + if (*t++!=',') error("!No comma found after quotes in: %s",s); + while (isspace(*t)) t++; + l=strtol(t,&serr,10); + if ((l!=ADDR_NAT && l!=ADDR_INT) || (serr && *serr)) + error("!Type parse error in: %s",s); + if (l==ADDR_NAT || s[1]=='+') s++; + else *s='+'; + *e='\0'; + chk(finalsmsc=strdup(s)); + if (verbose>=2) error("\nDecoded SMSC address: %s",finalsmsc); + bin[0]=1+(prepaddr(bin,finalsmsc)+1)/2; + textconv(pdusmsc,bin,bin[0]+1); +} + +static char *pdudata; + +static inline unsigned char charconv(char c,size_t offs) +{ + if ((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9')) return(c); + switch (c) { + case '@': return(0); + case '$': return(2); + case '"': return(96); + case ' ': + return(c); + case 0: assert(0); + } + error("Can't convert character '%c' (0x%02X) at offs %d (0-based), substituted '?'", + c,(unsigned char)c,offs); + return('?'); +} + +static inline void genpdu(void) +{ +static unsigned char pdu[64+MAXNUMLEN/2+(MAXBODYLEN*7)/8]; +unsigned char *d=pdu; +int i; +char inb=0,outb=0,xb; +unsigned char inreg; +size_t offs=0; + + *d++=PDU_TYPE; + *d++=PDU_MR; + i=prepaddr(d,phone); + *d=i; d+=1+1+(i+1)/2; + *d++=PDU_PID; + *d++=PDU_DCS; + *d++=PDU_VP; + if (bodylen>MAXBODYLEN) { + error("Body too large (%d>%d), cut",bodylen,MAXBODYLEN); + bodylen=MAXBODYLEN; + } + *d=bodylen; + assert(d0); assert(!!*body); + inreg=charconv(*body++,offs++); + bodylen--; + inb=7; + } + if (!outb) { + *++d=0x00; + outb=8; + } + xb=MIN(inb,outb); + d4("inb=%d,outb=%d,xb=%d\n",inb,outb,xb); + *d|=((inreg>>(unsigned)(7-inb))&((1<"); + if ((s=getenv("HOME"))) { +size_t l=strlen(s); +char *buf=malloc(l+50); + + memcpy(buf,s,l); + strcpy(buf+l,CONFIG_HOME); + readfile(buf,1); + free(buf); + } + readfile(CONFIG_MAIN,1); + + for (i=0;i=1) + error("\nPlease enter the SMS text body, end with EOF (ctrl-D):"); + chk(body=malloc(BODYLOAD)); + bodylen=fread(body,1,BODYLOAD,fin); + if (bodylen==-1) + error("!Error reading stream \"%s\": %m",(finame?finame:"")); + } + if (fin!=stdin) { + chkfclose(fin,finame); + free(finame); + } + + for (i=0;i=LONG_MAX || !serr || *serr) + error("!Number parse error for parameter \"%s\" of \"%s\" at: %s", + numarg[i].msg,*numarg[i].sp,serr); + } + + if (!strchr(device,'/')) { +size_t l=strlen(device); + chk(s=malloc(5+l+1)); + strcpy(s,"/dev/"); + strcpy(s+5,device); + free(device); + device=s; + } + devicename=strrchr(device,'/')+1; assert(!!(devicename-1)); + for (i=0,s=lockfile;*s;s++) { + if (*s!='%') continue; + s++; + if (*s=='%') continue; + if (*s=='s') { + if (i) error("!Only one \"%%s\" permitted in lockfile format-string"); + i=1; continue; + } + error("!Invalid format-character '%c' in lockfile format-string, only \"%%s\" allowed",*s); + } + genpdu(); + + if (lockfile && *lockfile && VARPRINTF(lockreal,lockfile,devicename)>0) { + if (verbose>=1) error(".Locking device \"%s\" by \"%s\"..",device,lockreal); + lockdevice(); + } + if (verbose>=1) error(".Opening device \"%s\"..",device); + if ((devfd=open(device,O_RDWR|O_NDELAY))<0) + error("!Cannot open device \"%s\" for rw-access: %m",device); + + if (tcgetattr(devfd,&restios)) + error("Unable to get termios settings: %m"); + else { + restios.c_cflag=(restios.c_cflag&~(CBAUD|CBAUDEX))|B0|HUPCL; + restios_yes=1; + } + tios.c_iflag=IGNBRK|IGNPAR|IXON|IXOFF; + tios.c_oflag=0; + tios.c_cflag=CS8|CREAD|CLOCAL|B19200|HUPCL; + 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; + if (tcflush(devfd,TCIOFLUSH)) + error("Error flushing termios (TCIOFLUSH) on device: %m"); + if (tcsetattr(devfd,TCSANOW,&tios)) + error("!Unable to set initial termios device settings"); + + do { + devcmd("",NULL,"\r\nAT\x1A"); + devcmd(NULL,NULL,"\r\nAT"); + smscset(); + devcmd(NULL,NULL,"\r\nAT+CMGF=0"); + devcmd("\n> ",NULL,"\r\nAT+CMGS=%d",(strlen(pdusmsc)+strlen(pdudata))/2); + s=devcmd(NULL,"\n+CMGS:","!%s%s\x1A",pdusmsc,pdudata); + } while (!s); + while (isspace(*s)) s++; + if (verbose>=1) error("\nMessage successfuly sent with MR: %s",s); + devcmd(NULL,NULL,"\r\nAT"); + + return(EXIT_SUCCESS); +} diff --git a/setup.h b/setup.h new file mode 100644 index 0000000..c32de27 --- /dev/null +++ b/setup.h @@ -0,0 +1,36 @@ +/* + * $Id$ + * + * $Log$ + * Revision 1.1.1.1 1999/05/26 13:06:26 short + * First alpha release. + * + */ + +#define CONFIG_MAIN "/etc/" PACKAGE "rc" +#define CONFIG_HOME "/." PACKAGE "rc" /* $HOME is prepended automatically */ +#define DEF_DEVICE "/dev/mobildock" +#ifndef DEF_LOCKFILE +#define DEF_LOCKFILE "/var/lock/LCK..%s" +#endif +#define DEF_CHARTIME 10 +#define DEF_CMDTIME 500 +#define MAXCONFIG 4096 +#define MAXCFGARGS 256 +#define MAXCFGLOOP 5 +#define MAXCFGNUM 20 +#define DEVLOCK_PERIOD 2 /* sec between attempts to obtain a lockfile */ +#define DEVLOCK_MAXEMPTY 1 +#define MAXSENDTIME 2 +#define ADDR_NAT 0x81 +#define ADDR_INT 0x91 +#define PDU_TYPE 0x11 +#define PDU_MR 0x00 +#define PDU_PID 0x00 +#define PDU_DCS 0x00 +#define PDU_VP 0xFF +#define MAXNUMLEN 20 +#define MAXBODYLEN 160 +#define BODYLOAD 200 +#define ERROR_SUBSTR1 "ERROR" +#define ERROR_SUBSTR2 "\nError:" diff --git a/strdup.c b/strdup.c new file mode 100644 index 0000000..3bd92a3 --- /dev/null +++ b/strdup.c @@ -0,0 +1,27 @@ +#include "config.h" +#ifndef lint +static char rcsid[] ATTR_UNUSED = "$Id$"; +#endif + +/* + * $Log$ + * Revision 1.1.1.1 1999/05/26 13:06:26 short + * First alpha release. + * + */ + +/* This part of code is a public domain */ + +#include +#include + +/* CONFORMING TO SVID 3, BSD 4.3 */ + +char *strdup(const char *s) +{ +size_t l; +char *d; + + if (!(d=malloc(l=strlen(s)+1))) return(NULL); + return memcpy(d,s,l); +} -- 1.8.3.1