This is am.c in view mode; [Download] [Up]
/*********************************************************************/ /* */ /* Programmer: */ /* Olaf Mueller <olaf@orest.escape.de> */ /* */ /* Purpose: */ /* Answering Machine */ /* main module */ /* */ /* History: */ /* 29-08-96 Initial Release Olaf Mueller */ /* */ /* Notes: */ /* */ /*********************************************************************/ #include <libc.h> #include <sys/dir.h> #define NoTESTMODE #include "defaults.h" #include "const.h" #include "misc.h" #include "device.h" #include "iodev.h" #include "timer.h" #include "zyxel.h" /* ZyXEL MODEM opinions */ #define RZYX_SILENCE 11 #define RZYX_QUIET 12 #define RZYX_ENDOFTEXT 13 #define RZYX_FAX 14 #define RZYX_BUSY 15 #define RZYX_HANGUP 16 /* <DLE>h is not documented. <DLE>d means dialtone detected */ #define RZYX_DATACALL 17 /* data calling tone detected (>=6.11a) */ /* other return codes */ #define ROTH_COMPLETED 0 #define ROTH_STAR 1 #define ROTH_PWDENTERED 2 #define ROTH_TIMEOUT 3 #define ROTH_EXPIRATION 4 #ifndef TESTMODE #define LOGFILE "/usr/spool/uucp/LOGFILE" #define DEBUGFILE "/usr/spool/uucp/DEBUG" #define ERRLOGFILE "/usr/spool/uucp/ERRLOG" #define SYSLOGFILE "/usr/spool/uucp/SYSLOG" #else #define LOGFILE "/dev/console" #define DEBUGFILE "/dev/console" #define ERRLOGFILE "/dev/console" #define SYSLOGFILE "/dev/console" #endif /* Voice compression mode for receiving voice */ #define RECVCM ZYX_AD3 //#define RECVCM ZYX_AD2 /* Silent detection for voice */ #define SILENTSENSITIVITY 15 /* 0 - 31 */ #define SILENTPERIOD 60 /* 0 - 255 (.1 sec) */ #define TEMPVOICE "/tmp/tmpvoice.zsnd" /* System voices */ #define ZSND ".zsnd" #define VOC_WELCOME "Welcome" #define VOC_NOMSGFORFRIEND "NoMessageForFriend" #define VOC_ROOTMENU "RootMenu" #define VOC_NOMSGTOPLAY "NoMessageToPlay" #define VOC_NOMSGTOARCHIVE "NoMessageToArchive" #define VOC_LASTMSGARCHIVED "LastMessageArchived" #define VOC_BADINPUT "BadInput" #define VOC_TIMELIMIT "TimeLimit" #define FAXSIGNAL "/tmp/faxsignal.am" /***** ***** configuration of am *****/ static int cfg_rings ; static int cfg_speaker ; static int cfg_volume ; static char cfg_localfaxid [200] ; static char cfg_port ; static char cfg_voicesystemdir [MAXPATHLEN+1] ; static int cfg_voicerootnumber ; static char cfg_voicefrienddir [MAXPATHLEN+1] ; static int cfg_voiceexpiration ; static int cfg_beepexpiration ; static char cfg_voicespooldir [MAXPATHLEN+1] ; static char cfg_faxspooldir [MAXPATHLEN+1] ; static char cfg_mailaddresses [MAXPATHLEN+1] ; static int cfg_voicemailmode ; static int cfg_faxmailmode ; static char cfg_execvoicemail [MAXPATHLEN+1] ; static char cfg_execfaxmail [MAXPATHLEN+1] ; static void setDefaults (void) { cfg_port = PORT ; cfg_rings = RINGS ; cfg_speaker = SPEAKER ; cfg_volume = VOLUME ; strcpy (cfg_localfaxid,LOCALFAXID) ; strcpy (cfg_voicesystemdir,VOICESYSTEMDIR) ; cfg_voicerootnumber = VOICEROOTNUMBER ; strcpy (cfg_voicefrienddir,VOICEFRIENDDIR) ; cfg_voiceexpiration = VOICEEXPIRATION ; cfg_beepexpiration = BEEPEXPIRATION ; strcpy (cfg_voicespooldir,VOICESPOOLDIR) ; strcpy (cfg_faxspooldir,FAXSPOOLDIR) ; strcpy (cfg_mailaddresses,MAILADDRESSES) ; cfg_voicemailmode = VOICEMAILMODE ; cfg_faxmailmode = FAXMAILMODE ; strcpy (cfg_execvoicemail,EXECVOICEMAIL) ; strcpy (cfg_execfaxmail,EXECFAXMAIL) ; } static char *stbeg (char *string) { while (*string != '\0' && (*string == ' ' || *string == '\t')) string++ ; return string ; } static int configurate (char *filename) { FILE *fp ; char buffer [1000] , *cp ; int ivalue ; if ((fp = fopen(filename,"r")) != NULL) { while (fgets(buffer,sizeof(buffer),fp) != NULL) if (buffer[0] != '#' && (cp = strchr(buffer,'=')) != NULL) if (!memcmp("PORT",buffer,(int)(cp-buffer))) cfg_port = *stbeg(cp+1) ; else if (sscanf(buffer,"RINGS=%d",&ivalue) == 1) cfg_rings = ivalue ; else if (sscanf(buffer,"SPEAKER=%d",&ivalue) == 1) cfg_speaker = ivalue ; else if (sscanf(buffer,"VOLUME=%d",&ivalue) == 1) cfg_volume = ivalue ; else if (!memcmp("LOCALFAXID",buffer,(int)(cp-buffer))) { strcpy (cfg_localfaxid,stbeg(cp+1)) ; *(cfg_localfaxid+strlen(cfg_localfaxid)-1) = '\0' ; } else if (!memcmp("VOICESYSTEMDIR",buffer,(int)(cp-buffer))) { strcpy (cfg_voicesystemdir,stbeg(cp+1)) ; *(cfg_voicesystemdir+strlen(cfg_voicesystemdir)-1) = '\0' ; } else if (sscanf(buffer,"VOICEROOTNUMBER=%d",&ivalue) == 1) cfg_voicerootnumber = ivalue ; else if (!memcmp("VOICEFRIENDDIR",buffer,(int)(cp-buffer))) { strcpy (cfg_voicefrienddir,stbeg(cp+1)) ; *(cfg_voicefrienddir+strlen(cfg_voicefrienddir)-1) = '\0' ; } else if (sscanf(buffer,"VOICEEXPIRATION=%d",&ivalue) == 1) cfg_voiceexpiration = ivalue ; else if (sscanf(buffer,"BEEPEXPIRATION=%d",&ivalue) == 1) cfg_beepexpiration = ivalue ; else if (!memcmp("VOICESPOOLDIR",buffer,(int)(cp-buffer))) { strcpy (cfg_voicespooldir,stbeg(cp+1)) ; *(cfg_voicespooldir+strlen(cfg_voicespooldir)-1) = '\0' ; } else if (!memcmp("FAXSPOOLDIR",buffer,(int)(cp-buffer))) { strcpy (cfg_faxspooldir,stbeg(cp+1)) ; *(cfg_faxspooldir+strlen(cfg_faxspooldir)-1) = '\0' ; } else if (!memcmp("MAILADDRESSES",buffer,(int)(cp-buffer))) { strcpy (cfg_mailaddresses,stbeg(cp+1)) ; *(cfg_mailaddresses+strlen(cfg_mailaddresses)-1) = '\0' ; } else if (sscanf(buffer,"VOICEMAILMODE=%d",&ivalue) == 1) cfg_voicemailmode = ivalue ; else if (sscanf(buffer,"FAXMAILMODE=%d",&ivalue) == 1) cfg_faxmailmode = ivalue ; else if (!memcmp("EXECVOICEMAIL",buffer,(int)(cp-buffer))) { strcpy (cfg_execvoicemail,stbeg(cp+1)) ; *(cfg_execvoicemail+strlen(cfg_execvoicemail)-1) = '\0' ; } else if (!memcmp("EXECFAXMAIL",buffer,(int)(cp-buffer))) { strcpy (cfg_execfaxmail,stbeg(cp+1)) ; *(cfg_execfaxmail+strlen(cfg_execfaxmail)-1) = '\0' ; } fclose (fp) ; } return fp ? 0 : -1 ; } /***** ***** signal handler *****/ static void signalhandler (int sig) { switch (sig) { case SIGALRM: break ; default: log (ERRLOGFILE,"ERROR: Signal %d caught (should never happen)\n",sig) ; break ; } } /* There are 3 (break) modes reading and writing voice data mode 0: no MFV Tone breaks mode 1: MFV Tone '*' or a digit breaks mode 2: MFV Tone '*' breaks and returns the 'pwdnr' In case of init, 'pwdnr' should be set to -1 before. */ #define VBLEN 72 #define IOTIMEOUT 1000L static int writeVoice (int line,char *block,int blocksize,int *pwdnr,int breakmode) { int pos = 0 , ii , recsize , transmit = 1 , dlestate = 0 ; char rec [20] , cc ; while (pos < blocksize) { if (transmit) { setTimer (ITIMER_REAL,IOTIMEOUT) ; if ((ii = write(line,block+pos,pos+VBLEN < blocksize ? VBLEN : blocksize-pos)) > 0) { setTimer (ITIMER_REAL,0L) ; pos += ii ; } else return ROTH_TIMEOUT ; } /* read bytes immediately */ ii = fcntl (line,F_GETFL,0); fcntl (line,F_SETFL,ii|O_NDELAY) ; recsize = read (line,rec,sizeof(rec)) ; fcntl (line,F_SETFL,ii) ; /* check received bytes */ for (ii = 0 ; ii < recsize ; ii++) { cc = *(rec+ii) ; if (cc == XON) { transmit = 1 ; log (DEBUGFILE,"XON from modem - started") ; } else if (cc == XOFF) { transmit = 0 ; log (DEBUGFILE,"XOFF from modem - stopped") ; } else if (dlestate) { dlestate = 0 ; switch (cc) { case '*': if (breakmode) return (*pwdnr < 0) ? ROTH_STAR : ROTH_PWDENTERED ; break ; case '#': *pwdnr = 0 ; break ; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (*pwdnr >= 0) { *pwdnr = 10 * *pwdnr + (cc-'0') ; if (breakmode == 1) return ROTH_PWDENTERED ; } break ; case 'c': return RZYX_FAX ; case 'b': return RZYX_BUSY ; case 'h': case 'd': return RZYX_HANGUP ; /* 'd' = Dialtone (6.11a) */ case 'e': return RZYX_DATACALL ; /* 'e' = Data Calling Tone (6.11a) */ case ETX: return RZYX_ENDOFTEXT ; } } else if (cc == DLE) dlestate = 1 ; } } return ROTH_COMPLETED ; } static int readVoice (int line,long *exptime,char *block,int *blocksize,int *dlestate,int *pwdnr,int breakmode) { int ii , recsize ; char rec [*blocksize] , cc ; setTimer (ITIMER_REAL,IOTIMEOUT) ; recsize = read (line,rec,*blocksize) ; if (recsize <= 0) return ROTH_TIMEOUT ; *exptime -= IOTIMEOUT - setTimer (ITIMER_REAL,0L) ; for (ii = *blocksize = 0 ; ii < recsize ; ii++) { cc = *(rec+ii) ; if (*dlestate) { *dlestate = 0 ; switch (cc) { case '*': if (breakmode) return (*pwdnr < 0) ? ROTH_STAR : ROTH_PWDENTERED ; case '#': *pwdnr = 0 ; break ; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (*pwdnr >= 0) { *pwdnr = 10 * *pwdnr + (cc-'0') ; if (breakmode == 1) return ROTH_PWDENTERED ; } break ; case 'c': return RZYX_FAX ; case 'b': return RZYX_BUSY ; case 'h': case 'd': return RZYX_HANGUP ; /* 'd' = Dialtone (6.11a) */ case 'e': return RZYX_DATACALL ; /* 'e' = Data Calling Tone (6.11a) */ case ETX: return RZYX_ENDOFTEXT ; case 's': return RZYX_SILENCE ; case 'q': return RZYX_QUIET ; case DLE: *(block + (*blocksize)++) = cc ; break ; } } else if (cc == DLE) *dlestate = 1 ; else *(block + (*blocksize)++) = cc ; } return *exptime > 0 ? ROTH_COMPLETED : ROTH_EXPIRATION ; } /* breakmode = 1 */ static int readDigitDuringVoice (int line,long exptime,int *pwdnr) { int ii , recsize , dlestate = 0 ; char rec [100] , cc ; while (exptime > 0) { setTimer (ITIMER_REAL,IOTIMEOUT) ; recsize = read (line,rec,sizeof(rec)) ; if (recsize <= 0) return ROTH_TIMEOUT ; exptime -= IOTIMEOUT - setTimer (ITIMER_REAL,0L) ; for (ii = 0 ; ii < recsize ; ii++) { cc = *(rec+ii) ; if (dlestate) { dlestate = 0 ; switch (cc) { case '*': return (*pwdnr < 0) ? ROTH_STAR : ROTH_PWDENTERED ; case '#': *pwdnr = 0 ; break ; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (*pwdnr == 0) { *pwdnr = cc - '0' ; return ROTH_PWDENTERED ; } break ; case 'c': return RZYX_FAX ; case 'b': return RZYX_BUSY ; case 'h': case 'd': return RZYX_HANGUP ; /* 'd' = Dialtone (6.11a) */ case 'e': return RZYX_DATACALL ; /* 'e' = Data Calling Tone (6.11a) */ case ETX: return RZYX_ENDOFTEXT ; case 's': return RZYX_SILENCE ; case 'q': return RZYX_QUIET ; } } else if (cc == DLE) dlestate = 1 ; } } return ROTH_EXPIRATION ; } static int dialog (int line,long msec,char *until,char *question,...) { va_list ap ; char buffer [1000] ; long usedtime ; char sbuf [1000] , rbuf [1000] ; int len , rc = -1 ; va_start (ap,question) ; vsprintf (buffer,question,ap) ; va_end (ap) ; debugsputs (sbuf,buffer) ; debugsputs (rbuf,until) ; log (DEBUGFILE,"send \"%s\" wait for \"%s\"",sbuf,rbuf) ; len = strlen (buffer) ; if (write(line,buffer,len) == len) { if ((usedtime = readUntil(line,msec,until)) >= 0L) { log (DEBUGFILE,"got it after %ld msec",usedtime) ; rc = 0 ; } else log (DEBUGFILE,"reading failed (Timeout after %ld msec)",msec) ; } else log (DEBUGFILE,"writing failed") ; return rc ; } static int dialogWithAnswer (int line,long msec,char *answer,int answerlength,char *until,char *question,...) { va_list ap ; char buffer [1000] ; long usedtime ; char sbuf [1000] , rbuf [1000] ; int len , rc = -1 ; va_start (ap,question) ; vsprintf (buffer,question,ap) ; va_end (ap) ; debugsputs (sbuf,buffer) ; debugsputs (rbuf,until) ; log (DEBUGFILE,"send \"%s\" wait for \"%s\"",sbuf,rbuf) ; len = strlen (buffer) ; if (write(line,buffer,len) == len) { if ((usedtime = readAnswerUntil(line,msec,answer,answerlength,until)) >= 0L) { log (DEBUGFILE,"got \"%s\" after %ld msec",debugsputs(rbuf,answer),usedtime) ; rc = 0 ; } else log (DEBUGFILE,"reading failed (Timeout after %ld msec)",msec) ; } else log (DEBUGFILE,"writing failed") ; return rc ; } static int playVoice (int line,char *filename,int *pwdnr,int breakmode) { ZyxelSND zsnd ; int rc ; if (ZSNDread(filename,&zsnd) != 0) { log (DEBUGFILE,"Error reading ZyXEL voice file.") ; return -1 ; } if (ZSNDmakePlayable(&zsnd) != 0) { log (DEBUGFILE,"Error making playable ZyXEL voice.") ; return -1 ; } log (DEBUGFILE,"Playing voice %s",strrchr(filename,'/')+1) ; /* set Voice compression mode */ dialog (line,1000L,"OK\r\n","AT+VSM=%d\r",VSM(zsnd.zyxel.voice)) ; /* set to voice transmission */ dialog (line,1000L,"CONNECT\r\n","AT+VTX\r") ; rc = writeVoice (line,zsnd.play,zsnd.playlen,pwdnr,breakmode) ; /* set to end of voice */ dialog (line,4000L,"VCON\r\n","%c%c",DLE,ETX) ; ZSNDfree (&zsnd) ; return rc ; } static int receiveVoice (int line,int voiceCompressionMode,int *pwdnr,int breakmode) { ZyxelHeader zyxhead = { 0 } ; FILE *fpzsnd ; char buf [1000] ; int rc , buflen , dlestate = 0 ; long exptime = cfg_voiceexpiration * 1000L ; memcpy (zyxhead.title,ZYX_TITLE,strlen(ZYX_TITLE)) ; zyxhead.mode = ZYX_VOC ; zyxhead.voice = voiceCompressionMode ; if ((fpzsnd = fopen(TEMPVOICE,"w")) == NULL) { log (DEBUGFILE,"receiveVoice: cannot open temp voice file.") ; return -1 ; } if (fwrite(&zyxhead,sizeof(ZyxelHeader),1,fpzsnd) != 1) { log (DEBUGFILE,"receiveVoice: cannot write to temp voice file.") ; fclose (fpzsnd) ; return -1 ; } /* set silent detection values */ dialog (line,1000L,"OK\r\n","AT+VSD=%d,%d\r",SILENTSENSITIVITY,SILENTPERIOD) ; /* set voice compression mode */ dialog (line,1000L,"OK\r\n","AT+VSM=%d\r",VSM(voiceCompressionMode)) ; /* set to Voice transmission */ dialog (line,1000L,"CONNECT\r\n","AT+VRX\r") ; do { buflen = sizeof(buf) ; if ((rc = readVoice(line,&exptime,buf,&buflen,&dlestate,pwdnr,breakmode)) != ROTH_TIMEOUT) fwrite (buf,buflen,1,fpzsnd) ; } while (rc == ROTH_COMPLETED) ; /* set to end of voice */ dialog (line,4000L,"VCON\r\n","%c%c",DLE,ETX) ; fclose (fpzsnd) ; return rc ; } static int receiveDigitDuringVoice (int line,int voiceCompressionMode,int *pwdnr) { int rc ; /* set silent detection off */ dialog (line,1000L,"OK\r\n","AT+VSD=%d,%d\r",0,SILENTPERIOD) ; /* set voice compression mode */ dialog (line,1000L,"OK\r\n","AT+VSM=%d\r",VSM(voiceCompressionMode)) ; /* set to Voice transmission */ dialog (line,1000L,"CONNECT\r\n","AT+VRX\r") ; rc = readDigitDuringVoice (line,cfg_beepexpiration*1000L,pwdnr) ; /* set to end of voice */ dialog (line,4000L,"VCON\r\n","%c%c",DLE,ETX) ; return rc ; } static int receiveFax (int line,char *fileexpression) { char faxparameter [200] , *dcs , *pts , *et , *remoteid ; char FaxPageHeader [3] = { '0' , '0' , '0' } ; char buffer [2000] ; int rc , pages , dlestate ; char filename [MAXPATHLEN+1] ; FILE *fp ; char dc2 = DC2 , cc ; dialog (line,2000L,"OK\r\n","AT+FCLASS=2\r") ; dialog (line,2000L,"OK\r\n","AT&H3\r") ; /* RTS/CTS */ dialog (line,2000L,"OK\r\n","AT+FBOR=1\r") ; /* reverse bit order */ dialog (line,2000L,"OK\r\n","AT+FCR=1\r") ; dialog (line,2000L,"OK\r\n","AT+FLID=%s\r",cfg_localfaxid) ; dialogWithAnswer (line,30000L,faxparameter,sizeof(faxparameter),"\r\nOK","ATA\r") ; if ((dcs = strstr(faxparameter,"+FDCS:")) != NULL) { int vr , br , wd , ln , df , ec , bf , st ; char *VR [] = { "Normal, 98 lpi" , "Fine, 196 lpi" } ; char *BR [] = { "2400 bit/s V.27 ter" , "4800 bit/s V.27 ter" , "7200 bit/s V.29 or v.17" , "9600 bit/s V.29 or v.17" , "12000 bit/s V.33 or v.17" , "14400 bit/s V.33 or v.17" } ; char *WD [] = { "1728 pixels in 215 mm" , "2048 pixels in 255 mm" , "2432 pixels in 303 mm" , "1216 pixels in 151 mm" , "864 pixels in 107 mm" } ; char *LN [] = { "A4, 297 mm" , "B4, 364 mm" , "unlimited length" } ; char *DF [] = { "1-D modified Huffman" , "2-D modified Read" , "2-D uncompressed mode" , "2-D modified Read" } ; char *EC [] = { "Disable ECM" , "Enable ECM,64 bytes/frame" , "Enable ECM,256 bytes/frame" } ; char *BF [] = { "Disable BFT" , "Enable BFT" } ; char *ST [] = { "0 ms" , "5 ms" , "10 ms" , "10 ms" , "20 ms" , "20 ms" , "40 ms" , "40 ms" } ; sscanf (dcs,"+FDCS:%d,%d,%d,%d,%d,%d,%d,%d",&vr,&br,&wd,&ln,&df,&ec,&bf,&st) ; log (LOGFILE,"remote capabilities") ; log (LOGFILE," VR = %s",VR[vr]) ; log (LOGFILE," BR = %s",BR[br]) ; log (LOGFILE," WD = %s",WD[wd]) ; log (LOGFILE," LN = %s",LN[ln]) ; log (LOGFILE," DF = %s",DF[df]) ; log (LOGFILE," EC = %s",EC[ec]) ; log (LOGFILE," BF = %s",BF[bf]) ; log (LOGFILE," ST = %s",ST[st]) ; FaxPageHeader [0] = vr + '0' ; } if ((remoteid = strstr(faxparameter,"+FTSI:")) != NULL) { remoteid += strlen ("+FTSI:") ; *strchr(remoteid,'\r') = '\0' ; log (LOGFILE,"connected to %s",remoteid) ; } for ( pages = 1 ; ; ) { dialogWithAnswer (line,8000L,buffer,sizeof(buffer),"\r\nCONNECT\r\n","AT+FDR\r") ; log (LOGFILE,"--- reading page %d",pages) ; sprintf (filename,"%s.%03d",fileexpression,pages) ; fp = fopen (filename,"w") ; fwrite (FaxPageHeader,sizeof(FaxPageHeader),1,fp) ; write (line,&dc2,1) ; for (dlestate = 0 , rc = ROTH_COMPLETED ; rc == ROTH_COMPLETED ; ) { setTimer (ITIMER_REAL,IOTIMEOUT) ; if (read(line,&cc,1) <= 0) { rc = ROTH_TIMEOUT ; break ; } setTimer (ITIMER_REAL,0L) ; if (dlestate) { dlestate = 0 ; switch (cc) { case ETX: rc = RZYX_ENDOFTEXT ; break ; case DLE: fwrite (&cc,1,1,fp) ; break ; default: break ; } } else if (cc == DLE) dlestate = 1 ; else fwrite (&cc,1,1,fp) ; } fclose (fp) ; if (rc == ROTH_TIMEOUT) { log (ERRLOGFILE,"receiveFax TIMEOUT (should never happen)") ; break ; } /* read result of page receiving */ readAnswerUntil (line,2000L,buffer,sizeof(buffer),"OK\r\n") ; if ((pts = strstr(buffer,"+FPTS:")) != NULL) { int ppr , lc ; char *PPR [] = { "Partial page errors" , "Page Good" , "Page bad, retrain requested" , "Page good, retrain requested" , "Page bad, interrupt requested" , "Page good, interrupt requested" } ; sscanf (pts,"+FPTS:%d,%d",&ppr,&lc) ; log (LOGFILE,"Result of page %d: %s. %d lines",pages,PPR[ppr],lc) ; } if ((et = strstr(buffer,"+FET:")) != NULL) { int ppm ; sscanf (et,"+FET:%d",&ppm) ; log (DEBUGFILE,"FET result = %d",ppm) ; if (ppm == 0) pages++ ; else { if (ppm == 2) { char *hng ; int hangup ; dialogWithAnswer (line,4000L,buffer,sizeof(buffer),"\r\nOK\r\n","AT+FDR\r") ; if ((hng = strstr(buffer,"+FHNG:")) != NULL) { sscanf (hng,"+FHNG:%d",&hangup) ; log (LOGFILE,"hangup [%d]",hangup) ; } } rc = ROTH_COMPLETED ; break ; } } } if (rc == ROTH_COMPLETED) { /* generating an empty fax file */ sprintf (filename,"%s.fax",fileexpression) ; fp = fopen (filename,"w") ; fclose (fp) ; sprintf (filename,"/usr/lib/NextPrinter/faxcleanup -X -Q -c \"%s\" %d %s", remoteid,pages,fileexpression) ; system (filename) ; } return rc ; } static int dataCall (int line) { log (LOGFILE,"dataCall: not yet implemented.") ; return 0 ; } static char *MODEM_manufacturer (int line,char *buffer,int buflen) { return (dialogWithAnswer(line,1000L,buffer,buflen,"\r\nOK\r\n","AT+FMFR?\r") == 0) ? strrchr (buffer,'\n') + 1 : NULL ; } static char *MODEM_revision (int line,char *buffer,int buflen) { return (dialogWithAnswer(line,1000L,buffer,buflen,"\r\n\r\nOK\r\n","AT+FREV?\r") == 0) ? strrchr (buffer,'\n') + 1 : NULL ; } #define MODEM_beep(line) dialog (line,1000L,"OK\r\n","AT+VBT=2+VTS=A\r") #define MODEM_speaker(line,how) dialog (line,1000L,"VCON\r\n","AT+VLS=%d\r",how ? 16 : 0) ; static void sendVoiceMail (char *filename) { char cmd [MAXPATHLEN+1] ; char newname [MAXPATHLEN+1] ; if (*cfg_mailaddresses) { switch (cfg_voicemailmode) { case 0: break ; case 1: sprintf (cmd,"echo \"Please look at your Voice Reader.\" | /usr/ucb/mail " "-s \"AnsweringMachine\" %s > /dev/null &",cfg_mailaddresses) ; system (cmd) ; break ; case 2: sprintf (cmd,"%s %s \"%s\" > /dev/null &", cfg_execvoicemail,filename,cfg_mailaddresses) ; system (cmd) ; break ; case 3: sprintf (newname,"%s/__archive%s",cfg_voicespooldir,strrchr(filename,'/')) ; if (rename(filename,newname) == 0) { sprintf (cmd,"%s %s \"%s\" > /dev/null &", cfg_execvoicemail,newname,cfg_mailaddresses) ; system (cmd) ; } break ; } } } static void sendFaxMail (char *fileexpression) { char cmd [MAXPATHLEN+1] ; // char newname [MAXPATHLEN+1] ; if (*cfg_mailaddresses) { switch (cfg_faxmailmode) { case 0: break ; case 1: sprintf (cmd,"echo \"Please look at your Fax Reader.\" | /usr/ucb/mail " "-s \"AnsweringMachine\" %s > /dev/null &",cfg_mailaddresses) ; system (cmd) ; break ; case 2: sprintf (cmd,"%s %s \"%s\" > /dev/null &", cfg_execfaxmail,fileexpression,cfg_mailaddresses) ; system (cmd) ; break ; case 3: /* sorry, archiving not finished yet */ sprintf (cmd,"%s %s \"%s\" > /dev/null &", cfg_execfaxmail,fileexpression,cfg_mailaddresses) ; system (cmd) ; break ; } } } static int moveVoiceAssumingSilence (char *oldname,char *newname) { ZyxelSND zsnd ; int len ; FILE *fpzsnd ; if (ZSNDread(oldname,&zsnd) != 0) { log (DEBUGFILE,"Error reading ZyXEL voice file.") ; return -1 ; } if ((len = zsnd.voicelen - (SILENTPERIOD * VBPS(RECVCM)) / 10) < 0) { ZSNDfree (&zsnd) ; remove (oldname) ; return 1 ; } if ((fpzsnd = fopen(newname,"w")) == NULL) { log (DEBUGFILE,"Error writing ZyXEL voice file.") ; ZSNDfree (&zsnd) ; return rename (oldname,newname) ; } if (fwrite(&zsnd.zyxel,sizeof(ZyxelHeader),1,fpzsnd) != 1 || fwrite(zsnd.voice,len,1,fpzsnd) != 1) { log (DEBUGFILE,"Error writing ZyXEL voice file.") ; fclose (fpzsnd) ; remove (newname) ; ZSNDfree (&zsnd) ; return rename (oldname,newname) ; } fclose (fpzsnd) ; ZSNDfree (&zsnd) ; remove (oldname) ; return 0 ; } static void passwordReceived (int line,int pwdnr,char *timebuffer) { char filename [MAXPATHLEN+1] ; int rc ; if (pwdnr == cfg_voicerootnumber) { DIR *dirp = opendir (cfg_voicespooldir) ; struct direct *dp = NULL ; int extlen = strlen (ZSND) ; for ( ; ; ) { sprintf (filename,"%s/%s%s",cfg_voicesystemdir,VOC_ROOTMENU,ZSND) ; pwdnr = -1 ; rc = playVoice (line,filename,&pwdnr,1) ; if (rc != ROTH_COMPLETED && rc != ROTH_STAR && rc != ROTH_PWDENTERED) break ; if (rc != ROTH_PWDENTERED) rc = receiveDigitDuringVoice (line,RECVCM,&pwdnr) ; if (rc == ROTH_PWDENTERED) { switch (pwdnr) { case 1: while ((dp = readdir(dirp)) != NULL) { int namelen = strlen (dp->d_name) ; if (namelen >= extlen && !strcmp(dp->d_name+namelen-extlen,ZSND)) break ; } if (dp) sprintf (filename,"%s/%s",cfg_voicespooldir,dp->d_name) ; else sprintf (filename,"%s/%s%s",cfg_voicesystemdir,VOC_NOMSGTOPLAY,ZSND) ; break ; case 2: if (dp) { char newname [MAXPATHLEN+1] ; sprintf (filename,"%s/%s",cfg_voicespooldir,dp->d_name) ; sprintf (filename,"%s/__archive/%s",cfg_voicespooldir,dp->d_name) ; rename (filename,newname) ; sprintf (filename,"%s/%s%s",cfg_voicesystemdir,VOC_LASTMSGARCHIVED,ZSND) ; } else sprintf (filename,"%s/%s%s",cfg_voicesystemdir,VOC_NOMSGTOARCHIVE,ZSND) ; break ; default: sprintf (filename,"%s/%s%s",cfg_voicesystemdir,VOC_BADINPUT,ZSND) ; break ; } rc = playVoice (line,filename,&pwdnr,2) ; if (rc != ROTH_COMPLETED && rc != ROTH_STAR && rc != ROTH_PWDENTERED) break ; } else if (rc == ROTH_EXPIRATION) { sprintf (filename,"%s/%s%s",cfg_voicesystemdir,VOC_TIMELIMIT,ZSND) ; rc = playVoice (line,filename,&pwdnr,0) ; break ; } else break ; } closedir (dirp) ; } else { log (DEBUGFILE,"Next error dont care.") ; sprintf (filename,"%s/%d%s",cfg_voicefrienddir,pwdnr,ZSND) ; if ((rc = playVoice(line,filename,&pwdnr,2)) == -1) { sprintf (filename,"%s/%s%s",cfg_voicesystemdir,VOC_NOMSGFORFRIEND,ZSND) ; rc = playVoice (line,filename,&pwdnr,2) ; } if (rc == ROTH_COMPLETED || rc == ROTH_STAR || rc == ROTH_PWDENTERED) { MODEM_beep (line) ; rc = receiveVoice (line,RECVCM,&pwdnr,0) ; switch (rc) { case RZYX_FAX: case RZYX_DATACALL: remove (TEMPVOICE) ; log (LOGFILE,"receiveVoice: Unexpected event Data/Fax.") ; break ; case RZYX_SILENCE: remove (TEMPVOICE) ; log (DEBUGFILE,"receiveVoice: silent voice removed.") ; break ; case RZYX_BUSY: case RZYX_HANGUP: case RZYX_ENDOFTEXT: case ROTH_EXPIRATION: log (DEBUGFILE,"receiveVoice: rc = %d.",rc) ; sprintf (filename,"%s/%s%s",cfg_voicespooldir,timebuffer,ZSND) ; log (LOGFILE,"received call %s",filename) ; if (rename(TEMPVOICE,filename) == 0) sendVoiceMail (filename) ; break ; case RZYX_QUIET: log (DEBUGFILE,"receiveVoice: rc = %d.",rc) ; sprintf (filename,"%s/%s%s",cfg_voicespooldir,timebuffer,ZSND) ; log (LOGFILE,"received call %s",filename) ; if (moveVoiceAssumingSilence(TEMPVOICE,filename) == 0) sendVoiceMail (filename) ; break ; case ROTH_TIMEOUT: remove (TEMPVOICE) ; log (ERRLOGFILE,"receiveVoice: Timeout (Should never happen).") ; break ; case -1: log (ERRLOGFILE,"receiveVoice: File system problems (Should never happen).") ; break ; } } } } static void phoneCall (int line) { char filename [MAXPATHLEN+1] ; char timebuffer [60] ; int pwdnr = -1 , rc ; currentTimeString (timebuffer,sizeof(timebuffer)) ; sprintf (filename,"%s/%s%s",cfg_voicesystemdir,VOC_WELCOME,ZSND) ; switch ((rc = playVoice(line,filename,&pwdnr,2))) { case ROTH_PWDENTERED: log (LOGFILE,"Password #%d entered.",pwdnr) ; passwordReceived (line,pwdnr,timebuffer) ; break ; case RZYX_FAX: log (LOGFILE,"Trying to make a fax connection.") ; sprintf (filename,"%s/%s",cfg_faxspooldir,timebuffer) ; if (receiveFax(line,filename) == ROTH_COMPLETED) sendFaxMail (filename) ; break ; case RZYX_DATACALL: log (LOGFILE,"Trying to make a data connection.") ; dataCall (line) ; break ; case ROTH_STAR: case RZYX_BUSY: case RZYX_HANGUP: case RZYX_ENDOFTEXT: log (DEBUGFILE,"playVoice: rc = %d. No action.",rc) ; break ; case ROTH_COMPLETED: MODEM_beep (line) ; rc = receiveVoice (line,RECVCM,&pwdnr,2) ; switch (rc) { case ROTH_PWDENTERED: remove (TEMPVOICE) ; log (LOGFILE,"Password #%d entered.",pwdnr) ; passwordReceived (line,pwdnr,timebuffer) ; break ; case RZYX_FAX: remove (TEMPVOICE) ; log (LOGFILE,"Trying to make a fax connection.") ; sprintf (filename,"%s/%s",cfg_faxspooldir,timebuffer) ; if (receiveFax(line,filename) == ROTH_COMPLETED) sendFaxMail (filename) ; break ; case RZYX_DATACALL: remove (TEMPVOICE) ; log (LOGFILE,"Trying to make a data connection.") ; dataCall (line) ; break ; case RZYX_SILENCE: remove (TEMPVOICE) ; log (LOGFILE,"receiveVoice: silent voice.") ; break ; case ROTH_STAR: case RZYX_BUSY: case RZYX_HANGUP: case RZYX_ENDOFTEXT: case ROTH_EXPIRATION: log (DEBUGFILE,"receiveVoice: rc = %d.",rc) ; sprintf (filename,"%s/%s%s",cfg_voicespooldir,timebuffer,ZSND) ; log (LOGFILE,"received call %s",filename) ; if (rename(TEMPVOICE,filename) == 0) sendVoiceMail (filename) ; break ; case RZYX_QUIET: log (DEBUGFILE,"receiveVoice: rc = %d.",rc) ; sprintf (filename,"%s/%s%s",cfg_voicespooldir,timebuffer,ZSND) ; log (LOGFILE,"received call %s",filename) ; if (moveVoiceAssumingSilence(TEMPVOICE,filename) == 0) sendVoiceMail (filename) ; break ; case ROTH_TIMEOUT: remove (TEMPVOICE) ; log (ERRLOGFILE,"receiveVoice: Timeout (Should never happen).") ; break ; case -1: log (ERRLOGFILE,"receiveVoice: File system problems (Should never happen).") ; break ; } break ; case -1: case ROTH_TIMEOUT: log (ERRLOGFILE,"playVoice: rc = %d (Should never happen).",rc) ; break ; } } static void takeOver (int line) { char filename [MAXPATHLEN+1] ; char timebuffer [60] ; int pwdnr = -1 , rc ; dialog (line,4000L,"VCON\r\n","ATA\r") ; rc = receiveDigitDuringVoice (line,RECVCM,&pwdnr) ; switch (rc) { case RZYX_FAX: log (LOGFILE,"Trying to make a fax connection.") ; currentTimeString (timebuffer,sizeof(timebuffer)) ; sprintf (filename,"%s/%s",cfg_faxspooldir,timebuffer) ; if (receiveFax(line,filename) == ROTH_COMPLETED) sendFaxMail (filename) ; break ; case RZYX_DATACALL: log (LOGFILE,"Trying to make a data connection.") ; dataCall (line) ; break ; default: log (DEBUGFILE,"receiveDigitDuringVoice: rc = %d.",rc) ; break ; } } static int initModem (int line) { char buf [400] ; log (LOGFILE,"----- init modem -----") ; dialog (line,2000L,"OK\r\n","AT\r") ; dialog (line,2000L,"OK\r\n","ATZ\r") ; log (LOGFILE,"%s modem",MODEM_manufacturer(line,buf,sizeof(buf))) ; log (LOGFILE,"rev %s",MODEM_revision(line,buf,sizeof(buf))) ; dialog (line,2000L,"OK\r\n","ATE1M%dL%dS0=%d\r",cfg_speaker,cfg_volume,cfg_rings) ; dialog (line,2000L,"OK\r\n","AT&D3&S1\r") ; dialog (line,2000L,"OK\r\n","AT+FCLASS=8\r") ; return 0 ; } /***** ***** Strategy for accessing and locking device DIALOUT ***** After every foreign access (and at our start time) we have to ***** initialize the modem, which we can only do, if we have locked the device. ***** If we cannot lock the device, we wait 2 seconds and try again. ***** The initialization of the modem after a foreign access is necessary, ***** because we have no idea, what the other process has done with it. ***** ***** Now we wait ad infinitum for any bytes on the device we could read ***** (via 'select'). If there are some, we try to lock the device for reading ***** these bytes. If we cannot (because device is already locked), we take ***** hands off and ckeck the lock from time to time to see, whether the device ***** is unlocked. If we can, we assume, that the modem wants to tell US something, ***** in general 'RING'. ***** If not, probably a stupid process tries to talk with the modem without ***** locking the device. Great confusion will occur, and no process will ***** understand a word. But who cares. ***** ***** Now we handle the phone call, unlock the device and finish. *****/ int main (int argc,char *argv[]) { int line , ringcount ; char buf [1000] ; #ifndef TESTMODE fd_set fdset ; #endif log (LOGFILE,"***** Starting Answering Machine *****") ; setDefaults () ; #ifndef TESTMODE if (argc > 2) #else if (argc > 1) #endif { if (configurate(argv[1]) == 0) log (LOGFILE," using config file \'%s\'",argv[1]) ; else log (LOGFILE," config file \'%s\' not found (using defaults)",argv[1]) ; } else log (LOGFILE," using defaults") ; signal (SIGALRM,signalhandler) ; siginterrupt (SIGALRM,1) ; //signal (SIGINT,signalhandler) ; //signal (SIGTERM,signalhandler) ; if ((line = openDevice(DIALOUT,cfg_port)) < 0) { log (LOGFILE,"cannot open dial out line") ; sleep (2) ; return 1 ; } #ifndef TESTMODE FD_ZERO (&fdset) ; FD_SET (line,&fdset) ; /* endless loop to get control over the modem after activity */ for ( ; ; ) { while (lockDevice(DIALOUT,cfg_port) != 0) { log (LOGFILE,"cannot lock dial out line") ; sleep (2) ; } initDevice (line,B38400) ; initModem (line) ; unlockDevice (DIALOUT,cfg_port) ; select (1,&fdset,NULL,NULL,NULL) ; log (LOGFILE,"Activity on port") ; if (lockDevice(DIALOUT,cfg_port) != 0) { log (LOGFILE,"port in use") ; do { sleep (2) ; } while (deviceLocked(DIALOUT,cfg_port) == 0) ; log (LOGFILE,"port freed") ; } else break ; } remove (FAXSIGNAL) ; /* endless loop to check, what does the modem wants to tell us */ for ( ringcount = 0 ; ; ) { if (readUntil(line,6000L,"\r\n") != -1L && readAnswerUntil(line,500L,buf,sizeof(buf),"\r\n") != -1L) { if (strstr(buf,"RING")) { log (LOGFILE,"ringing %d",++ringcount) ; } else if (strstr(buf,"VCON")) { log (LOGFILE,"Voice connection.") ; phoneCall (line) ; break ; } else { log (LOGFILE,"no reaction to modem opinion \"%s\"",buf) ; break ; } } else { if (ringcount) { /* we wait 10 seconds for a fax signal */ for (ringcount = 0 ; ringcount < 50 ; ringcount++) { if (access(FAXSIGNAL,F_OK) == 0) { takeOver (line) ; remove (FAXSIGNAL) ; break ; } usleep (200000L) ; } } else log (LOGFILE,"cannot understand modem speech") ; break ; } } unlockDevice (DIALOUT,cfg_port) ; #else initDevice (line,B38400) ; initModem (line) ; /* endless loop to check, what does the modem wants to tell us */ for ( ringcount = 0 ; ; ) { if (readUntil(line,6000L,"\r\n") != -1L && readAnswerUntil(line,500L,buf,sizeof(buf),"\r\n") != -1L) { if (strstr(buf,"RING")) { log (LOGFILE,"ringing %d",++ringcount) ; } else if (strstr(buf,"VCON")) { log (LOGFILE,"Voice connection.") ; MODEM_speaker (line,1) ; phoneCall (line) ; break ; } else { log (LOGFILE,"no reaction to modem opinion \"%s\"",buf) ; break ; } } else log (LOGFILE,"timeout for connection") ; } #endif closeDevice (line) ; return 0 ; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.