#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <stdarg.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOCFUnserialize.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include "CCLArgFunctions.h"
#include "cclkeys.h"
#include <SystemConfiguration/SCSchemaDefinitions.h>
#include "CCLEngine.h"
#include "CCLEngine_defs.h"
#include <AssertMacros.h>
#define min(a,b) (((a)<=(b))?(a):(b))
#define kNormalDialMode 0
#define kBlindDialMode 1 // CCL term; SC uses is "IgnoreDialTone"
#define kManualDialMode 2 // user does the dialing
#define DIR_MODEMS_USER "/Library/Modem Scripts"
#define DIR_MODEMS_SYS "/System/Library/Modem Scripts"
enum {
kUserMsgFLog = 1,
kUserMsgFStatus = 2
};
#define infd STDIN_FILENO
#define outfd STDOUT_FILENO
#define PPP_ARG_FD 3
enum
{
kInvalidTimer = 0,
kMatchReadTimer,
kPauseTimer,
kCharDelayTimer,
kBreakTimer
};
enum enginemode {
mode_connect = 0,
mode_disconnect,
mode_listen
};
struct callout {
struct timeval c_time;
void *c_arg;
void (*c_func)(void *);
struct callout *c_next;
};
u_int8_t gNullString[2];
char *Commands[] =
{
"!\0",
"@CCLSCRIPT\0",
"@ORIGINATE\0",
"@ANSWER\0",
"@HANGUP\0",
"@LABEL\0",
"ASK\0",
"CHRDELAY\0",
"COMMUNICATINGAT\0",
"DECTRIES\0",
"DTRSET\0",
"DTRCLEAR",
"EXIT",
"FLUSH",
"HSRESET",
"IFANSWER",
"IFORIGINATE",
"IFSTR",
"IFTRIES",
"INCTRIES",
"JUMP",
"JSR",
"LBREAK",
"LOGMSG", "MATCHCLR",
"MATCHREAD",
"MATCHSTR",
"NOTE",
"PAUSE",
"RETURN",
"SBREAK",
"SERRESET",
"SETSPEED",
"SETTRIES",
"USERHOOK",
"WRITE",
"MONITORLINE",
"DEBUGLEVEL"
};
TRScriptVars SV; u_int8_t *VarStrings[vsMax+1];
u_int8_t DialString1MaxLen;
u_int8_t DialString2MaxLen;
u_int8_t DialString3MaxLen;
u_int8_t VerboseBuffer[256];u_int32_t LastExitError;
u_int32_t LastAskedMasked;
FILE* filefd = (FILE *) 0;
char *filename = NULL;
char *phone_num = "";
char *username = "";
char *password = "";
char *alertname = "";
char *cancelname = "";
char *iconurl = "";
CFStringRef alertNameRef = 0;
CFStringRef cancelNameRef = 0;
CFURLRef iconURL = 0;
char* localBunPath = "";
CFURLRef localBundleURL = NULL;
CFBundleRef cclBundle = NULL;
CFURLRef cclBundleURL= NULL;
CFURLRef appBundleURL= NULL;
int pulse = 0;
int dialmode = 0; int speaker = 1;
int errorcorrection = 1;
int datacompression = 1;
u_char *serviceID = NULL;
int verbose = 0;
u_int32_t debuglevel = 0;
int sysloglevel = LOG_NOTICE;
int syslogfacility = 0; int usestderr = 1;
int signalerror = 0;
struct callout *callout = NULL;
struct timeval timenow;
enum enginemode enginemode = mode_connect;
fd_set allset;
int maxfd;
void SetVarString(u_int32_t vs, u_int8_t * data);
u_int8_t *GetVarString(u_int32_t vs);
int PrepScript();
u_int8_t NextLine();
int NextCommand();
int NextInt(u_int32_t *theIntPtr); void PrepStr(u_int8_t *destStr, u_int32_t *isVarString, u_int32_t *varIndex, int varSubstitution);
void SkipBlanks();
void RunScript();
int MatchStr();
void Note();
u_int8_t Write();
void CommunicatingAt();
u_int8_t Ask();
void WriteContinue();
void UserHook();
void ScheduleTimer(long type, u_int32_t milliseconds);
int IfStr();
void MatchClr();
void Break();
void SetSpeed(void);
void SerReset(void);
void MonitorLine();
void DebugLevel();
void HSReset();
void Flush();
void DTRCommand(short DTRCode);
void terminate(int exitError);
void InitScript();
void Play();
void calltimeout(void);
struct timeval *timeleft(struct timeval *);
void timeout(void (*func)(void *), void *arg, u_long time);
void untimeout(void (*func)(void *), void *arg);
void ReceiveMatchData(u_int8_t nextChar);
int publish_entry(u_char *serviceid, CFStringRef entry, CFTypeRef value);
int unpublish_entry(u_char *serviceid, CFStringRef entry);
bool SetVarStringFromDict(CFDictionaryRef dict, CFStringRef key, int varStringIndex);
void sLog(char *fmt, ...);
#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
(_O=4,(char*)0):(char*)0)
#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0)
#define ARG(c,v) (c?(--c,*v++):(char*)0)
void badsignal(int signo)
{
signalerror = cclErr_NoMemErr;
}
void hangup(int signo)
{
if (enginemode != mode_disconnect)
signalerror = cclErr_ScriptCancelled;
}
void StartRead()
{
FD_SET(infd, &allset);
maxfd = 1;
}
void StopRead()
{
FD_ZERO(&allset);
maxfd = 0;
}
#pragma mark Bundle Configuration/Startup
bool SetVarStringFromDict(CFDictionaryRef dict, CFStringRef key, int varStringIndex)
{
bool retVal= false;
u_char text[256];
CFStringRef varString;
varString= (CFStringRef)CFDictionaryGetValue(dict, key);
if(varString == NULL) {
retVal = true; } else if(CFStringGetTypeID() == CFGetTypeID(varString)) {
if(CFStringGetPascalString(varString,text,256,CFStringGetSystemEncoding()))
{
SetVarString(varStringIndex, text);
retVal= true;
}
} else {
sLog("SetVarStringFromDict: value with non-string type");
CFShow(key);
CFShow(varString);
}
return retVal;
}
bool VerifyCCLBundle(CFBundleRef cclBun)
{
CFDictionaryRef infoDict= CFBundleGetInfoDictionary(cclBun);
int verNum = -1;
if(!infoDict || !GetIntFromDict(infoDict, &verNum, kCCLVersionKey))
return false;
return (verNum == kCCLBundleVersion);
}
bool ConfigureCCLParameters(CFDictionaryRef personalityDict)
{
bool rval = false;
CFDictionaryRef cclParms= GetCFDictionaryFromDict(personalityDict, kCCLParametersKey);
if(cclParms)
{
rval = SetVarStringFromDict(cclParms, kCCLVarString27Key, vsString27);
rval &= SetVarStringFromDict(cclParms, kCCLVarString28Key, vsString28);
rval &= SetVarStringFromDict(cclParms, kCCLVarString29Key, vsString29);
rval &= SetVarStringFromDict(cclParms, kCCLVarString30Key, vsString30);
rval&=SetVarStringFromDict(cclParms,kCCLConnectSpeedKey,vsConnectSpeed);
rval &= SetVarStringFromDict(cclParms, kCCLInitStringKey, vsInit);
}
return rval;
}
CFURLRef
CopyScriptWithPersonality(CFBundleRef bundleRef, CFStringRef personalityKey)
{
CFURLRef rval = NULL;
CFDictionaryRef infoDict;
CFDictionaryRef persDict;
CFDictionaryRef persEntry = NULL;
if (!(infoDict = CFBundleGetInfoDictionary(bundleRef)))
goto finish;
if (!(persDict = GetCFDictionaryFromDict(infoDict, kCCLPersonalitiesKey)))
goto finish;
if (personalityKey)
persEntry = GetCFDictionaryFromDict(persDict, personalityKey);
if (!persEntry)
persEntry = GetCFDictionaryFromDict(persDict,kCCLDefaultPersonalityKey);
if (persEntry) {
CFStringRef scriptName = NULL;
if (!GetCFStringFromDict(persEntry,&scriptName,kCCLScriptNameKey))
goto finish;
if (!scriptName)
goto finish;
rval = CFBundleCopyResourceURL(bundleRef, scriptName, NULL, NULL);
if (rval) {
if (!ConfigureCCLParameters(persEntry)) {
CFRelease(rval);
rval = NULL;
}
}
}
finish:
return rval;
}
bool ConfigureWithBundle(CFBundleRef bundleRef, CFStringRef personality)
{
bool retVal= false;
UInt8 pathBuffer[MAXPATHLEN];
if(bundleRef!= NULL)
{
if(VerifyCCLBundle(bundleRef))
{
CFURLRef scriptURL= CopyScriptWithPersonality(bundleRef, personality);
if(scriptURL!= NULL)
{
if(CFURLGetFileSystemRepresentation(scriptURL, true, pathBuffer, MAXPATHLEN))
{
if (filename) free(filename);
filename = strdup((char*)pathBuffer);
retVal= true;
CFRetain(bundleRef);
cclBundle= bundleRef;
}
CFRelease(scriptURL);
}
}
}
return retVal;
}
bool ResolveCCLPath(char *path, char fullpath[PATH_MAX], struct stat *sb)
{
char *extptr = path + (strlen(path) - (kCCL_BundleExtLen + sizeof('.')));
bool cclExt;
cclExt = (*extptr == '.') && (strcmp(extptr+1, kCCL_BundleExtension) == 0);
if (path[0] == '/') {
if (!cclExt) {
snprintf(fullpath, PATH_MAX, "%s.%s", path, kCCL_BundleExtension);
if (stat(fullpath, sb) == 0) return true;
}
strlcpy(fullpath, path, PATH_MAX);
if (stat(fullpath, sb) == 0) return true;
} else {
if (!cclExt) {
snprintf(fullpath, PATH_MAX, "%s/%s.%s", DIR_MODEMS_SYS, path,
kCCL_BundleExtension);
if (stat(fullpath, sb) == 0) return true;
}
snprintf(fullpath, PATH_MAX, "%s/%s", DIR_MODEMS_SYS, path);
if (stat(fullpath, sb) == 0) return true;
if (!cclExt) {
snprintf(fullpath, PATH_MAX, "%s/%s.%s", DIR_MODEMS_USER, path,
kCCL_BundleExtension);
if (stat(fullpath, sb) == 0) return true;
}
snprintf(fullpath, PATH_MAX, "%s/%s", DIR_MODEMS_USER, path);
if (stat(fullpath, sb) == 0) return true;
}
return false;
}
#define LOG_MAX 1024
void sLog(char *fmt, ...)
{
va_list ap;
char taggedfmt[LOG_MAX], s[64];
time_t t;
if (sysloglevel) {
va_start(ap, fmt);
vsyslog(sysloglevel, fmt, ap);
va_end(ap);
}
if (usestderr) {
time(&t);
strftime(s, sizeof(s), "%c : ", localtime(&t));
strlcpy(taggedfmt, s, LOG_MAX);
strlcat(taggedfmt, fmt, LOG_MAX);
strlcat(taggedfmt, "\n", LOG_MAX);
va_start(ap, fmt);
vfprintf(stderr, taggedfmt, ap);
va_end(ap);
}
}
bool ConfigureCCL(char* path, CFStringRef personality)
{
bool retVal = false;
CFURLRef url;
CFBundleRef bundleRef;
char fullpath[PATH_MAX];
struct stat sb;
if (!ResolveCCLPath(path, fullpath, &sb)) {
sLog("couldn't find CCL '%s'", path);
goto finish;
}
url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
(UInt8*)fullpath, strlen(fullpath), S_ISDIR(sb.st_mode));
if (!url) goto finish;
bundleRef= CFBundleCreate(kCFAllocatorDefault, url);
if(bundleRef) {
retVal = ConfigureWithBundle(bundleRef, personality);
CFRelease(bundleRef);
if (retVal)
cclBundleURL = url; else {
CFRelease(url);
sLog("%s does not appear to be a CCL bundle", fullpath);
}
} else {
if (S_ISREG(sb.st_mode)) {
CFRelease(url);
if (filename) free(filename);
filename = strdup(fullpath);
retVal = true;
} else {
sLog("%s is neither a bundle nor a file", filename);
}
}
finish:
return retVal;
}
bool ParseEngineDict(CFDictionaryRef argDict)
{
bool success= false;
CFDictionaryRef engineDict= GetCFDictionaryFromDict(argDict, kCCLEngineDictKey);
if(engineDict!= NULL)
{
CFStringRef modeString= (CFStringRef) CFDictionaryGetValue(engineDict, kCCLEngineModeKey);
if(modeString!= NULL)
{
if((CFStringGetTypeID()== CFGetTypeID(modeString)) && (CFStringCompare(modeString, kCCLEngineModeDisconnect, 0)== kCFCompareEqualTo))
{
enginemode = mode_disconnect;
}
success = CopyCStringFromDict(engineDict, &alertname, kCCLEngineAlertNameKey);
success &= CopyCStringFromDict(engineDict, &localBunPath, kCCLEngineBundlePathKey);
success &= CopyCStringFromDict(engineDict, &cancelname, kCCLEngineCancelNameKey);
success &= CopyCStringFromDict(engineDict, &iconurl, kCCLEngineIconPathKey);
success &= GetIntFromDict(engineDict, &usestderr, kCCLEngineLogToStdErrKey);
success &= CopyCStringFromDict(engineDict, (char**) &serviceID, kCCLEngineServiceIDKey);
success &= GetIntFromDict(engineDict, &syslogfacility, kCCLEngineSyslogFacilityKey);
success &= GetIntFromDict(engineDict, &sysloglevel, kCCLEngineSyslogLevelKey);
success &= GetIntFromDict(engineDict, &verbose, kCCLEngineVerboseLoggingKey);
}
}
return success;
}
bool ParseModemDict(CFDictionaryRef argDict)
{
bool success= false;
CFDictionaryRef modemDict= GetCFDictionaryFromDict(argDict, kSCEntNetModem);
if(modemDict!= NULL)
{
char* tempFilePath = NULL;
CFStringRef personality = NULL;
if (!CopyCStringFromDict(modemDict, &tempFilePath, kSCPropNetModemConnectionScript))
goto finish;
if (!tempFilePath)
goto finish;
if (!GetCFStringFromDict(modemDict, &personality, kSCPropNetModemConnectionPersonality))
goto finish;
success = ConfigureCCL(tempFilePath, personality);
free(tempFilePath);
if (!success)
goto finish;
dialmode= kNormalDialMode; CFStringRef dialModeStr = (CFStringRef) CFDictionaryGetValue(modemDict, kSCPropNetModemDialMode);
if(dialModeStr && CFStringGetTypeID()==CFGetTypeID(dialModeStr)){
if (kCFCompareEqualTo == CFStringCompare(dialModeStr,
kSCValNetModemDialModeIgnoreDialTone,0))
dialmode = kBlindDialMode;
else if (kCFCompareEqualTo == CFStringCompare(dialModeStr,
kSCValNetModemDialModeManual,0))
dialmode = kManualDialMode;
}
if (enginemode == mode_connect) {
success &= GetIntFromDict(modemDict, &datacompression, kSCPropNetModemDataCompression);
success &= GetIntFromDict(modemDict, &errorcorrection, kSCPropNetModemErrorCorrection);
success &= CopyCStringFromDict(modemDict,&phone_num,kModemPhoneNumberKey);
success &= GetIntFromDict(modemDict, &pulse, kSCPropNetModemPulseDial);
success &= GetIntFromDict(modemDict, &speaker, kSCPropNetModemSpeaker);
success &= CopyCStringFromDict(modemDict, &username, kSCPropNetPPPAuthName);
success &= CopyCStringFromDict(modemDict, &password, kSCPropNetPPPAuthPassword);
success &= SetVarStringFromDict(modemDict, kSCPropNetModemAccessPointName, vsAPN);
success &= SetVarStringFromDict(modemDict, kSCPropNetModemDeviceContextID, vsCID);
}
success &= SetVarStringFromDict(modemDict, kSCPropNetModemConnectSpeed, vsConnectSpeed);
}
finish:
return success;
}
bool ParseArgDictionary(CFDictionaryRef argDict)
{
CFBundleRef mainBundle= NULL;
if(ParseEngineDict(argDict)) {
if(ParseModemDict(argDict)) {
mainBundle= CFBundleGetMainBundle();
if(mainBundle)
appBundleURL= CFBundleCopyBundleURL(mainBundle);
return true;
}
}
return false;
}
#define kParseBufferSize 4096
bool ParsePipeArgs()
{
ssize_t bRead= 0;
ssize_t tbRead= 0;
bool retVal= false;
void* tempBuf;
char* dataBuf= malloc(kParseBufferSize);
if (!dataBuf) goto finish;
bRead= read(PPP_ARG_FD, dataBuf, kParseBufferSize);
tbRead= bRead;
while (bRead == kParseBufferSize) {
tempBuf= realloc(dataBuf, (tbRead+ kParseBufferSize));
if (tempBuf) {
dataBuf= tempBuf;
bRead= read(PPP_ARG_FD, &dataBuf[tbRead], kParseBufferSize);
tbRead+= bRead;
} else {
goto finish;
}
}
CFDictionaryRef argDict= IOCFUnserialize(dataBuf, kCFAllocatorDefault, kCFPropertyListImmutable, NULL);
if(argDict!= NULL)
{
retVal= ParseArgDictionary(argDict);
}
finish:
if (dataBuf) free(dataBuf);
close(PPP_ARG_FD);
return retVal;
}
int main(int argc, char **argv)
{
char c;
int ret, nready, status, i;
struct stat statbuf;
fd_set rset;
struct timeval timo;
signal(SIGHUP, hangup);
signal(SIGINT, hangup);
signal(SIGTERM, hangup);
signal(SIGCHLD, badsignal);
signal(SIGUSR1, badsignal);
signal(SIGUSR2, badsignal);
signal(SIGABRT, badsignal);
signal(SIGALRM, badsignal);
signal(SIGFPE, badsignal);
signal(SIGILL, badsignal);
signal(SIGPIPE, badsignal);
signal(SIGQUIT, badsignal);
signal(SIGSEGV, badsignal);
for (i = 0; i <= vsMax; i++)
VarStrings[i] = 0;
openlog("ccl", LOG_PID | LOG_NDELAY, syslogfacility);
if(!ParsePipeArgs())
terminate(cclErr_BadParameter);
InitScript();
alertNameRef = CFStringCreateWithCString(NULL, alertname, kCFStringEncodingUTF8);
cancelNameRef = CFStringCreateWithCString(NULL, cancelname, kCFStringEncodingUTF8);
if (*iconurl)
iconURL = CFURLCreateFromFileSystemRepresentation(nil, (UInt8*) iconurl, strlen(iconurl), false );
if (*localBunPath)
localBundleURL = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*) localBunPath, strlen(localBunPath), true );
ret = stat(filename, &statbuf);
if (ret < 0)
terminate(cclErr_BadParameter);
SV.script = malloc((int)statbuf.st_size);
if (!SV.script)
terminate(cclErr_NoMemErr);
SV.scriptSize = (int)statbuf.st_size;
filefd = fopen(filename, "r");
if (filefd) {
ret = fread(SV.script, statbuf.st_size, 1, filefd);
if (ret < 0)
terminate(cclErr_BadParameter);
}
if (serviceID) {
unpublish_entry((u_char*) serviceID, kSCPropNetModemConnectSpeed);
}
StopRead();
Play();
for ( ; ; ) {
rset = allset;
nready = select(maxfd, &rset, NULL, NULL, timeleft(&timo));
if(signalerror)
terminate(signalerror);
if (FD_ISSET(infd, &rset)) {
status = read(infd, &c, 1);
switch (status) {
case 1:
ReceiveMatchData(c);
break;
default:
if (errno != EINTR)
terminate(cclErr);
}
}
calltimeout();
}
exit(0); return 0; }
void InitScript()
{
u_char text[256], text1[256];
gNullString[0] = 1;
gNullString[1] = ' ';
bzero(&SV, sizeof(struct TRScriptVars));
LastExitError = 0;
DialString1MaxLen = 40;
DialString2MaxLen = 40;
DialString3MaxLen = 40;
SV.topOfStack = cclNestingLimit; SV.theAbortErr = 0;
SV.lineCount = 0;
SV.scriptPrepped = 0; SV.scriptPrepFailed = 0; SV.scriptAllocSize = 0;
SV.scriptSize = 0;
SV.script = NULL;
SV.askLabel = 0;
SV.indexTable = NULL;
SV.commands = NULL;
LastAskedMasked = 0;
text[0] = strlen(username);
bcopy(username, &text[1], text[0]);
SetVarString(vsUserName, text);
text[0] = strlen(password);
bcopy(password, &text[1], text[0]);
SetVarString(vsPassWord, text);
sprintf((char*) text, "%d", speaker);
text1[0] = strlen((char*) text);
bcopy(text, &text1[1], text[0]);
SetVarString(vsModemSpeaker, text1);
sprintf((char*) text, "%d", errorcorrection);
text1[0] = strlen((char*) text);
bcopy(text, &text1[1], text[0]);
SetVarString(vsErrorCorrection, text1);
sprintf((char*) text, "%d", datacompression);
text1[0] = strlen((char*) text);
bcopy(text, &text1[1], text[0]);
SetVarString(vsDataCompression, text1);
sprintf((char*) text, "%d", dialmode);
text1[0] = strlen((char*) text);
bcopy(text, &text1[1], text[0]);
SetVarString(vsDialMode, text1);
text1[0] = 1;
text1[1] = pulse ? 'P' : 'T';
SetVarString(vsTonePulse, text1);
}
void DisposeScript()
{
int i;
if (SV.script) {
free(SV.script);
SV.script = 0;
}
if (SV.commands) {
free(SV.commands);
SV.commands = 0;
}
if (SV.indexTable) {
free(SV.indexTable);
SV.indexTable = 0;
}
for (i = 0; i <= vsMax; i++) {
if (VarStrings[i]) {
free(VarStrings[i]);
VarStrings[i] = 0;
}
}
}
void SetVarString(u_int32_t vs, u_int8_t * data)
{
if (vs > vsMax)
return;
if (VarStrings[vs]) {
free(VarStrings[vs]);
VarStrings[vs] = 0;
}
if (data) {
VarStrings[vs] = malloc(*data + 1);
if (VarStrings[vs])
bcopy(data, VarStrings[vs], *data + 1 );
else
sLog("SetVarString: %s", strerror(errno)); }
}
u_int8_t *GetVarString(u_int32_t vs)
{
if (vs > vsMax)
{
return gNullString;
}
if (VarStrings[vs])
{
return VarStrings[vs];
}
return gNullString;
}
void Play(void)
{
u_int32_t remaining;
u_int8_t *src, len = 0, buf[256];
u_char text[256];
LastExitError = 0;
if (!SV.scriptPrepped ) { if (SV.theAbortErr = PrepScript()) { SV.scriptPrepFailed = 1; sLog("Script parsing failed after %d lines", SV.scriptLine);
terminate(SV.theAbortErr); return;
}
else
SV.scriptPrepped = 1;
}
SV.chrDelayValue = 0;
switch (enginemode) {
case mode_connect:
SV.ctlFlags &= ~cclHangupMode;
SV.ctlFlags |= cclOriginateMode; SV.scriptLine = SV.originateLine;
text[0] = strlen(phone_num);
bcopy(phone_num, &text[1], text[0]);
if (GetVarString(vsDialString) == gNullString)
SetVarString(vsDialString, text);
SetVarString(vsDialString1, NULL);
SetVarString(vsDialString2, NULL);
SetVarString(vsDialString3, NULL);
if (src = GetVarString(vsDialString)) {
remaining = *src++;
if (len = min(DialString1MaxLen, remaining)) {
*buf = len;
bcopy(src, buf+1, len);
SetVarString(vsDialString1, buf);
src += len;
remaining -= len;
}
if (len = min(DialString2MaxLen, remaining)) {
*buf = len;
bcopy(src, buf+1, len);
SetVarString(vsDialString2, buf);
src += len;
remaining -= len;
}
if (len = min(DialString3MaxLen, remaining)) {
*buf = len;
bcopy(src, buf+1, len);
SetVarString(vsDialString3, buf);
src += len;
remaining -= len;
}
}
break;
case mode_disconnect:
SV.ctlFlags &= ~cclAnswerMode; SV.ctlFlags |= cclHangupMode;
SV.scriptLine = SV.hangUpLine; break;
case mode_listen:
SV.ctlFlags |= cclAnswerMode; SV.ctlFlags &= ~cclHangupMode;
SV.scriptLine = SV.answerLine; break;
}
RunScript(); }
int PrepScript()
{
u_int8_t *bp, *d, *s, c;
u_int16_t *indexp, Lindex, i, cmd;
u_int32_t labelIndex;
int result = 0;
do {
if (SV.scriptSize > MAX_SCRIPT_SIZE) {
result = cclErr_ScriptTooBig;
break;
}
Lindex = 1;
bp = SV.script;
for( i = SV.scriptSize; i; --i ) {
if ((*bp == chrCR) || (*bp == chrNL))
Lindex++;
bp++;
}
if (Lindex > MAX_SCRIPT_LINES) {
result = cclErr_TooManyLines;
break;
}
SV.lineCount = Lindex - 1;
c = SV.script[SV.scriptSize-1];
if (c != chrCR && c != chrNL)
SV.lineCount++;
bp = malloc(500);
if (!bp) {
result = ENOMEM;
break;
}
SV.commands = bp;
*((u_int16_t *)bp) = cLastCmd;
bp += 2;
for( i = 0; i < cLastCmd; ) {
s = (u_int8_t*) Commands[i++];
d = bp + 1;
while( c = *s++ )
*d++ = c;
*bp = (d - bp - 1); bp = d;
}
SV.indexTable = malloc(SV.lineCount * sizeof(u_int16_t *));
if (!SV.indexTable) {
result = ENOMEM;
break;
}
indexp = SV.indexTable;
for( i = 0; ++i <= SV.lineCount; )
*indexp++ = 0;
indexp = SV.indexTable;
bp = SV.script;
Lindex = 0;
*indexp++ = 0;
for (i = 0; i < SV.scriptSize; i++) {
if ((*bp == chrCR) || (*bp == chrNL))
*indexp++ = i + 1;
bp++;
}
SV.scriptLine = 0;
}
while (0);
while (result == 0 && NextLine()) {
cmd = NextCommand();
switch (cmd) {
case cNoCmd:
result = cclErr_BadCommand;
break;
case cOriginateLabel:
if( SV.originateLine )
result = cclErr_DuplicateLabel;
else
SV.originateLine = SV.scriptLine;
break;
case cAnswerLabel:
if( SV.answerLine )
result = cclErr_DuplicateLabel;
else
SV.answerLine = SV.scriptLine;
break;
case cHangUpLabel:
if( SV.hangUpLine )
result = cclErr_DuplicateLabel;
else
SV.hangUpLine = SV.scriptLine;
break;
case cScriptLabel:
result = NextInt(&labelIndex);
if (result == 0) {
labelIndex--;
if ((labelIndex < 0) || (labelIndex >= MAXLABELS))
result = cclErr_BadLabel;
else {
if (SV.labels[labelIndex])
result = cclErr_DuplicateLabel;
else
SV.labels[labelIndex] = SV.scriptLine;
}
}
break;
case cExit:
result = NextInt(&labelIndex);
break;
case cHSReset:
for (i = 0; result == 0 && i < kHSResetParamCount; i++)
result = NextInt(&labelIndex);
break;
case cAsk: case cChrDelay: case cCommunicatingAt: case cIfAnswer: case cIfOriginate: case cJSR: case cJump: case cMatchRead: case cPause: case cSetSpeed: case cSetTries: case cUserHook: case cMonitorLine: case cDebugLevel: result = NextInt(&labelIndex);
break;
case cIfStr:
for (i = 0; result == 0 && i < (kIfStrParamCount - 1); i++)
result = NextInt(&labelIndex);
break;
case cMatchStr:
if (NextInt(&labelIndex) == 0 && labelIndex > maxMatch) {
result = cclErr_MatchStrIndxErr;
break;
}
result = NextInt(&labelIndex);
SkipBlanks();
PrepStr(SV.strBuf, NULL, NULL, 1);
if (SV.strBuf[0] == 0)
result = cclErr_BadParameter;
break;
case cIfTries:
for (i = 0; result == 0 && i < (kIfTriesParamCount - 1); i++)
result = NextInt(&labelIndex);
break;
case cLogMsg: case cNote:
case cWrite:
SkipBlanks();
PrepStr(SV.strBuf, NULL, NULL, 1);
if( SV.strBuf[0] == 0 )
result = cclErr_BadParameter;
break;
case cSerReset:
for (i = 0; result == 0 && i < kSerResetParamCount; i++)
result = NextInt(&labelIndex);
break;
default:
break;
}
}
if (result == 0) {
if (SV.originateLine == 0) result = cclErr_NoOriginateLabel;
else if (SV.answerLine == 0) result = cclErr_NoAnswerLabel;
else if (SV.hangUpLine == 0) result = cclErr_NoHangUpLabel;
else SV.scriptLine = 0;
while (result == 0 && NextLine()) {
cmd = NextCommand();
switch (cmd) {
case cIfAnswer:
case cIfOriginate:
case cJump:
case cJSR:
result = NextInt(&labelIndex);
if (result == 0) {
labelIndex--;
if ((labelIndex < 0) || (labelIndex >= MAXLABELS))
result = cclErr_BadLabel;
else {
if (SV.labels[ labelIndex ] == 0)
result = cclErr_LabelUndefined;
}
}
break;
case cIfStr:
case cIfTries:
case cMatchStr:
result = NextInt(&labelIndex);
if( result == 0 ) {
result = NextInt(&labelIndex);
if (result == 0) {
labelIndex--;
if ((labelIndex < 0) || (labelIndex >= MAXLABELS))
result = cclErr_BadLabel;
else {
if( SV.labels[ labelIndex ] == 0)
result = cclErr_LabelUndefined;
}
}
}
break;
case cAsk:
result = NextInt(&labelIndex); if (result == 0) {
SkipBlanks();
PrepStr(SV.strBuf, 0, 0, 1);
if (NextInt(&labelIndex) == 0) {
labelIndex--;
if ((labelIndex < 0) || (labelIndex >= MAXLABELS))
result = cclErr_BadLabel;
else {
if (SV.labels[labelIndex] == 0)
result = cclErr_LabelUndefined;
}
}
}
break;
default:
break;
}
}
}
return result;
}
u_int8_t NextLine()
{
u_int8_t *bp;
short i;
SV.scriptLineIndex = 0;
SV.scriptLine++;
if ((SV.scriptLine < 1) || (SV.scriptLine > SV.lineCount))
return 0;
i = SV.indexTable[SV.scriptLine-1];
SV.scriptLinePtr = SV.script + i;
SV.scriptLineSize = 0;
bp = SV.scriptLinePtr;
while ((i++ < SV.scriptSize) &&
(*bp != chrCR) && (*bp != chrNL) &&
(SV.scriptLineSize < 255)) {
bp++;
SV.scriptLineSize++;
}
return 1;
}
int equalstring(u_int8_t *s1, u_int8_t *s2)
{
int l1 = *s1, l2 = *s2;
s1++; s2++;
while (l1 && l2 && (toupper(*s1) == toupper(*s2))) {
l1--; l2--;
s1++; s2++;
}
return ((l1 == 0) && (l2 == 0));
}
int NextCommand()
{
u_int8_t *bp, *cmdStrPtr, cmdFound;
int cmdIndex, result;
char text[256];
result = cComment;
SkipBlanks();
bp = SV.scriptLinePtr + SV.scriptLineIndex;
cmdStrPtr = &SV.strBuf[1];
SV.strBuf[0] = 0;
while ((SV.scriptLineIndex < SV.scriptLineSize)
&& ((*bp != chrSpace) && (*bp != chrHT))) {
SV.scriptLineIndex++;
SV.strBuf[0]++;
*cmdStrPtr++ = *bp++;
}
bcopy(&SV.strBuf[1], &text[0], SV.strBuf[0]);
text[SV.strBuf[0]] = 0;
if (SV.strBuf[0]) {
bp = SV.commands + 2;
cmdIndex = cFirstCmd;
result = cNoCmd;
cmdFound = 0;
while (cmdIndex <= cLastCmd && !cmdFound) {
if (cmdIndex == cComment && SV.strBuf[1] == bp[1]) {
result = cComment;
cmdFound = 1;
}
if( equalstring(&SV.strBuf[0], bp)) {
result = cmdIndex;
cmdFound = 1;
}
else {
bp += *bp + 1;
cmdIndex++;
}
}
}
return result;
}
void PrepStr(u_int8_t *destStr, u_int32_t *isVarString, u_int32_t *varIndex, int varSubstitution)
{
u_int8_t *s, *d, *maskPtr;
u_int16_t srcStrIndex;
u_int8_t done, chDelimiter, escChar, dstStrLen, centerDot = '€';
if ( isVarString != NULL )
*isVarString = 0;
if (SV.scriptLineIndex > SV.scriptLineSize) {
destStr[0] = '\0';
return;
}
srcStrIndex = SV.scriptLineIndex;
s = SV.scriptLinePtr + srcStrIndex;
chDelimiter = 0;
if ((*s == chrDblQuote) || (*s == chrQuote)) {
chDelimiter = *s++;
require_action(srcStrIndex < SV.scriptLineSize, exit, dstStrLen = 0);
srcStrIndex++;
}
d = destStr + 1;
dstStrLen = 0;
done = 0;
while ((srcStrIndex < SV.scriptLineSize) && !done) {
maskPtr = 0;
switch (*s) {
case chrDblQuote:
case chrQuote:
if (chDelimiter == *s)
done = 1;
else {
require_action(srcStrIndex < SV.scriptLineSize, exit, dstStrLen = 0);
srcStrIndex++;
require_action(dstStrLen < UINT8_MAX, exit, dstStrLen = 0);
dstStrLen++;
*d++ = *s++;
}
break;
case chrHT:
case chrSpace:
case chrComma:
case chrSemiColon:
if (!chDelimiter)
done = 1;
else {
require_action(srcStrIndex < SV.scriptLineSize, exit, dstStrLen = 0);
srcStrIndex++;
require_action(dstStrLen < UINT8_MAX, exit, dstStrLen = 0);
dstStrLen++;
*d++ = *s++;
}
break;
case chrBackSlash:
s++;
require_action(dstStrLen < UINT8_MAX, exit, dstStrLen = 0);
dstStrLen++;
if ((*s == chrBackSlash) || (*s == chrCaret)) {
require_action(srcStrIndex < (SV.scriptLineSize - 1), exit, dstStrLen = 0);
srcStrIndex += 2;
*d++ = *s++;
}
else if (*s == 'x') {
require_action(srcStrIndex < (SV.scriptLineSize - 3), exit, dstStrLen = 0);
srcStrIndex += 4;
s++;
escChar = ((*s - ((*s <= '9') ? '0' : ('A' - 10))) * 16);
s++;
escChar += (*s - ((*s <= '9') ? '0' : ('A' - 10)));
s++;
*d++ = escChar;
}
else {
require_action(srcStrIndex < (SV.scriptLineSize - 2), exit, dstStrLen = 0);
srcStrIndex += 3;
escChar = ((*s++ - '0') * 10);
escChar += (*s++ - '0');
*d++ = escChar;
}
break;
case chrCaret:
{
u_int8_t vs, *vsp;
int i;
if (varSubstitution == 0) {
require_action(srcStrIndex < SV.scriptLineSize, exit, dstStrLen = 0);
srcStrIndex++;
require_action(dstStrLen < UINT8_MAX, exit, dstStrLen = 0);
dstStrLen++;
*d++ = *s++;
break;
}
s++;
require_action(srcStrIndex < (SV.scriptLineSize - 1), exit, dstStrLen = 0);
srcStrIndex += 2;
switch (vs = *s++) {
case '*': vs = vsAsk; break;
case 'u': case 'U':
vs = vsUserName;
break;
case 'p': case 'P': vs = vsPassWord; break;
default: {
vs -= '0';
if (*s >= '0' && *s <= '9') {
require_action(srcStrIndex < SV.scriptLineSize, exit, dstStrLen = 0);
srcStrIndex++;
vs = 10 * vs + *s++ - '0';
}
break;
}
}
if (isVarString)
*isVarString = 1;
if (varIndex)
*varIndex = vs;
if (vsp = GetVarString(vs)) {
if (SV.logMaskOn && vs == (SV.maskStringId - 1) && SV.maskStart) {
maskPtr = d + SV.maskStart - 1;
}
for (i = 1; i <= *vsp; i++, dstStrLen++) {
require_action(dstStrLen < UINT8_MAX, exit, dstStrLen = 0);
*d++ = vsp[i];
}
if (maskPtr) {
for (i = SV.maskStop - SV.maskStart; i >= 0; i--)
*maskPtr++ = centerDot;
}
}
break;
}
default:
require_action(srcStrIndex < SV.scriptLineSize, exit, dstStrLen = 0);
srcStrIndex++;
require_action(dstStrLen < UINT8_MAX, exit, dstStrLen = 0);
dstStrLen++;
*d++ = *s++;
break;
}
}
exit:
*destStr = dstStrLen; SV.scriptLineIndex = srcStrIndex + 1;
}
void varSubstitution(u_int8_t *src, u_int8_t *dst, int dstmaxlen)
{
u_int8_t *s = src + 1, *d = dst + 1;
u_int16_t srclen = src[0];
u_int8_t len = 0, dstlen = 0;
while ((len < srclen) && (dstlen < dstmaxlen)) {
switch (*s) {
case chrCaret:
{
u_int8_t vs, *vsp;
int i;
s++;
len += 2;
switch (vs = *s++) {
case '*': vs = vsAsk; break;
case 'u': case 'U': vs = vsUserName; break;
case 'p': case 'P': vs = vsPassWord; break;
default: {
vs -= '0';
if (*s >= '0' && *s <= '9') {
len++;
vs = 10 * vs + *s++ - '0';
}
break;
}
}
if (vsp = GetVarString(vs)) {
for (i = 1; (i <= *vsp) && (dstlen < dstmaxlen); i++, dstlen++)
*d++ = vsp[i];
}
break;
}
default:
len++;
dstlen++;
*d++ = *s++;
break;
}
}
*dst = dstlen; }
void SkipBlanks()
{
u_int8_t *s;
s = SV.scriptLinePtr + SV.scriptLineIndex;
while ((SV.scriptLineIndex < SV.scriptLineSize)
&& ((*s == chrSpace) || (*s == chrHT))) {
s++;
SV.scriptLineIndex++;
}
}
int NextInt(u_int32_t *theIntPtr)
{
int sign;
u_int8_t *intStrPtr, intStrNdx, intStrLen;
u_int8_t intStr[256];
u_int32_t isVarString = 0;
SkipBlanks();
PrepStr( intStr, &isVarString, NULL, 1);
*theIntPtr = 0;
sign = 1;
intStrLen = *intStr;
intStrPtr = intStr + 1;
if( intStrLen == 0 )
return cclErr_BadParameter;
if (isVarString && (intStrLen == 1) && *intStrPtr == ' ')
*intStrPtr = '0';
intStrNdx = 1;
if (*intStrPtr == '-') {
sign = -1;
intStrNdx++;
intStrPtr++;
}
if (intStrNdx > intStrLen)
return cclErr_BadParameter;
while ((intStrNdx++ <= intStrLen) && (*intStrPtr >= '0') && (*intStrPtr <= '9'))
*theIntPtr = (*theIntPtr * 10) + (*intStrPtr++ - '0');
if (intStrPtr == (intStr + 1)) return cclErr_BadParameter;
*theIntPtr *= sign; return 0;
}
void RunScript()
{
u_int8_t running, cmd;
int code, result = 0;
u_int32_t i, lval, jumpLabel;
if (SV.scriptLine == 0) { SV.ctlFlags &= ~cclPlaying; SV.theAbortErr = cclErr_EndOfScriptErr; terminate(cclErr_EndOfScriptErr); return;
}
SV.ctlFlags |= cclPlaying; running = 1; while (running) { if (!NextLine()) {
SV.ctlFlags &= ~cclPlaying; SV.theAbortErr = cclErr_EndOfScriptErr; terminate(cclErr_EndOfScriptErr); return;
}
cmd = NextCommand(); switch (cmd) {
case cAsk:
running = !Ask(); break;
case cChrDelay:
NextInt( &i ); SV.chrDelayValue = i * 100;
break;
case cCommunicatingAt:
CommunicatingAt(); break;
case cDecTries:
SV.loopCounter--; break;
case cDTRClear:
DTRCommand(DTR_CLEAR); break;
case cDTRSet:
DTRCommand(DTR_SET); break;
case cExit:
running = 0;
SV.ctlFlags &= ~cclPlaying;
NextInt((u_int32_t*)&code); SV.theAbortErr = code;
Note();
terminate(code); break;
case cFlush:
Flush();
break;
case cHSReset:
HSReset(); break;
case cIfAnswer:
if (SV.ctlFlags & cclAnswerMode) {
NextInt(&i);
SV.scriptLine = SV.labels[i - 1];
}
break;
case cIfOriginate:
if (SV.ctlFlags & cclOriginateMode) {
NextInt(&i);
SV.scriptLine = SV.labels[i - 1];
}
break;
case cIfStr:
jumpLabel = IfStr();
if (jumpLabel > 0)
SV.scriptLine = jumpLabel;
break;
case cIfTries:
NextInt(&i);
if (SV.loopCounter >= i) {
NextInt(&i);
SV.scriptLine = SV.labels[i - 1];
}
break;
case cIncTries:
SV.loopCounter++; break;
case cJump:
NextInt(&i);
SV.scriptLine = SV.labels[i - 1];
break;
case cJSR:
if (SV.topOfStack == 0) {
running = 0;
SV.ctlFlags &= ~cclPlaying;
SV.theAbortErr = cclErr_SubroutineOverFlow; terminate(cclErr_SubroutineOverFlow);
}
else {
SV.stack[--SV.topOfStack] = SV.scriptLine; NextInt(&i);
SV.scriptLine = SV.labels[i - 1];
}
break;
case cLogMsg: case cNote:
Note(); break;
case cMatchClr:
MatchClr(); break;
case cMatchRead:
SV.ctlFlags |= cclMatchPending; for(i = 0; i < maxMatch; i++) { SV.matchStr[i].matchStrIndex = 0;
SV.matchStr[i].inVarStr = 0;
}
NextInt(&i); if( i > 0 ) {
running = 0; ScheduleTimer(kMatchReadTimer, i * 100);
StartRead();
}
break;
case cMatchStr:
result = MatchStr(); if (result) { running = 0;
SV.ctlFlags &= ~cclPlaying;
SV.theAbortErr = result; terminate(result);
}
break;
case cPause:
NextInt(&i);
if (i > 0) {
lval = SV.pauseTimer = i;
lval *= 100; usleep(lval * 1000);
}
break;
case cReturn:
if (SV.topOfStack == cclNestingLimit) {
running = 0;
SV.ctlFlags &= ~cclPlaying;
SV.theAbortErr = cclErr_SubroutineOverFlow; terminate(cclErr_SubroutineOverFlow);
}
else
SV.scriptLine = SV.stack[SV.topOfStack++];
break;
case cLBreak:
Break(LONGBREAK);
break;
case cSBreak:
Break(SHORTBREAK);
break;
case cMonitorLine:
MonitorLine();
break;
case cDebugLevel:
DebugLevel();
break;
case cSerReset:
SerReset();
break;
case cSetSpeed:
SetSpeed();
break;
case cSetTries:
NextInt(&i);
SV.loopCounter = i;
break;
case cUserHook:
UserHook(); break;
case cWrite:
running = Write();
break;
default: {
break;
}
}
}
}
int MatchStr()
{
u_int32_t matchIndex, i;
TPMatchStrInfo matchInfo;
int result;
result = NextInt(&matchIndex); if (result == 0) {
matchIndex--;
if ((matchIndex < 0) || (matchIndex >= maxMatch)) { return cclErr_MatchStrIndxErr;
}
matchInfo = &SV.matchStr[matchIndex];
result = NextInt(&i);
if (result == 0) {
matchInfo->matchLine = SV.labels[i - 1];
SkipBlanks();
if ((SV.scriptLinePtr[SV.scriptLineIndex] == chrDblQuote)
|| (SV.scriptLinePtr[SV.scriptLineIndex] == chrQuote)) {
matchInfo->delimiterChar = SV.scriptLinePtr[SV.scriptLineIndex];
SV.scriptLineIndex++;
}
else
matchInfo->delimiterChar = 0;
matchInfo->matchStr = &SV.scriptLinePtr[SV.scriptLineIndex];
}
}
return result;
}
void MatchClr()
{
int matchIndex;
TPMatchStrInfo matchInfo;
for (matchIndex = 0; matchIndex < maxMatch; matchIndex++) {
matchInfo = &SV.matchStr[matchIndex];
matchInfo->matchStr = 0;
matchInfo->matchStrIndex = 0;
matchInfo->delimiterChar = 0;
matchInfo->varStr = 0;
matchInfo->inVarStr = 0;
matchInfo->varStrIndex = 0;
matchInfo->matchLine = 0;
}
}
int MatchFind(u_int8_t newChar)
{
int i, matchFound;
TPMatchStrInfo matchInfo = NULL;
char text[256];
u_int8_t matchStrChar, c;
matchFound = 0; newChar &= 0x7f; for (i = 0; i < maxMatch && !matchFound; i++) {
matchInfo = &SV.matchStr[i];
if (matchInfo->matchStr) { if (matchInfo->inVarStr) {
matchStrChar = matchInfo->varStr[matchInfo->varStrIndex];
matchInfo->varStrIndex++;
if (matchStrChar == chrBackSlash) {
matchStrChar = matchInfo->varStr[matchInfo->varStrIndex];
matchInfo->varStrIndex++;
if ((matchStrChar != chrBackSlash)
&& (matchStrChar != chrCaret)) {
if (matchStrChar == 'x') {
c = matchInfo->matchStr[matchInfo->varStrIndex++];
matchStrChar = ((c - ((c <= '9') ? '0' : ('A' - 10))) * 16);
c = matchInfo->matchStr[matchInfo->varStrIndex++];
matchStrChar += (c - ((c <= '9') ? '0' : ('A' - 10)));
}
else {
matchStrChar = (matchStrChar - 0x30) * 10;
matchStrChar += matchInfo->varStr[matchInfo->varStrIndex] - 0x30;
matchInfo->varStrIndex++;
}
}
}
if (matchInfo->varStrIndex == (matchInfo->varStrSize + 1))
matchInfo->inVarStr = 0;
}
else {
matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex];
matchInfo->matchStrIndex++; if (matchStrChar == chrBackSlash) {
matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex];
matchInfo->matchStrIndex++; if ((matchStrChar != chrBackSlash)
&& (matchStrChar != chrCaret)) {
if (matchStrChar == 'x') {
c = matchInfo->matchStr[matchInfo->matchStrIndex++];
matchStrChar = ((c - ((c <= '9') ? '0' : ('A' - 10))) * 16);
c = matchInfo->matchStr[matchInfo->matchStrIndex++];
matchStrChar += (c - ((c <= '9') ? '0' : ('A' - 10)));
}
else {
matchStrChar = (matchStrChar - 0x30) * 10;
matchStrChar += matchInfo->matchStr[matchInfo->matchStrIndex] - 0x30;
matchInfo->matchStrIndex++;
}
}
}
else if (matchStrChar == chrCaret) {
u_int8_t vs;
matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex];
matchInfo->matchStrIndex++; if (matchStrChar == '*')
vs = vsAsk;
else if (matchStrChar == 'u' || matchStrChar == 'U')
vs = vsUserName;
else if (matchStrChar == 'p' || matchStrChar == 'P')
vs = vsPassWord;
else {
vs = matchStrChar - '0';
matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex];
if (matchStrChar >= '0' && matchStrChar <= '9') {
matchInfo->matchStrIndex++; vs = 10 * vs + matchStrChar - '0';
}
}
matchInfo->varStr = GetVarString(vs);
if (matchInfo->varStr) {
matchStrChar = matchInfo->varStr[1];
matchInfo->varStrIndex = 2;
matchInfo->inVarStr = 1;
matchInfo->varStrSize = matchInfo->varStr[0];
if( (matchInfo->varStrSize + 1) == matchInfo->varStrIndex)
matchInfo->inVarStr = 0;
}
else {
matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex];
matchInfo->matchStrIndex++;
}
}
}
if (newChar == matchStrChar) { if (!matchInfo->inVarStr)
switch (matchInfo->matchStr[ matchInfo->matchStrIndex ]) {
case chrDblQuote:
case chrQuote:
if (matchInfo->delimiterChar)
matchFound = i + 1;
break;
case chrHT:
case chrSpace:
case chrComma:
case chrSemiColon:
if (!matchInfo->delimiterChar)
matchFound = i + 1;
break;
case chrCR:
case chrNL:
matchFound = i + 1;
break;
}
}
else { matchInfo->matchStrIndex = 0;
matchInfo->inVarStr = 0;
matchInfo->varStr = 0;
}
}
}
if (matchFound) {
int x;
if (matchInfo->inVarStr) {
for (x = 0; x < matchInfo->varStrIndex; x++)
VerboseBuffer[x+1] = matchInfo->varStr[x];
VerboseBuffer[0] = x;
}
else {
for (x = 0; x < matchInfo->matchStrIndex; x++)
VerboseBuffer[x+1] = matchInfo->matchStr[x];
VerboseBuffer[0] = x;
}
bcopy(&VerboseBuffer[1], &text[0], VerboseBuffer[0]);
text[VerboseBuffer[0]] = 0;
if (verbose) {
sLog("CCLMatched : %s", text);
}
}
return matchFound;
}
void Break(u_int32_t len)
{
tcsendbreak(outfd, len);
}
#define IsValidBaudRate(a) \
( ((a) == 300) || \
((a) == 600) || \
((a) == 1200) || \
((a) == 1800) || \
((a) == 2400) || \
((a) == 3600) || \
((a) == 4800) || \
((a) == 7200) || \
((a) == 9600) || \
((a) == 14400) || \
((a) == 19200) || \
((a) == 28800) || \
((a) == 38400) || \
((a) == 57600) || \
((a) == 115200) || \
((a) == 230400) || \
((a) == 460800) || \
((a) == 921600))
void SerReset(void)
{
struct termios tios;
u_int32_t temp;
if (tcgetattr(infd, &tios) < 0)
return;
NextInt(&temp);
SV.serialSpeed = IsValidBaudRate(temp) ? temp : 9600;
cfsetispeed(&tios, SV.serialSpeed);
cfsetospeed(&tios, SV.serialSpeed);
NextInt(&temp);
switch (temp) {
case 1:
tios.c_cflag |= PARENB + PARODD; break;
case 2:
tios.c_cflag |= PARENB;
tios.c_cflag &= ~PARODD; break;
default:
tios.c_cflag &= ~(PARENB + PARODD); break;
}
tios.c_cflag &= ~CSIZE; NextInt(&temp);
switch (temp) {
case 5:
tios.c_cflag |= CS5; break;
case 6:
tios.c_cflag |= CS6; break;
case 7:
tios.c_cflag |= CS7; break;
default:
tios.c_cflag |= CS8; }
NextInt(&temp);
switch (temp) {
case 2:
tios.c_cflag |= CSTOPB; break;
default:
tios.c_cflag &= ~CSTOPB; break;
}
tcsetattr(infd, TCSAFLUSH, &tios);
tcsetattr(outfd, TCSAFLUSH, &tios);
}
void MonitorLine()
{
struct termios tios;
u_int32_t temp;
if (tcgetattr(infd, &tios) < 0)
return;
NextInt(&temp);
if (temp)
tios.c_cflag &= ~CLOCAL;
else
tios.c_cflag |= CLOCAL;
tcsetattr(infd, TCSAFLUSH, &tios);
tcsetattr(outfd, TCSAFLUSH, &tios);
}
void DebugLevel()
{
u_int32_t temp;
NextInt(&temp);
debuglevel = temp;
}
void SetSpeed(void)
{
u_int32_t temp;
struct termios tios;
NextInt(&temp);
SV.serialSpeed = IsValidBaudRate(temp) ? temp : 2400;
if (tcgetattr(infd, &tios) >= 0) {
cfsetispeed(&tios, SV.serialSpeed);
cfsetospeed(&tios, SV.serialSpeed);
tcsetattr(infd, TCSAFLUSH, &tios);
tcsetattr(outfd, TCSAFLUSH, &tios);
}
}
CFStringRef copyUserLocalizedString(CFBundleRef bundle,
CFStringRef key, CFStringRef value, CFArrayRef userLanguages)
{
CFStringRef result = NULL, errStr= NULL;
CFDictionaryRef stringTable;
CFDataRef tableData;
SInt32 errCode;
CFURLRef tableURL;
CFArrayRef locArray, prefArray;
if (userLanguages == NULL)
return CFBundleCopyLocalizedString(bundle, key, value, NULL);
if (key == NULL)
return (value ? CFRetain(value) : CFRetain(CFSTR("")));
locArray = CFBundleCopyBundleLocalizations(bundle);
if (locArray) {
prefArray = CFBundleCopyLocalizationsForPreferences(locArray, userLanguages);
if (prefArray) {
if (CFArrayGetCount(prefArray)) {
tableURL = CFBundleCopyResourceURLForLocalization(bundle, CFSTR("Localizable"), CFSTR("strings"), NULL,
CFArrayGetValueAtIndex(prefArray, 0));
if (tableURL) {
if (CFURLCreateDataAndPropertiesFromResource(NULL, tableURL, &tableData, NULL, NULL, &errCode)) {
stringTable = CFPropertyListCreateFromXMLData(NULL, tableData, kCFPropertyListImmutable, &errStr);
if (errStr)
CFRelease(errStr);
if (stringTable) {
result = CFDictionaryGetValue(stringTable, key);
if (result)
CFRetain(result);
CFRelease(stringTable);
}
CFRelease(tableData);
}
CFRelease(tableURL);
}
}
CFRelease(prefArray);
}
CFRelease(locArray);
}
if (result == NULL)
result = (value && !CFEqual(value, CFSTR(""))) ? CFRetain(value) : CFRetain(key);
return result;
}
bool localizeStringWithBundle(u_char* inString, u_char* outString, CFIndex outSize, CFURLRef curURL)
{
bool retVal= false;
if(curURL && inString && outString)
{
CFStringRef ref, loggedInUser, msg;
CFPropertyListRef langRef;
CFBundleRef bdl;
loggedInUser = SCDynamicStoreCopyConsoleUser(NULL, 0, 0);
if (loggedInUser)
{
CFPreferencesSynchronize(kCFPreferencesAnyApplication, loggedInUser, kCFPreferencesAnyHost);
langRef = CFPreferencesCopyValue(CFSTR("AppleLanguages"), kCFPreferencesAnyApplication,
loggedInUser, kCFPreferencesAnyHost);
if (langRef)
{
ref = CFStringCreateWithPascalString(NULL, inString, kCFStringEncodingUTF8);
if (ref)
{
bdl = CFBundleCreate(0, curURL);
if (bdl)
{
msg = copyUserLocalizedString(bdl, ref, ref, langRef);
if (msg)
{
retVal= CFStringGetPascalString(msg, outString, outSize, kCFStringEncodingUTF8);
CFRelease(msg);
}
CFRelease(bdl);
}
CFRelease(ref);
}
CFRelease(langRef);
}
CFRelease(loggedInUser);
}
}
return retVal;
}
void localizeString(u_char* inString, u_char* outString, CFIndex outSize)
{
bool success = true;
outString[0]= 0;
if(!localizeStringWithBundle(inString, outString, outSize, cclBundleURL))
{
if(!localizeStringWithBundle(inString, outString, outSize, appBundleURL))
{
success = localizeStringWithBundle(inString, outString, outSize, localBundleURL);
}
}
if (!success)
bcopy(inString, outString, inString[0]+1);
}
#pragma mark -
void Note()
{
u_char text[256];
u_char localText[256];
u_int32_t msgDestination, msgLevel = 0; CFStringRef ref;
SkipBlanks();
PrepStr(text, 0, 0, 0);
localizeString(text, localText, 256);
varSubstitution(localText, SV.strBuf, sizeof(SV.strBuf));
NextInt(&msgLevel); switch (msgLevel) {
case 1:
msgDestination = kUserMsgFLog;
break;
case 2:
default:
msgDestination = kUserMsgFStatus;
break;
case 3:
msgDestination = kUserMsgFLog | kUserMsgFStatus;
break;
}
bcopy(&SV.strBuf[1], &localText[0], SV.strBuf[0]);
localText[SV.strBuf[0]] = '\0';
if (serviceID && (msgDestination & 2)) {
ref = CFStringCreateWithCString(NULL, (char*) localText, kCFStringEncodingUTF8);
if (ref) {
publish_entry(serviceID, kSCPropNetModemNote, ref);
CFRelease(ref);
}
}
if (msgDestination & 1) {
sLog("%s", localText);
}
}
u_int8_t Write()
{
u_int32_t isVarString;
u_int16_t i, j;
u_int32_t varIndex;
char text[256];
SkipBlanks();
PrepStr(SV.strBuf, &isVarString, &varIndex, 1);
if (isVarString && (varIndex == vsPassWord || ((varIndex == vsAsk) && LastAskedMasked))) {
VerboseBuffer[0] = SV.strBuf[0];
for (i = 1; i <= SV.strBuf[0]; i++)
VerboseBuffer[i] = '¥';
}
else {
for (i = 1, j = 1; i <= SV.strBuf[0] && j < 256; i++) {
u_int8_t c = SV.strBuf[i];
if (c < 0x20) {
VerboseBuffer[j++] = '\\';
VerboseBuffer[j++] = '0' + c / 10;
VerboseBuffer[j++] = '0' + c % 10;
}
else {
VerboseBuffer[j++] = c;
}
}
VerboseBuffer[0] = j - 1;
}
bcopy(&VerboseBuffer[1], &text[0], VerboseBuffer[0]);
text[VerboseBuffer[0]] = 0;
if (verbose) {
sLog("CCLWrite : %s", text);
}
SV.writeBufIndex = 1;
if (SV.chrDelayValue) {
WriteContinue();
ScheduleTimer(kCharDelayTimer, SV.chrDelayValue);
return 0;
}
else {
write(outfd, &SV.strBuf[1], SV.strBuf[0]);
if(signalerror)
terminate(signalerror);
return 1;
}
}
void WriteContinue()
{
write(outfd, &SV.strBuf[SV.writeBufIndex], 1);
if(signalerror)
terminate(signalerror);
if (SV.writeBufIndex < SV.strBuf[0] ) SV.writeBufIndex++;
return;
}
void TimerExpired(long type)
{
if (SV.ctlFlags & cclPlaying) {
switch (type) {
case kMatchReadTimer:
StopRead();
RunScript(); break;
case kCharDelayTimer:
if (SV.writeBufIndex == SV.strBuf[0] ) { WriteContinue(); RunScript(); }
else {
if (SV.writeBufIndex < SV.strBuf[0]) { WriteContinue(); ScheduleTimer(kCharDelayTimer, SV.chrDelayValue);
}
}
break;
}
}
}
u_int8_t Ask()
{
u_int32_t maskflag, flags = 0, label;
CFStringRef ref, resp;
CFUserNotificationRef alert;
CFOptionFlags alertflags;
CFMutableDictionaryRef dict;
SInt32 error;
CFMutableArrayRef array;
u_char text[256];
u_char localText[256];
if (alertname[0] == 0)
return 0;
#define kCmdAskAllowCancel 1
#define kCmdAskAllowEntry 2
#define kCmdAskMaskEntry 4
if (NextInt(&maskflag))
maskflag = 0;
SkipBlanks();
PrepStr(text, 0, 0, 0);
localizeString(text, localText, 256);
varSubstitution(localText, SV.strBuf, sizeof(SV.strBuf));
if (NextInt(&label)) {
SV.askLabel = 0;
flags &= ~kCmdAskAllowCancel;
}
else {
SV.askLabel = (u_int16_t) label;
flags |= kCmdAskAllowCancel;
}
flags |= kCmdAskAllowEntry;
switch (maskflag) {
case 0:
flags &= ~kCmdAskMaskEntry;
break;
case 1:
flags |= kCmdAskMaskEntry;
LastAskedMasked = maskflag;
break;
case 2:
flags &= ~kCmdAskAllowEntry;
break;
}
ref = CFStringCreateWithPascalString(NULL, SV.strBuf, kCFStringEncodingUTF8);
if (ref) {
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict) {
alertflags = 0;
if (alertNameRef)
CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertNameRef);
if (flags & kCmdAskAllowEntry) {
array = CFArrayCreateMutable(NULL, 0, NULL);
if (array) {
CFArrayAppendValue(array, ref);
CFDictionaryAddValue(dict, kCFUserNotificationTextFieldTitlesKey, array);
CFRelease(array);
if (flags & kCmdAskMaskEntry)
alertflags = CFUserNotificationSecureTextField(0);
}
}
else
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, ref);
if (cancelNameRef && (flags & kCmdAskAllowCancel))
CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, cancelNameRef);
if (iconURL)
CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL);
if (localBundleURL)
CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localBundleURL);
alert = CFUserNotificationCreate(NULL, 0, alertflags, &error, dict);
if (alert) {
CFUserNotificationReceiveResponse(alert, 0, &alertflags);
if ((alertflags & 3) == 1) {
if (SV.askLabel) {
SV.scriptLine = SV.labels[SV.askLabel - 1];
SV.askLabel = 0;
}
}
else {
if (flags & kCmdAskAllowEntry) {
resp = CFUserNotificationGetResponseValue(alert, kCFUserNotificationTextFieldValuesKey, 0);
if (resp) {
CFStringGetPascalString(resp, text, sizeof(text), kCFStringEncodingUTF8);
SetVarString(vsAsk, text);
}
}
}
CFRelease(alert);
}
CFRelease(dict);
}
CFRelease(ref);
}
return 0;
}
void CommunicatingAt()
{
u_int32_t speed;
CFNumberRef num;
NextInt(&speed); sLog("Communicating at %d bps.", speed);
if (serviceID) {
num = CFNumberCreate(NULL, kCFNumberIntType, &speed);
if (num) {
publish_entry(serviceID, kSCPropNetModemConnectSpeed, num);
CFRelease(num);
}
}
}
void UserHook()
{
u_int32_t event;
NextInt( &event );
#if 0
UInt8 len = 0;
TStreamMessage *mp;
len = sizeof( ARA_notify_msg ) + sizeof( UInt32 );
mp = new ( len ) TStreamMessage;
if( !mp )
{
DebugStop("\p TCCLScript::UserHook: no memory for message!");
return; }
mp->Reset( len );
mp->SetType( M_PCPROTO );
ARA_notify_msg* PrvMsg = (ARA_notify_msg*) mp->GetDataPointer();
PrvMsg->PRIM_type = kARAPrvMsg; PrvMsg->CODE_type = kNotifyMsg; PrvMsg->MODL_dst = kAnyModuleId; PrvMsg->MODL_src = kScriptModuleId; PrvMsg->MSG_type = kNotifyUserHook;
PrvMsg->MSG_error = 0;
PrvMsg->MSG_flags = 0;
*((UInt32*) (PrvMsg + 1)) = event;
fScriptMod -> ScriptSendDataUp( mp ); #endif
}
void ScheduleTimer(long type, u_int32_t milliseconds)
{
if (milliseconds)
timeout((void*)TimerExpired, (void*)type, milliseconds);
else
untimeout((void*)TimerExpired, (void*)type);
}
int IfStr()
{
u_int8_t noMatch = 0, *src;
u_int32_t i = 0, strIndex, labelIndex;
NextInt( &strIndex );
src = GetVarString(strIndex);
NextInt(&i); labelIndex = SV.labels[i - 1]; SkipBlanks(); PrepStr(SV.strBuf, 0, 0, 1);
if (equalstring(&SV.strBuf[0], src)) { return labelIndex;
}
else {
return noMatch;
}
}
void ReceiveMatchData(u_int8_t nextChar)
{
int32_t matchIndex;
if (debuglevel & 0x1) {
if (usestderr) {
fprintf(stderr, "%c", nextChar);
}
}
if ((matchIndex = MatchFind(nextChar)) != 0) {
SV.scriptLine = SV.matchStr[--matchIndex].matchLine;
SV.ctlFlags &= ~cclMatchPending;
ScheduleTimer(kMatchReadTimer, 0);
StopRead();
RunScript();
}
}
void Flush()
{
tcflush(infd, TCIFLUSH);
}
void HSReset()
{
u_int32_t outputXON_OFF, outputCTS, XonChar, XoffChar, inputXON_OFF, inputDTR;
struct termios tios;
if (tcgetattr(outfd, &tios) < 0)
return;
NextInt(&outputXON_OFF);
if (outputXON_OFF)
tios.c_iflag |= IXON;
else
tios.c_iflag &= ~IXON;
NextInt(&outputCTS);
if (outputCTS)
tios.c_cflag |= CCTS_OFLOW;
else
tios.c_cflag &= ~CCTS_OFLOW;
NextInt(&XonChar);
NextInt(&XoffChar);
tios.c_cc[VSTOP] = XoffChar;
tios.c_cc[VSTART] = XonChar;
NextInt(&inputXON_OFF);
if (inputXON_OFF)
tios.c_iflag |= IXOFF;
else
tios.c_iflag &= ~IXOFF;
NextInt(&inputDTR);
if (inputDTR)
tios.c_cflag |= CRTS_IFLOW;
else
tios.c_cflag &= ~CRTS_IFLOW;
tcsetattr(infd, TCSAFLUSH, &tios);
tcsetattr(outfd, TCSAFLUSH, &tios);
}
void DTRCommand(short DTRCode)
{
ioctl(infd, DTRCode == DTR_SET ? TIOCSDTR : TIOCCDTR);
}
static char* cclErrStrings[] =
{
"internal error used to abort match read", "Bad parameter given to the engine", "Duplicate label", "Label undefined", "Subroutine overflow", "No memory...", "CCL error base", "There is at least one script open", "Script Canceled", "Script contains too many lines", "Script contains too many characters", "CCL has not been initialized", "Cancel in progress.", "Play command already in progress.", "Exit with no error.", "Label out of range.", "Bad command.", "End of script reached, expecting Exit.", "Match string index is out of bounds.", "Modem error, modem not responding.", "No dial tone.", "No carrier.", "Line busy.", "No answer.", "No @ORIGINATE label", "No @ANSWER label", "No @HANGUP label", "Can't connect because number is empty.", "Incorrect script for the modem." };
void terminate(int exitError)
{
#if 0
CFNumberRef num;
#endif
#if 1
if (verbose || exitError != 0) {
char *errStr = NULL;
if (exitError == cclErr_DuplicateLabel) {
if (SV.strBuf[0]) {
SV.strBuf[SV.strBuf[0]+1] = '\0';
errStr = (char*)(&SV.strBuf[0] + 1);
} else {
errStr = "<no custom error>";
}
} else if (exitError <= cclErr_AbortMatchRead &&
exitError >= cclErr_BadScriptErr)
errStr = cclErrStrings[cclErr_AbortMatchRead - exitError];
if (errStr)
sLog("CCLExit: %d (%s)", exitError, errStr);
else
sLog("CCLExit: %d", exitError);
}
#endif
#if 0
if (serviceID && (exitError || (enginemode != mode_disconnect))) {
num = CFNumberCreate(NULL, kCFNumberIntType, &exitError);
if (num) {
publish_entry(serviceID, kSCPropNetPPPLastCause, num);
CFRelease(num);
}
}
#endif
if (serviceID) {
unpublish_entry(serviceID, kSCPropNetModemNote);
}
close(infd);
close(outfd);
exit(exitError);
}
void timeout(void (*func)(void *), void *arg, u_long time)
{
struct callout *newp, *p, **pp;
if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL)
terminate(cclErr_NoMemErr);
newp->c_arg = arg;
newp->c_func = func;
gettimeofday(&timenow, NULL);
newp->c_time.tv_sec = timenow.tv_sec + time/1000;
newp->c_time.tv_usec = timenow.tv_usec + ((time%1000)*1000);
if (newp->c_time.tv_usec >= 1000000) {
newp->c_time.tv_sec++;
newp->c_time.tv_usec -= 1000000;
}
for (pp = &callout; (p = *pp); pp = &p->c_next)
if (newp->c_time.tv_sec < p->c_time.tv_sec
|| (newp->c_time.tv_sec == p->c_time.tv_sec
&& newp->c_time.tv_usec < p->c_time.tv_usec))
break;
newp->c_next = p;
*pp = newp;
}
void untimeout(void (*func)(void *), void *arg)
{
struct callout **copp, *freep;
for (copp = &callout; (freep = *copp); copp = &freep->c_next)
if (freep->c_func == func && freep->c_arg == arg) {
*copp = freep->c_next;
free((char *) freep);
break;
}
}
void calltimeout()
{
struct callout *p;
while (callout != NULL) {
p = callout;
if (gettimeofday(&timenow, NULL) < 0)
terminate(cclErr_NoMemErr);
if (!(p->c_time.tv_sec < timenow.tv_sec
|| (p->c_time.tv_sec == timenow.tv_sec
&& p->c_time.tv_usec <= timenow.tv_usec)))
break;
callout = p->c_next;
(*p->c_func)(p->c_arg);
free((char *) p);
}
}
struct timeval *timeleft(struct timeval *tvp)
{
if (callout == NULL)
return NULL;
gettimeofday(&timenow, NULL);
tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
if (tvp->tv_usec < 0) {
tvp->tv_usec += 1000000;
tvp->tv_sec -= 1;
}
if (tvp->tv_sec < 0)
tvp->tv_sec = tvp->tv_usec = 0;
return tvp;
}
int publish_entry(u_char *serviceid, CFStringRef entry, CFTypeRef value)
{
CFMutableDictionaryRef dict;
CFStringRef key;
SCDynamicStoreRef cfgCache;
CFPropertyListRef ref;
int ret = 0;
if (cfgCache = SCDynamicStoreCreate(0, CFSTR("CCLEngine"), 0, 0)) {
if (key = SCDynamicStoreKeyCreate(0, CFSTR("%@/%@/%@/%s/%@"),
kSCDynamicStoreDomainState, kSCCompNetwork, kSCCompService, serviceid, kSCEntNetModem)) {
if (ref = SCDynamicStoreCopyValue(cfgCache, key)) {
dict = CFDictionaryCreateMutableCopy(0, 0, ref);
CFRelease(ref);
}
else
dict = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (dict) {
CFDictionarySetValue(dict, entry, value);
ret = SCDynamicStoreSetValue(cfgCache, key, dict);
CFRelease(dict);
}
CFRelease(key);
}
CFRelease(cfgCache);
}
return ret;
}
int unpublish_entry(u_char *serviceid, CFStringRef entry)
{
CFPropertyListRef ref;
CFMutableDictionaryRef dict;
CFStringRef key;
SCDynamicStoreRef cfgCache;
int ret = 0;
if (cfgCache = SCDynamicStoreCreate(0, CFSTR("CCLEngine"), 0, 0)) {
if (key = SCDynamicStoreKeyCreate(0, CFSTR("%@/%@/%@/%s/%@"),
kSCDynamicStoreDomainState, kSCCompNetwork, kSCCompService, serviceid, kSCEntNetModem)) {
if (ref = SCDynamicStoreCopyValue(cfgCache, key)) {
if (dict = CFDictionaryCreateMutableCopy(0, 0, ref)) {
CFDictionaryRemoveValue(dict, entry);
ret = SCDynamicStoreSetValue(cfgCache, key, dict);
CFRelease(dict);
}
CFRelease(ref);
}
CFRelease(key);
}
CFRelease(cfgCache);
}
return ret;
}