1/* 2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25/* -------------------------------------------------------------------------- 26Includes 27-------------------------------------------------------------------------- */ 28 29#include <stdio.h> 30#include <ctype.h> 31#include <time.h> 32#include <fcntl.h> 33#include <signal.h> 34#include <errno.h> 35#include <string.h> 36#include <stdlib.h> 37#include <syslog.h> 38#include <sys/time.h> 39#include <sys/stat.h> 40#include <sys/types.h> 41#include <unistd.h> 42#include <sys/un.h> 43#include <sys/socket.h> 44#include <stdarg.h> 45#include <termios.h> 46#include <sys/ioctl.h> 47#include <sys/param.h> 48#include <CoreFoundation/CoreFoundation.h> 49#include <IOKit/IOCFUnserialize.h> 50#include <SystemConfiguration/SystemConfiguration.h> 51 52#include "CCLArgFunctions.h" 53#include "cclkeys.h" 54 55// import System Configuration schema for keys that are prefs 56#include <SystemConfiguration/SCSchemaDefinitions.h> 57 58#include "CCLEngine.h" 59#include "CCLEngine_defs.h" 60 61/* -------------------------------------------------------------------------- 62Defines 63-------------------------------------------------------------------------- */ 64 65#define min(a,b) (((a)<=(b))?(a):(b)) 66#define kNormalDialMode 0 67#define kBlindDialMode 1 // CCL term; SC uses is "IgnoreDialTone" 68#define kManualDialMode 2 // user does the dialing 69 70#define DIR_MODEMS_USER "/Library/Modem Scripts" 71#define DIR_MODEMS_SYS "/System/Library/Modem Scripts" 72 73 74enum { 75 kUserMsgFLog = 1, 76 kUserMsgFStatus = 2 77}; 78 79#define infd STDIN_FILENO 80#define outfd STDOUT_FILENO 81#define PPP_ARG_FD 3 82 83enum 84{ 85 kInvalidTimer = 0, 86 kMatchReadTimer, 87 kPauseTimer, 88 kCharDelayTimer, 89 kBreakTimer 90}; 91 92enum enginemode { 93 mode_connect = 0, 94 mode_disconnect, 95 mode_listen 96}; 97 98struct callout { 99 struct timeval c_time; /* time at which to call routine */ 100 void *c_arg; /* argument to routine */ 101 void (*c_func)(void *); /* routine */ 102 struct callout *c_next; 103}; 104 105/* -------------------------------------------------------------------------- 106Globals 107-------------------------------------------------------------------------- */ 108 109u_int8_t gNullString[2]; 110 111char *Commands[] = 112{ 113 "!\0", /* 1 */ 114 "@CCLSCRIPT\0", /* 2 */ 115 "@ORIGINATE\0", /* 3 */ 116 "@ANSWER\0", /* 4 */ 117 "@HANGUP\0", /* 5 */ 118 "@LABEL\0", /* 6 */ 119 "ASK\0", /* 7 */ 120 "CHRDELAY\0", /* 8 */ 121 "COMMUNICATINGAT\0", /* 9 */ 122 "DECTRIES\0", /* 10 */ 123 "DTRSET\0", /* 11 */ 124 "DTRCLEAR", /* 12 */ 125 "EXIT", /* 13 */ 126 "FLUSH", /* 14 */ 127 "HSRESET", /* 15 */ 128 "IFANSWER", /* 16 */ 129 "IFORIGINATE", /* 17 */ 130 "IFSTR", /* 18 */ 131 "IFTRIES", /* 19 */ 132 "INCTRIES", /* 20 */ 133 "JUMP", /* 21 */ 134 "JSR", /* 22 */ 135 "LBREAK", /* 23 */ 136 "LOGMSG", /* 24 */ // undocumented. use cNote. KW 137 "MATCHCLR", /* 25 */ 138 "MATCHREAD", /* 26 */ 139 "MATCHSTR", /* 27 */ 140 "NOTE", /* 28 */ 141 "PAUSE", /* 29 */ 142 "RETURN", /* 30 */ 143 "SBREAK", /* 31 */ 144 "SERRESET", /* 32 */ 145 "SETSPEED", /* 33 */ 146 "SETTRIES", /* 34 */ 147 "USERHOOK", /* 35 */ 148 "WRITE", /* 36 */ 149 "MONITORLINE", /* 37 */ 150 "DEBUGLEVEL" /* 38 */ 151}; 152 153 154TRScriptVars SV; // script variables structure 155u_int8_t *VarStrings[vsMax+1]; 156 157 158// The following fields are set from values retrieved from the 159// CCL script 'mlts' resource. The values are received in one 160// or more Script_ccl_setup messages. See ReadVARMessages(). 161// NOTE: if the script file has no 'mlts' resource, the setup 162// message is created using a default 'mlts' stored in the 163// ScriptMod configurator (CCL configurator). 164u_int8_t DialString1MaxLen; 165u_int8_t DialString2MaxLen; 166u_int8_t DialString3MaxLen; 167 168u_int8_t VerboseBuffer[256];// Buffer for forming text for verbose log messages. 169u_int32_t LastExitError; 170 171// Spackle a security hole. Was the last ask masked? if so, 172// the next write we do should log masked. 173// Don't forget to set and clear this flag in later asks and 174// after the write 175u_int32_t LastAskedMasked; 176 177FILE* filefd = (FILE *) 0; 178char *filename = NULL; 179char *phone_num = ""; 180char *username = ""; 181char *password = ""; 182char *alertname = ""; 183char *cancelname = ""; 184char *iconurl = ""; 185CFStringRef alertNameRef = 0; 186CFStringRef cancelNameRef = 0; 187CFURLRef iconURL = 0; 188 189char* localBunPath = ""; 190CFURLRef localBundleURL = NULL; 191 192CFBundleRef cclBundle = NULL; 193CFURLRef cclBundleURL= NULL; 194CFURLRef appBundleURL= NULL; 195 196 197 198int pulse = 0; 199int dialmode = 0; // 0 = normal, 1 = blind(ignoredialtone), 2 = manual 200int speaker = 1; 201int errorcorrection = 1; 202int datacompression = 1; 203u_char *serviceID = NULL; 204 205/* 206int cellphoneCID= 0; 207int cellphoneQofS= 0; 208int limitBandwidth= 0; 209*/ 210 211int verbose = 0; 212u_int32_t debuglevel = 0; 213int sysloglevel = LOG_NOTICE; 214int syslogfacility = 0; //LOG_RAS; Kevin- variable not defined, punt... 215int usestderr = 1; 216int signalerror = 0; 217 218struct callout *callout = NULL; /* Callout list */ 219struct timeval timenow; /* Current time */ 220enum enginemode enginemode = mode_connect; 221 222fd_set allset; 223int maxfd; 224 225/* -------------------------------------------------------------------------- 226Forward Declarations 227-------------------------------------------------------------------------- */ 228void SetVarString(u_int32_t vs, u_int8_t * data); 229u_int8_t *GetVarString(u_int32_t vs); 230int PrepScript(); 231u_int8_t NextLine(); 232int NextCommand(); 233int NextInt(u_int32_t *theIntPtr); // XXX returns signed numbers! 234void PrepStr(u_int8_t *destStr, u_int32_t *isVarString, u_int32_t *varIndex, int varSubstitution); 235void SkipBlanks(); 236void RunScript(); 237int MatchStr(); 238void Note(); 239u_int8_t Write(); 240void CommunicatingAt(); 241u_int8_t Ask(); 242void WriteContinue(); 243void UserHook(); 244void ScheduleTimer(long type, u_int32_t milliseconds); 245int IfStr(); 246 247void MatchClr(); 248void Break(); 249void SetSpeed(void); 250void SerReset(void); 251void MonitorLine(); 252void DebugLevel(); 253void HSReset(); 254void Flush(); 255void DTRCommand(short DTRCode); 256 257void terminate(int exitError); 258void InitScript(); 259void Play(); 260void calltimeout(void); 261struct timeval *timeleft(struct timeval *); 262void timeout(void (*func)(void *), void *arg, u_long time); 263void untimeout(void (*func)(void *), void *arg); 264void ReceiveMatchData(u_int8_t nextChar); 265int publish_entry(u_char *serviceid, CFStringRef entry, CFTypeRef value); 266int unpublish_entry(u_char *serviceid, CFStringRef entry); 267bool SetVarStringFromDict(CFDictionaryRef dict, CFStringRef key, int varStringIndex); 268void sLog(char *fmt, ...); 269 270 271 272/*************** Micro getopt() *********************************************/ 273#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \ 274 (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\ 275 &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0)) 276#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \ 277 (_O=4,(char*)0):(char*)0) 278#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0) 279#define ARG(c,v) (c?(--c,*v++):(char*)0) 280 281/*************** Micro getopt() *********************************************/ 282 283 284/* -------------------------------------------------------------------------- 285-------------------------------------------------------------------------- */ 286void badsignal(int signo) 287{ 288 signalerror = cclErr_NoMemErr; 289} 290 291/* -------------------------------------------------------------------------- 292-------------------------------------------------------------------------- */ 293void hangup(int signo) 294{ 295 // 7341084: ignore signals during disconnection 296 if (enginemode != mode_disconnect) 297 signalerror = cclErr_ScriptCancelled; 298} 299 300 301/* -------------------------------------------------------------------------- 302-------------------------------------------------------------------------- */ 303void StartRead() 304{ 305 FD_SET(infd, &allset); 306 maxfd = 1; 307} 308 309/* -------------------------------------------------------------------------- 310-------------------------------------------------------------------------- */ 311void StopRead() 312{ 313 FD_ZERO(&allset); 314 maxfd = 0; 315} 316 317#pragma mark Bundle Configuration/Startup 318 319/* -------------------------------------------------------------------------- 320SetVarStringFromDict 321- handles pulling the data out and setting the var value for the passed in key 322- Returns success (true) even if the value is missing from the input dict 323- fails only if the data is not of string type or if it can't be extracted 324-------------------------------------------------------------------------- */ 325bool SetVarStringFromDict(CFDictionaryRef dict, CFStringRef key, int varStringIndex) 326{ 327 bool retVal= false; 328 u_char text[256]; 329 CFStringRef varString; 330 331 varString= (CFStringRef)CFDictionaryGetValue(dict, key); 332 if(varString == NULL) { 333 retVal = true; // missing values are okay (will return gNullString) 334 } else if(CFStringGetTypeID() == CFGetTypeID(varString)) { 335 // X varStrings are defined to be in the system encoding? 336 if(CFStringGetPascalString(varString,text,256,CFStringGetSystemEncoding())) 337 { 338 SetVarString(varStringIndex, text); 339 retVal= true; 340 } 341 } else { 342 sLog("SetVarStringFromDict: value with non-string type"); 343 CFShow(key); 344 CFShow(varString); 345 } 346 return retVal; 347} 348 349/* -------------------------------------------------------------------------- 350// check to make sure that the CCL version is the one we know 351-------------------------------------------------------------------------- */ 352bool VerifyCCLBundle(CFBundleRef cclBun) 353{ 354 CFDictionaryRef infoDict= CFBundleGetInfoDictionary(cclBun); 355 int verNum = -1; 356 357 if(!infoDict || !GetIntFromDict(infoDict, &verNum, kCCLVersionKey)) 358 return false; 359 360 return (verNum == kCCLBundleVersion); 361} 362 363/* -------------------------------------------------------------------------- 364XX: should merge in keys in case we want to store CCLParameters in SC 365Missing values are ignored by SetVarStringFromDict 366-------------------------------------------------------------------------- */ 367bool ConfigureCCLParameters(CFDictionaryRef personalityDict) 368{ 369 bool rval = false; 370 CFDictionaryRef cclParms= GetCFDictionaryFromDict(personalityDict, kCCLParametersKey); 371 if(cclParms) 372 { 373 rval = SetVarStringFromDict(cclParms, kCCLVarString27Key, vsString27); 374 rval &= SetVarStringFromDict(cclParms, kCCLVarString28Key, vsString28); 375 rval &= SetVarStringFromDict(cclParms, kCCLVarString29Key, vsString29); 376 rval &= SetVarStringFromDict(cclParms, kCCLVarString30Key, vsString30); 377 rval&=SetVarStringFromDict(cclParms,kCCLConnectSpeedKey,vsConnectSpeed); 378 rval &= SetVarStringFromDict(cclParms, kCCLInitStringKey, vsInit); 379 380 // Preferred* keys are handled by libccl's cclparser + SC. 381 // The user has to confirm that they want those values. 382 } 383 return rval; 384} 385 386/* -------------------------------------------------------------------------- 387-------------------------------------------------------------------------- */ 388CFURLRef 389CopyScriptWithPersonality(CFBundleRef bundleRef, CFStringRef personalityKey) 390{ 391 CFURLRef rval = NULL; 392 CFDictionaryRef infoDict; 393 CFDictionaryRef persDict; 394 CFDictionaryRef persEntry = NULL; 395 396 if (!(infoDict = CFBundleGetInfoDictionary(bundleRef))) 397 goto finish; 398 if (!(persDict = GetCFDictionaryFromDict(infoDict, kCCLPersonalitiesKey))) 399 goto finish; 400 401 // check the provided personality key 402 if (personalityKey) 403 persEntry = GetCFDictionaryFromDict(persDict, personalityKey); 404 // or try the default personality (X could mask missing personality) 405 if (!persEntry) 406 persEntry = GetCFDictionaryFromDict(persDict,kCCLDefaultPersonalityKey); 407 408 // if we found something, get the script name 409 if (persEntry) { 410 CFStringRef scriptName = NULL; 411 412 if (!GetCFStringFromDict(persEntry,&scriptName,kCCLScriptNameKey)) 413 goto finish; 414 if (!scriptName) 415 goto finish; 416 417 rval = CFBundleCopyResourceURL(bundleRef, scriptName, NULL, NULL); 418 if (rval) { 419 // We do this here to avoid having to determinepersonality twice... 420 if (!ConfigureCCLParameters(persEntry)) { 421 CFRelease(rval); 422 rval = NULL; 423 } 424 // modemDict CCLParameters (if any) merged in later 425 } 426 } 427 428finish: 429 return rval; 430} 431 432/* -------------------------------------------------------------------------- 433-------------------------------------------------------------------------- */ 434bool ConfigureWithBundle(CFBundleRef bundleRef, CFStringRef personality) 435{ 436 bool retVal= false; 437 UInt8 pathBuffer[MAXPATHLEN]; 438 439 if(bundleRef!= NULL) 440 { 441 if(VerifyCCLBundle(bundleRef)) 442 { 443 CFURLRef scriptURL= CopyScriptWithPersonality(bundleRef, personality); 444 if(scriptURL!= NULL) 445 { 446 if(CFURLGetFileSystemRepresentation(scriptURL, true, pathBuffer, MAXPATHLEN)) 447 { 448 if (filename) free(filename); 449 filename = strdup((char*)pathBuffer); 450 retVal= true; 451 CFRetain(bundleRef); 452 cclBundle= bundleRef; 453 } 454 CFRelease(scriptURL); 455 } 456 } 457 } 458 return retVal; 459} 460 461/* -------------------------------------------------------------------------- 462ResolveCCLPath() resolves the full path with the following preferences: 463- /full/path/to/foo.ccl 464- /full/path/to/foo 465- /System/Library/Modem Scripts/foo.ccl 466- /System/Library/Modem Scripts/foo 467- /Library/Modem Scripts/foo.ccl 468- /Library/Modem Scripts/foo 469Returns true if it found something 470Side Effects: fullpath used as scratch space 471 472Fairly common case should (eventually) be fully-resolved .ccl path. 473Note: even if foo exists; prefers foo.ccl 474-------------------------------------------------------------------------- */ 475bool ResolveCCLPath(char *path, char fullpath[PATH_MAX], struct stat *sb) 476{ 477 char *extptr = path + (strlen(path) - (kCCL_BundleExtLen + sizeof('.'))); 478 bool cclExt; 479 480 // try to avoid an unnecessary stat() 481 cclExt = (*extptr == '.') && (strcmp(extptr+1, kCCL_BundleExtension) == 0); 482 483 if (path[0] == '/') { 484 if (!cclExt) { 485 snprintf(fullpath, PATH_MAX, "%s.%s", path, kCCL_BundleExtension); 486 if (stat(fullpath, sb) == 0) return true; 487 } 488 strlcpy(fullpath, path, PATH_MAX); 489 if (stat(fullpath, sb) == 0) return true; 490 } else { 491 // path is not absolute so try the common directories 492 if (!cclExt) { 493 snprintf(fullpath, PATH_MAX, "%s/%s.%s", DIR_MODEMS_SYS, path, 494 kCCL_BundleExtension); 495 if (stat(fullpath, sb) == 0) return true; 496 } 497 snprintf(fullpath, PATH_MAX, "%s/%s", DIR_MODEMS_SYS, path); 498 if (stat(fullpath, sb) == 0) return true; 499 500 if (!cclExt) { 501 snprintf(fullpath, PATH_MAX, "%s/%s.%s", DIR_MODEMS_USER, path, 502 kCCL_BundleExtension); 503 if (stat(fullpath, sb) == 0) return true; 504 } 505 snprintf(fullpath, PATH_MAX, "%s/%s", DIR_MODEMS_USER, path); 506 if (stat(fullpath, sb) == 0) return true; 507 } 508 509 return false; 510} 511 512#define LOG_MAX 1024 513/* -------------------------------------------------------------------------- 514sLog(): capture syslog/stderr idiom that was scattered throughout the code 515-------------------------------------------------------------------------- */ 516void sLog(char *fmt, ...) 517{ 518 va_list ap; 519 char taggedfmt[LOG_MAX], s[64]; 520 time_t t; 521 522 523 if (sysloglevel) { 524 va_start(ap, fmt); 525 vsyslog(sysloglevel, fmt, ap); 526 va_end(ap); 527 } 528 if (usestderr) { 529 time(&t); 530 strftime(s, sizeof(s), "%c : ", localtime(&t)); 531 strlcpy(taggedfmt, s, LOG_MAX); 532 strlcat(taggedfmt, fmt, LOG_MAX); 533 strlcat(taggedfmt, "\n", LOG_MAX); 534 va_start(ap, fmt); 535 vfprintf(stderr, taggedfmt, ap); 536 va_end(ap); 537 } 538} 539 540/* -------------------------------------------------------------------------- 541validate path and see if it's a bundle; otherwise it's an old-school flat CCL 542-------------------------------------------------------------------------- */ 543bool ConfigureCCL(char* path, CFStringRef personality) 544{ 545 bool retVal = false; 546 CFURLRef url; 547 CFBundleRef bundleRef; 548 char fullpath[PATH_MAX]; 549 struct stat sb; 550 551 // it's possible we have only a name 552 // we need to turn it into a path and perhaps add .ccl to it 553 if (!ResolveCCLPath(path, fullpath, &sb)) { 554 sLog("couldn't find CCL '%s'", path); 555 goto finish; 556 } 557 558 url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, 559 (UInt8*)fullpath, strlen(fullpath), S_ISDIR(sb.st_mode)); 560 if (!url) goto finish; 561 562 bundleRef= CFBundleCreate(kCFAllocatorDefault, url); 563 if(bundleRef) { 564 // retains cclBundle, copies filename, etc 565 retVal = ConfigureWithBundle(bundleRef, personality); 566 CFRelease(bundleRef); 567 if (retVal) 568 cclBundleURL = url; // keep for loc 569 else { 570 CFRelease(url); 571 sLog("%s does not appear to be a CCL bundle", fullpath); 572 } 573 } else { 574 // maybe it was "old school" 575 if (S_ISREG(sb.st_mode)) { 576 CFRelease(url); 577 if (filename) free(filename); 578 filename = strdup(fullpath); 579 retVal = true; 580 } else { 581 sLog("%s is neither a bundle nor a file", filename); 582 } 583 } 584 585finish: 586 return retVal; 587} 588 589/* -------------------------------------------------------------------------- 590-------------------------------------------------------------------------- */ 591bool ParseEngineDict(CFDictionaryRef argDict) 592{ 593 bool success= false; 594 595 CFDictionaryRef engineDict= GetCFDictionaryFromDict(argDict, kCCLEngineDictKey); 596 if(engineDict!= NULL) 597 { 598 CFStringRef modeString= (CFStringRef) CFDictionaryGetValue(engineDict, kCCLEngineModeKey); 599 if(modeString!= NULL) 600 { 601 // we assumed mode_connect when we initialized enginemode 602 if((CFStringGetTypeID()== CFGetTypeID(modeString)) && (CFStringCompare(modeString, kCCLEngineModeDisconnect, 0)== kCFCompareEqualTo)) 603 { 604 enginemode = mode_disconnect; 605 } 606 // Get*FromDict return success if the value was missing 607 // i.e. the only errors are from conversion problems 608 success = CopyCStringFromDict(engineDict, &alertname, kCCLEngineAlertNameKey); 609 success &= CopyCStringFromDict(engineDict, &localBunPath, kCCLEngineBundlePathKey); 610 success &= CopyCStringFromDict(engineDict, &cancelname, kCCLEngineCancelNameKey); 611 success &= CopyCStringFromDict(engineDict, &iconurl, kCCLEngineIconPathKey); 612 success &= GetIntFromDict(engineDict, &usestderr, kCCLEngineLogToStdErrKey); 613 success &= CopyCStringFromDict(engineDict, (char**) &serviceID, kCCLEngineServiceIDKey); 614 success &= GetIntFromDict(engineDict, &syslogfacility, kCCLEngineSyslogFacilityKey); 615 success &= GetIntFromDict(engineDict, &sysloglevel, kCCLEngineSyslogLevelKey); 616 success &= GetIntFromDict(engineDict, &verbose, kCCLEngineVerboseLoggingKey); 617 } 618 } 619 620 return success; 621} 622 623/* -------------------------------------------------------------------------- 624-------------------------------------------------------------------------- */ 625bool ParseModemDict(CFDictionaryRef argDict) 626{ 627 bool success= false; 628 CFDictionaryRef modemDict= GetCFDictionaryFromDict(argDict, kSCEntNetModem); 629 630 // CFShow(modemDict); 631 if(modemDict!= NULL) 632 { 633 char* tempFilePath = NULL; 634 CFStringRef personality = NULL; 635 636 if (!CopyCStringFromDict(modemDict, &tempFilePath, kSCPropNetModemConnectionScript)) 637 goto finish; 638 if (!tempFilePath) 639 goto finish; 640 if (!GetCFStringFromDict(modemDict, &personality, kSCPropNetModemConnectionPersonality)) 641 goto finish; 642 643 // ConfigureCCL extracts default CCLParameters, handles NULL personality 644 success = ConfigureCCL(tempFilePath, personality); 645 free(tempFilePath); 646 if (!success) 647 goto finish; 648 649 // dialmode is a string which we convert 650 dialmode= kNormalDialMode; // assume normal dial 651 CFStringRef dialModeStr = (CFStringRef) CFDictionaryGetValue(modemDict, kSCPropNetModemDialMode); 652 if(dialModeStr && CFStringGetTypeID()==CFGetTypeID(dialModeStr)){ 653 if (kCFCompareEqualTo == CFStringCompare(dialModeStr, 654 kSCValNetModemDialModeIgnoreDialTone,0)) 655 dialmode = kBlindDialMode; 656 else if (kCFCompareEqualTo == CFStringCompare(dialModeStr, 657 kSCValNetModemDialModeManual,0)) 658 dialmode = kManualDialMode; 659 } 660 661 // only needed for mode_connect 662 if (enginemode == mode_connect) { 663 // expected, but we have reasonable defaults 664 success &= GetIntFromDict(modemDict, &datacompression, kSCPropNetModemDataCompression); 665 success &= GetIntFromDict(modemDict, &errorcorrection, kSCPropNetModemErrorCorrection); 666 success &= CopyCStringFromDict(modemDict,&phone_num,kModemPhoneNumberKey); 667 success &= GetIntFromDict(modemDict, &pulse, kSCPropNetModemPulseDial); 668 success &= GetIntFromDict(modemDict, &speaker, kSCPropNetModemSpeaker); 669 success &= CopyCStringFromDict(modemDict, &username, kSCPropNetPPPAuthName); 670 success &= CopyCStringFromDict(modemDict, &password, kSCPropNetPPPAuthPassword); 671 672 673 // optional, and SetVarStringFromDict sets to gNull if missing 674 success &= SetVarStringFromDict(modemDict, kSCPropNetModemAccessPointName, vsAPN); 675 success &= SetVarStringFromDict(modemDict, kSCPropNetModemDeviceContextID, vsCID); 676 } 677 678 // speed might be needed for disconnect? 679 success &= SetVarStringFromDict(modemDict, kSCPropNetModemConnectSpeed, vsConnectSpeed); 680 // stored kSCPropNetModemConnectSpeed could override CCL's static config 681 // X could call ConfigureCCLParameters with modem dict (SC) version here 682 } 683 684finish: 685 return success; 686} 687 688 689/* -------------------------------------------------------------------------- 690-------------------------------------------------------------------------- */ 691bool ParseArgDictionary(CFDictionaryRef argDict) 692{ 693 CFBundleRef mainBundle= NULL; 694 695 if(ParseEngineDict(argDict)) { 696 if(ParseModemDict(argDict)) { 697 mainBundle= CFBundleGetMainBundle(); 698 if(mainBundle) 699 appBundleURL= CFBundleCopyBundleURL(mainBundle); 700 return true; 701 } 702 } 703 704 return false; 705} 706 707 708/* -------------------------------------------------------------------------- 709-------------------------------------------------------------------------- */ 710#define kParseBufferSize 4096 711bool ParsePipeArgs() 712{ 713 ssize_t bRead= 0; 714 ssize_t tbRead= 0; 715 716 bool retVal= false; 717 718 void* tempBuf; 719 char* dataBuf= malloc(kParseBufferSize); 720 if (!dataBuf) goto finish; 721 722 bRead= read(PPP_ARG_FD, dataBuf, kParseBufferSize); 723 tbRead= bRead; 724 while (bRead == kParseBufferSize) { 725 tempBuf= realloc(dataBuf, (tbRead+ kParseBufferSize)); 726 if (tempBuf) { 727 dataBuf= tempBuf; 728 bRead= read(PPP_ARG_FD, &dataBuf[tbRead], kParseBufferSize); 729 tbRead+= bRead; 730 } else { 731 goto finish; 732 } 733 734 } 735 CFDictionaryRef argDict= IOCFUnserialize(dataBuf, kCFAllocatorDefault, kCFPropertyListImmutable, NULL); 736 if(argDict!= NULL) 737 { 738 retVal= ParseArgDictionary(argDict); 739 } 740 741finish: 742 if (dataBuf) free(dataBuf); 743 close(PPP_ARG_FD); 744 return retVal; 745} 746 747/* -------------------------------------------------------------------------- 748-------------------------------------------------------------------------- */ 749int main(int argc, char **argv) 750{ 751 char c; 752 int ret, nready, status, i; 753 struct stat statbuf; 754 fd_set rset; 755 struct timeval timo; 756 757/* move DEBUG stuff (below) here if ppp/CCLEngine interactions need debugging */ 758 759 signal(SIGHUP, hangup); /* Hangup */ 760 signal(SIGINT, hangup); /* Interrupt */ 761 signal(SIGTERM, hangup); /* Terminate */ 762 signal(SIGCHLD, badsignal); 763 signal(SIGUSR1, badsignal); 764 signal(SIGUSR2, badsignal); 765 signal(SIGABRT, badsignal); 766 signal(SIGALRM, badsignal); 767 signal(SIGFPE, badsignal); 768 signal(SIGILL, badsignal); 769 signal(SIGPIPE, badsignal); 770 signal(SIGQUIT, badsignal); 771 signal(SIGSEGV, badsignal); 772 773 // zero all of the strings (removed from InitScript) 774 for (i = 0; i <= vsMax; i++) 775 VarStrings[i] = 0; 776 777 openlog("ccl", LOG_PID | LOG_NDELAY, syslogfacility); 778 779 if(!ParsePipeArgs()) 780 terminate(cclErr_BadParameter); 781 782/* DEBUG for attach on connect only (don't sweat the PID; 783 (gdb) attach CCLEngine * / 784if (enginemode == mode_connect) { 785fprintf(stderr, "CCLEngine pid %d\n", getpid()); 786fflush(stderr); 787sleep(3); 788} 789*/ 790 791 InitScript(); // sets whatever variables PPA() didn't 792 793 alertNameRef = CFStringCreateWithCString(NULL, alertname, kCFStringEncodingUTF8); 794 cancelNameRef = CFStringCreateWithCString(NULL, cancelname, kCFStringEncodingUTF8); 795 if (*iconurl) 796 iconURL = CFURLCreateFromFileSystemRepresentation(nil, (UInt8*) iconurl, strlen(iconurl), false /*notDir*/); 797 if (*localBunPath) 798 localBundleURL = CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*) localBunPath, strlen(localBunPath), true /*isDir*/); 799 800 // figure out file size 801 ret = stat(filename, &statbuf); 802 if (ret < 0) 803 terminate(cclErr_BadParameter); 804 805 SV.script = malloc((int)statbuf.st_size); 806 if (!SV.script) 807 terminate(cclErr_NoMemErr); 808 809 SV.scriptSize = (int)statbuf.st_size; 810 811 filefd = fopen(filename, "r"); 812 if (filefd) { 813 ret = fread(SV.script, statbuf.st_size, 1, filefd); 814 if (ret < 0) 815 terminate(cclErr_BadParameter); 816 } 817 818 /* start by unpublishing any remaining information */ 819 if (serviceID) { 820 unpublish_entry((u_char*) serviceID, kSCPropNetModemConnectSpeed); 821 } 822 StopRead(); 823 Play(); 824 825 for ( ; ; ) { 826 827 rset = allset; 828 nready = select(maxfd, &rset, NULL, NULL, timeleft(&timo)); 829 if(signalerror) 830 terminate(signalerror); 831 if (FD_ISSET(infd, &rset)) { 832 status = read(infd, &c, 1); 833 switch (status) { 834 case 1: 835 ReceiveMatchData(c); 836 break; 837 default: 838 if (errno != EINTR) 839 terminate(cclErr); 840 } 841 } 842 843 calltimeout(); 844 845 } 846 847 // NOT REACHED 848 exit(0); // insure the process exit status is 0 849 return 0; // ...and make main fit the ANSI spec. 850} 851 852/* -------------------------------------------------------------------------- 853 init everything 854-------------------------------------------------------------------------- */ 855void InitScript() 856{ 857 u_char text[256], text1[256]; 858 859 gNullString[0] = 1; 860 gNullString[1] = ' '; 861 862 // 863 // Initialize script variables and varStrings 864 // 865 bzero(&SV, sizeof(struct TRScriptVars)); 866 867 LastExitError = 0; 868 869 DialString1MaxLen = 40; 870 DialString2MaxLen = 40; 871 DialString3MaxLen = 40; 872 873 SV.topOfStack = cclNestingLimit; // this stack is for JSR and RETURN commands. 874 SV.theAbortErr = 0; 875 SV.lineCount = 0; 876 SV.scriptPrepped = 0; // PrepScript has not yet been called 877 SV.scriptPrepFailed = 0; // PrepScript has not yet rejected the script 878 SV.scriptAllocSize = 0; 879 SV.scriptSize = 0; 880 SV.script = NULL; 881 SV.askLabel = 0; 882 SV.indexTable = NULL; 883 SV.commands = NULL; 884 885 LastAskedMasked = 0; 886 887 888 text[0] = strlen(username); 889 bcopy(username, &text[1], text[0]); 890 SetVarString(vsUserName, text); 891 892 text[0] = strlen(password); 893 bcopy(password, &text[1], text[0]); 894 SetVarString(vsPassWord, text); 895 896 sprintf((char*) text, "%d", speaker); 897 text1[0] = strlen((char*) text); 898 bcopy(text, &text1[1], text[0]); 899 SetVarString(vsModemSpeaker, text1); 900 901 sprintf((char*) text, "%d", errorcorrection); 902 text1[0] = strlen((char*) text); 903 bcopy(text, &text1[1], text[0]); 904 SetVarString(vsErrorCorrection, text1); 905 906 sprintf((char*) text, "%d", datacompression); 907 text1[0] = strlen((char*) text); 908 bcopy(text, &text1[1], text[0]); 909 SetVarString(vsDataCompression, text1); 910 911 sprintf((char*) text, "%d", dialmode); 912 text1[0] = strlen((char*) text); 913 bcopy(text, &text1[1], text[0]); 914 SetVarString(vsDialMode, text1); 915 916 text1[0] = 1; 917 text1[1] = pulse ? 'P' : 'T'; 918 SetVarString(vsTonePulse, text1); 919} 920 921/* -------------------------------------------------------------------------- 922 dispose everything 923-------------------------------------------------------------------------- */ 924void DisposeScript() 925{ 926 int i; 927 928 if (SV.script) { 929 free(SV.script); 930 SV.script = 0; 931 } 932 933 if (SV.commands) { 934 free(SV.commands); 935 SV.commands = 0; 936 } 937 938 if (SV.indexTable) { 939 free(SV.indexTable); 940 SV.indexTable = 0; 941 } 942 943 for (i = 0; i <= vsMax; i++) { 944 if (VarStrings[i]) { 945 free(VarStrings[i]); 946 VarStrings[i] = 0; 947 } 948 } 949} 950 951/* -------------------------------------------------------------------------- 952varStrings are held in the global VarStrings[]. Each element is NULL 953until SetVarString is called, in which case memory is allocated and the 954data copied in. If Set has not previously succeeded, Get will return a 955pointer to gNullString, a zero length pstring. Memory errors don't abort 956the script but they may break it if the empty string wasn't acceptable 957for that particular varString's value. 958-------------------------------------------------------------------------- */ 959void SetVarString(u_int32_t vs, u_int8_t * data) 960{ 961 if (vs > vsMax) 962 return; 963 964 if (VarStrings[vs]) { 965 free(VarStrings[vs]); 966 VarStrings[vs] = 0; 967 } 968 969 if (data) { 970 VarStrings[vs] = malloc(*data + 1); 971 if (VarStrings[vs]) 972 bcopy(data, VarStrings[vs], *data + 1 ); 973 else 974 sLog("SetVarString: %s", strerror(errno)); // should be ENOMEM 975 } 976} 977 978/* -------------------------------------------------------------------------- 979-------------------------------------------------------------------------- */ 980u_int8_t *GetVarString(u_int32_t vs) 981{ 982 if (vs > vsMax) 983 { 984 return gNullString; 985 } 986 if (VarStrings[vs]) 987 { 988 return VarStrings[vs]; 989 } 990 return gNullString; 991} 992 993 994/* -------------------------------------------------------------------------- 995called to start execution of the script. this is true regardless as 996to the connect/answer mode set by SetAnswering(). 997-------------------------------------------------------------------------- */ 998void Play(void) 999{ 1000 u_int32_t remaining; 1001 u_int8_t *src, len = 0, buf[256]; 1002 u_char text[256]; 1003 1004 LastExitError = 0; 1005 1006 if (!SV.scriptPrepped ) { // if the Script has not yet been preflighted... 1007 if (SV.theAbortErr = PrepScript()) { // if the preflight returns an error... 1008 SV.scriptPrepFailed = 1; // so Disconnect won't try to re-preflight the script. 1009 sLog("Script parsing failed after %d lines", SV.scriptLine); 1010 terminate(SV.theAbortErr); // a script syntax error was detected 1011 return; 1012 } 1013 else 1014 SV.scriptPrepped = 1; 1015 } 1016 1017 SV.chrDelayValue = 0; 1018 1019 switch (enginemode) { 1020 case mode_connect: 1021 SV.ctlFlags &= ~cclHangupMode; 1022 SV.ctlFlags |= cclOriginateMode; // turn on OriginateMode 1023 SV.scriptLine = SV.originateLine; // set the script entry point 1024 1025 // if the dial string has not yet arrived in a Script_ccl_setup message, 1026 // fetch the dial string from ScriptMod, which received it from either a 1027 // T_conn_req or a Script_execute message. 1028 1029 // XX should an empty phone number string -> gNullString? 1030 text[0] = strlen(phone_num); 1031 bcopy(phone_num, &text[1], text[0]); 1032 if (GetVarString(vsDialString) == gNullString) 1033 SetVarString(vsDialString, text); 1034 1035 SetVarString(vsDialString1, NULL); 1036 SetVarString(vsDialString2, NULL); 1037 SetVarString(vsDialString3, NULL); 1038 1039 if (src = GetVarString(vsDialString)) { 1040 1041 remaining = *src++; 1042 1043 if (len = min(DialString1MaxLen, remaining)) { 1044 *buf = len; 1045 bcopy(src, buf+1, len); 1046 SetVarString(vsDialString1, buf); 1047 src += len; 1048 remaining -= len; 1049 } 1050 1051 if (len = min(DialString2MaxLen, remaining)) { 1052 *buf = len; 1053 bcopy(src, buf+1, len); 1054 SetVarString(vsDialString2, buf); 1055 src += len; 1056 remaining -= len; 1057 } 1058 1059 if (len = min(DialString3MaxLen, remaining)) { 1060 *buf = len; 1061 bcopy(src, buf+1, len); 1062 SetVarString(vsDialString3, buf); 1063 src += len; 1064 remaining -= len; 1065 } 1066 1067 // and ignore any remaining characters 1068 } 1069 1070 break; 1071 case mode_disconnect: 1072 SV.ctlFlags &= ~cclAnswerMode; // though we'll return to Answering, on disconnect 1073 SV.ctlFlags |= cclHangupMode; 1074 SV.scriptLine = SV.hangUpLine; // set the script entry point 1075 break; 1076 case mode_listen: 1077 SV.ctlFlags |= cclAnswerMode; // turn on AnswerMode 1078 SV.ctlFlags &= ~cclHangupMode; 1079 SV.scriptLine = SV.answerLine; // set the script entry point 1080 break; 1081 } 1082 1083 RunScript(); // plays the script from @entrypoint until Exit 1084} 1085 1086/* -------------------------------------------------------------------------- 1087This routine prepares a script to be played. 1088CCL variablesa and labels are set up for playing the script, and commands are checked. 1089-------------------------------------------------------------------------- */ 1090int PrepScript() 1091{ 1092 u_int8_t *bp, *d, *s, c; 1093 u_int16_t *indexp, Lindex, i, cmd; 1094 u_int32_t labelIndex; 1095 int result = 0; 1096 1097 // The following "do {} while (false);" construct 1098 // prepares for the task of looping through the lines 1099 // of script. If there is something wrong with script 1100 // or if it can't allocate memory, it breaks immediately 1101 // with an error value in "result". 1102 1103 do { 1104 if (SV.scriptSize > MAX_SCRIPT_SIZE) { 1105 result = cclErr_ScriptTooBig; 1106 break; 1107 } 1108 1109 Lindex = 1; 1110 bp = SV.script; 1111 1112 // count the lines in the script: 1113 for( i = SV.scriptSize; i; --i ) { 1114 if ((*bp == chrCR) || (*bp == chrNL)) 1115 Lindex++; 1116 bp++; 1117 } 1118 1119 if (Lindex > MAX_SCRIPT_LINES) { 1120 result = cclErr_TooManyLines; 1121 break; 1122 } 1123 1124 // Lindex is 1-based; lineCount is zero-based 1125 // However, if the last character wasn't a line terminator; bump 1126 SV.lineCount = Lindex - 1; 1127 c = SV.script[SV.scriptSize-1]; 1128 if (c != chrCR && c != chrNL) 1129 SV.lineCount++; 1130 1131 bp = malloc(500); 1132 if (!bp) { 1133 result = ENOMEM; 1134 break; 1135 } 1136 1137 SV.commands = bp; 1138 *((u_int16_t *)bp) = cLastCmd; 1139 bp += 2; 1140 for( i = 0; i < cLastCmd; ) { 1141 s = (u_int8_t*) Commands[i++]; 1142 d = bp + 1; 1143 while( c = *s++ ) 1144 *d++ = c; 1145 *bp = (d - bp - 1); // set count for pascal string 1146 bp = d; 1147 } 1148 1149 SV.indexTable = malloc(SV.lineCount * sizeof(u_int16_t *)); 1150 if (!SV.indexTable) { 1151 result = ENOMEM; 1152 break; 1153 } 1154 1155 indexp = SV.indexTable; 1156 for( i = 0; ++i <= SV.lineCount; ) 1157 *indexp++ = 0; 1158 1159 /* adjust the size of line index array */ 1160 indexp = SV.indexTable; 1161 1162 /* fill line index array, for each line, */ 1163 /* SV.indexTable->lineIndex == byte offset */ 1164 /* from start of script to start of line. */ 1165 bp = SV.script; 1166 Lindex = 0; 1167 *indexp++ = 0; 1168 for (i = 0; i < SV.scriptSize; i++) { 1169 if ((*bp == chrCR) || (*bp == chrNL)) 1170 *indexp++ = i + 1; 1171 bp++; 1172 } 1173 1174 SV.scriptLine = 0; 1175 } 1176 while (0); 1177 1178 // The initialization loop above may have set 1179 // result != noErr. If so, the following while 1180 // loop is not entered. 1181 1182 while (result == 0 && NextLine()) { /*process the next line of the script*/ 1183 cmd = NextCommand(); 1184 switch (cmd) { 1185 case cNoCmd: 1186 result = cclErr_BadCommand; 1187 break; 1188 1189 case cOriginateLabel: 1190 if( SV.originateLine ) 1191 result = cclErr_DuplicateLabel; 1192 else 1193 SV.originateLine = SV.scriptLine; 1194 break; 1195 1196 case cAnswerLabel: 1197 if( SV.answerLine ) 1198 result = cclErr_DuplicateLabel; 1199 else 1200 SV.answerLine = SV.scriptLine; 1201 break; 1202 1203 case cHangUpLabel: 1204 if( SV.hangUpLine ) 1205 result = cclErr_DuplicateLabel; 1206 else 1207 SV.hangUpLine = SV.scriptLine; 1208 break; 1209 1210 case cScriptLabel: 1211 result = NextInt(&labelIndex); 1212 if (result == 0) { 1213 labelIndex--; 1214 if ((labelIndex < 0) || (labelIndex >= MAXLABELS)) 1215 result = cclErr_BadLabel; 1216 else { 1217 if (SV.labels[labelIndex]) 1218 result = cclErr_DuplicateLabel; 1219 else 1220 SV.labels[labelIndex] = SV.scriptLine; 1221 } 1222 } 1223 break; 1224 1225 // The following cases are commands for which we check the syntax 1226 // to verify as much a possible that the parameters are OK 1227 case cExit: 1228 // check for exit result parameter 1229 result = NextInt(&labelIndex); 1230 // check for result in appropriate range? 1231 break; 1232 1233 case cHSReset: 1234 for (i = 0; result == 0 && i < kHSResetParamCount; i++) 1235 result = NextInt(&labelIndex); 1236 break; 1237 1238 case cAsk: // check for a integer maskflag value 1239 case cChrDelay: // check for a integer delay value 1240 case cCommunicatingAt: // check for a integer modem speed value 1241 case cIfAnswer: // ditto 1242 case cIfOriginate: // ditto 1243 case cJSR: // ditto 1244 case cJump: // mega ditto 1245 case cMatchRead: // check for a integer timeout value 1246 case cPause: // check for a integer pause time value 1247 case cSetSpeed: // check for a integer interface speed value 1248 case cSetTries: // check for a integer tries value 1249 case cUserHook: // check for a integer opcode value 1250 case cMonitorLine: // check for a integer bits mask value 1251 case cDebugLevel: // check for a integer bits mask value 1252 result = NextInt(&labelIndex); 1253 break; 1254 1255 case cIfStr: 1256 for (i = 0; result == 0 && i < (kIfStrParamCount - 1); i++) 1257 result = NextInt(&labelIndex); 1258 break; 1259 1260 case cMatchStr: 1261 if (NextInt(&labelIndex) == 0 && labelIndex > maxMatch) { 1262 result = cclErr_MatchStrIndxErr; 1263 break; 1264 } 1265 result = NextInt(&labelIndex); 1266 SkipBlanks(); 1267 PrepStr(SV.strBuf, NULL, NULL, 1); 1268 // 1269 // Don't allow to match empty string 1270 // 1271 if (SV.strBuf[0] == 0) 1272 result = cclErr_BadParameter; 1273 break; 1274 1275 case cIfTries: 1276 for (i = 0; result == 0 && i < (kIfTriesParamCount - 1); i++) 1277 result = NextInt(&labelIndex); 1278 break; 1279 1280 case cLogMsg: // undocumented. use cNote. KW 1281 case cNote: 1282 case cWrite: 1283 SkipBlanks(); 1284 PrepStr(SV.strBuf, NULL, NULL, 1); 1285 if( SV.strBuf[0] == 0 ) 1286 result = cclErr_BadParameter; 1287 break; 1288 1289 case cSerReset: 1290 for (i = 0; result == 0 && i < kSerResetParamCount; i++) 1291 result = NextInt(&labelIndex); 1292 break; 1293 1294 default: 1295 break; 1296 }/* end SWITCH on command type */ 1297 }/* end WHILE */ 1298 1299 if (result == 0) { 1300 if (SV.originateLine == 0) result = cclErr_NoOriginateLabel; 1301 else if (SV.answerLine == 0) result = cclErr_NoAnswerLabel; 1302 else if (SV.hangUpLine == 0) result = cclErr_NoHangUpLabel; 1303 else SV.scriptLine = 0; 1304 1305 // process the script again to check that all 1306 // destination labels are valid: 1307 while (result == 0 && NextLine()) { 1308 cmd = NextCommand(); 1309 switch (cmd) { 1310 case cIfAnswer: 1311 case cIfOriginate: 1312 case cJump: 1313 case cJSR: 1314 result = NextInt(&labelIndex); 1315 if (result == 0) { 1316 labelIndex--; 1317 if ((labelIndex < 0) || (labelIndex >= MAXLABELS)) 1318 result = cclErr_BadLabel; 1319 else { 1320 if (SV.labels[ labelIndex ] == 0) 1321 result = cclErr_LabelUndefined; 1322 } 1323 } 1324 break; 1325 1326 case cIfStr: 1327 case cIfTries: 1328 case cMatchStr: 1329 // discard the first parameter 1330 result = NextInt(&labelIndex); 1331 if( result == 0 ) { 1332 result = NextInt(&labelIndex); 1333 if (result == 0) { 1334 labelIndex--; 1335 if ((labelIndex < 0) || (labelIndex >= MAXLABELS)) 1336 result = cclErr_BadLabel; 1337 else { 1338 if( SV.labels[ labelIndex ] == 0) 1339 result = cclErr_LabelUndefined; 1340 } 1341 } 1342 } 1343 break; 1344 1345 case cAsk: 1346 result = NextInt(&labelIndex); // check for a integer maskflag value 1347 if (result == 0) { 1348 SkipBlanks(); 1349 PrepStr(SV.strBuf, 0, 0, 1); // fetch the message Pascal string 1350 1351 // this is the optional third parameter, added for ARA 3.0, for Manual Dialing. 1352 // NextInt() will return cclErr_BadParameter if the third parameter doesn't exist. 1353 1354 if (NextInt(&labelIndex) == 0) { 1355 labelIndex--; 1356 if ((labelIndex < 0) || (labelIndex >= MAXLABELS)) 1357 result = cclErr_BadLabel; 1358 else { 1359 if (SV.labels[labelIndex] == 0) 1360 result = cclErr_LabelUndefined; 1361 } 1362 } 1363 } 1364 break; 1365 1366 default: 1367 break; 1368 } 1369 }/* end WHILE */ 1370 }/* end IF no error */ 1371 return result; 1372} 1373 1374/* -------------------------------------------------------------------------- 1375Advance to the next line of the script. 1376-------------------------------------------------------------------------- */ 1377u_int8_t NextLine() 1378{ 1379 u_int8_t *bp; 1380 short i; 1381 1382 SV.scriptLineIndex = 0; /* reset line index */ 1383 SV.scriptLine++; /* check for out of bounds */ 1384 if ((SV.scriptLine < 1) || (SV.scriptLine > SV.lineCount)) 1385 return 0; 1386 1387 /* update the line ptr */ 1388 i = SV.indexTable[SV.scriptLine-1]; 1389 SV.scriptLinePtr = SV.script + i; 1390 1391 SV.scriptLineSize = 0; 1392 bp = SV.scriptLinePtr; 1393 while ((i++ < SV.scriptSize) && 1394 (*bp != chrCR) && (*bp != chrNL) && 1395 (SV.scriptLineSize < 255)) { 1396 bp++; 1397 SV.scriptLineSize++; 1398 } 1399 return 1; 1400} 1401 1402/* -------------------------------------------------------------------------- 1403-------------------------------------------------------------------------- */ 1404int equalstring(u_int8_t *s1, u_int8_t *s2) 1405{ 1406 int l1 = *s1, l2 = *s2; 1407 1408 s1++; s2++; 1409 while (l1 && l2 && (toupper(*s1) == toupper(*s2))) { 1410 l1--; l2--; 1411 s1++; s2++; 1412 } 1413 return ((l1 == 0) && (l2 == 0)); 1414} 1415 1416 1417/* -------------------------------------------------------------------------- 1418Fetch the next command from the script, and set it up for processing. 1419Return the command number of the command found, or a helpful error 1420code if no command is found 1421-------------------------------------------------------------------------- */ 1422int NextCommand() 1423{ 1424 u_int8_t *bp, *cmdStrPtr, cmdFound; 1425 int cmdIndex, result; 1426 char text[256]; 1427 1428 result = cComment; /* assume a blank line */ 1429 SkipBlanks(); /* skip any initial blanks */ 1430 1431 bp = SV.scriptLinePtr + SV.scriptLineIndex; 1432 cmdStrPtr = &SV.strBuf[1]; 1433 SV.strBuf[0] = 0; 1434 1435 while ((SV.scriptLineIndex < SV.scriptLineSize) 1436 && ((*bp != chrSpace) && (*bp != chrHT))) { 1437 SV.scriptLineIndex++; 1438 SV.strBuf[0]++; 1439 *cmdStrPtr++ = *bp++; 1440 } 1441 bcopy(&SV.strBuf[1], &text[0], SV.strBuf[0]); 1442 text[SV.strBuf[0]] = 0; 1443 // printf("nextcmd = strBuf = %s\n", text); 1444 //syslog(LOG_INFO, "nextcmd = strBuf = %s\n", text); 1445 1446 if (SV.strBuf[0]) { /* search for this command in command list */ 1447 bp = SV.commands + 2; 1448 cmdIndex = cFirstCmd; 1449 result = cNoCmd; /*assume that we don't have a command*/ 1450 cmdFound = 0; 1451 1452 while (cmdIndex <= cLastCmd && !cmdFound) { 1453 if (cmdIndex == cComment && SV.strBuf[1] == bp[1]) { 1454 result = cComment; 1455 cmdFound = 1; 1456 } 1457 if( equalstring(&SV.strBuf[0], bp)) { 1458 result = cmdIndex; 1459 cmdFound = 1; 1460 } 1461 else { 1462 bp += *bp + 1; 1463 cmdIndex++; 1464 } 1465 } 1466 } 1467 1468 return result; 1469} 1470 1471/* -------------------------------------------------------------------------- 1472Parse and prepare srcStr (from srcNdx) and place result in destStr; 1473srcStr and destStr are both pascal strings (Str255). 1474 1475If the string is derived by substituting for a varString, return true 1476in "isVarString" and the string index in "varIndex. 1477-------------------------------------------------------------------------- */ 1478void PrepStr(u_int8_t *destStr, u_int32_t *isVarString, u_int32_t *varIndex, int varSubstitution) 1479{ 1480 u_int8_t *s, *d, *maskPtr; 1481 u_int16_t srcStrIndex; 1482 u_int8_t done, chDelimiter, escChar, dstStrLen, centerDot = '�'; 1483 1484 // 1485 // Assume the string is not a varString. 1486 // 1487 if ( isVarString != NULL ) 1488 *isVarString = 0; 1489 1490 // make sure we don't walk off the end of the line 1491 if (SV.scriptLineIndex > SV.scriptLineSize) { 1492 destStr[0] = '\0'; 1493 return; 1494 } 1495 1496 srcStrIndex = SV.scriptLineIndex; 1497 s = SV.scriptLinePtr + srcStrIndex; 1498 1499 // 1500 // determine string terminators 1501 // default is space, tab, comma, semicolon 1502 // 1503 chDelimiter = 0; 1504 if ((*s == chrDblQuote) || (*s == chrQuote)) { 1505 chDelimiter = *s++; 1506 srcStrIndex++; 1507 } 1508 1509 d = destStr + 1; 1510 dstStrLen = 0; 1511 done = 0; 1512 1513 while ((srcStrIndex < SV.scriptLineSize) && !done) { 1514 1515 // copy & prepare the string 1516 maskPtr = 0; 1517 switch (*s) { 1518 // special delimiters 1519 case chrDblQuote: 1520 case chrQuote: 1521 if (chDelimiter == *s) 1522 done = 1; 1523 else { 1524 srcStrIndex++; 1525 dstStrLen++; 1526 *d++ = *s++; 1527 } 1528 break; 1529 1530 // default delimiters 1531 case chrHT: 1532 case chrSpace: 1533 case chrComma: 1534 case chrSemiColon: 1535 if (!chDelimiter) 1536 done = 1; 1537 else { 1538 srcStrIndex++; 1539 dstStrLen++; 1540 *d++ = *s++; 1541 } 1542 break; 1543 1544 // copy escape character into the destStr 1545 case chrBackSlash: 1546 s++; 1547 if ((*s == chrBackSlash) || (*s == chrCaret)) { 1548 srcStrIndex += 2; 1549 *d++ = *s++; 1550 } 1551 else if (*s == 'x') { 1552 srcStrIndex += 4; 1553 s++; 1554 escChar = ((*s - ((*s <= '9') ? '0' : ('A' - 10))) * 16); 1555 s++; 1556 escChar += (*s - ((*s <= '9') ? '0' : ('A' - 10))); 1557 s++; 1558 *d++ = escChar; 1559 } 1560 else { 1561 srcStrIndex += 3; 1562 escChar = ((*s++ - '0') * 10); 1563 escChar += (*s++ - '0'); 1564 *d++ = escChar; 1565 } 1566 dstStrLen++; 1567 break; 1568 1569 // copy the varString into the destStr 1570 case chrCaret: 1571 { 1572 u_int8_t vs, *vsp; 1573 int i; 1574 1575 if (varSubstitution == 0) { 1576 srcStrIndex++; 1577 dstStrLen++; 1578 *d++ = *s++; 1579 break; 1580 } 1581 1582 s++; 1583 srcStrIndex += 2; 1584 1585 switch (vs = *s++) { 1586 case '*': vs = vsAsk; break; 1587 case 'u': case 'U': 1588 vs = vsUserName; 1589 break; 1590 case 'p': case 'P': vs = vsPassWord; break; 1591 default: { 1592 vs -= '0'; 1593 if (*s >= '0' && *s <= '9') { 1594 srcStrIndex++; 1595 vs = 10 * vs + *s++ - '0'; 1596 } 1597 break; 1598 } 1599 } 1600 1601 if (isVarString) 1602 *isVarString = 1; 1603 1604 if (varIndex) 1605 *varIndex = vs; 1606 1607 if (vsp = GetVarString(vs)) { 1608 if (SV.logMaskOn && vs == (SV.maskStringId - 1) && SV.maskStart) { 1609 maskPtr = d + SV.maskStart - 1; 1610 } 1611 1612 for (i = 1; i <= *vsp; i++, dstStrLen++) 1613 *d++ = vsp[i]; 1614 1615 if (maskPtr) { 1616 // %% centerDot = GetPasswordBulletChar(); 1617 for (i = SV.maskStop - SV.maskStart; i >= 0; i--) 1618 *maskPtr++ = centerDot; 1619 } 1620 } 1621 break; 1622 } 1623 1624 // copy srcStr byte into the destStr 1625 default: 1626 srcStrIndex++; 1627 dstStrLen++; 1628 *d++ = *s++; 1629 break; 1630 } 1631 } 1632 1633 *destStr = dstStrLen; // pascal string - set length 1634 SV.scriptLineIndex = srcStrIndex + 1; // skip over the string terminator 1635 1636} 1637 1638/* -------------------------------------------------------------------------- 1639parse a string and substitute variable with actual values 1640-------------------------------------------------------------------------- */ 1641void varSubstitution(u_int8_t *src, u_int8_t *dst, int dstmaxlen) 1642{ 1643 u_int8_t *s = src + 1, *d = dst + 1; 1644 u_int16_t srclen = src[0]; 1645 u_int8_t len = 0, dstlen = 0; 1646 1647 while ((len < srclen) && (dstlen < dstmaxlen)) { 1648 // copy & prepare the string 1649 switch (*s) { 1650 1651 // copy the varString into the destStr 1652 case chrCaret: 1653 { 1654 u_int8_t vs, *vsp; 1655 int i; 1656 1657 s++; 1658 len += 2; 1659 1660 switch (vs = *s++) { 1661 case '*': vs = vsAsk; break; 1662 case 'u': case 'U': vs = vsUserName; break; 1663 case 'p': case 'P': vs = vsPassWord; break; 1664 default: { 1665 vs -= '0'; 1666 if (*s >= '0' && *s <= '9') { 1667 len++; 1668 vs = 10 * vs + *s++ - '0'; 1669 } 1670 break; 1671 } 1672 } 1673 if (vsp = GetVarString(vs)) { 1674 for (i = 1; (i <= *vsp) && (dstlen < dstmaxlen); i++, dstlen++) 1675 *d++ = vsp[i]; 1676 } 1677 break; 1678 } 1679 1680 // copy srcStr byte into the dst 1681 default: 1682 len++; 1683 dstlen++; 1684 *d++ = *s++; 1685 break; 1686 } 1687 } 1688 1689 *dst = dstlen; // pascal string - set length 1690} 1691 1692/* -------------------------------------------------------------------------- 1693Skip over white space (spaces and tabs) 1694-------------------------------------------------------------------------- */ 1695void SkipBlanks() 1696{ 1697 u_int8_t *s; 1698 1699 s = SV.scriptLinePtr + SV.scriptLineIndex; 1700 while ((SV.scriptLineIndex < SV.scriptLineSize) 1701 && ((*s == chrSpace) || (*s == chrHT))) { 1702 s++; 1703 SV.scriptLineIndex++; 1704 } 1705} 1706 1707/* -------------------------------------------------------------------------- 1708Read the next text from the command line and convert the ascii to 1709an int, and set theIntPtr to the result. 1710If there is no int to be read, return cclErr_BadParameter. 1711If everything is OK, return noErr 1712-------------------------------------------------------------------------- */ 1713int NextInt(u_int32_t *theIntPtr) 1714{ 1715 int sign; 1716 u_int8_t *intStrPtr, intStrNdx, intStrLen; 1717 u_int8_t intStr[256]; 1718 u_int32_t isVarString = 0; 1719 1720 /* skip over blanks in script line, and pull out the number string */ 1721 SkipBlanks(); 1722 PrepStr( intStr, &isVarString, NULL, 1); 1723 1724 *theIntPtr = 0; 1725 sign = 1; 1726 intStrLen = *intStr; 1727 intStrPtr = intStr + 1; 1728 if( intStrLen == 0 ) 1729 return cclErr_BadParameter; 1730 // if it's a variable that is null (gNullString is a pstring = " "), 1731 // substitute zero instead of failing later 1732 if (isVarString && (intStrLen == 1) && *intStrPtr == ' ') 1733 *intStrPtr = '0'; 1734 1735 /* check for sign operator */ 1736 intStrNdx = 1; 1737 if (*intStrPtr == '-') { 1738 sign = -1; 1739 intStrNdx++; 1740 intStrPtr++; 1741 } 1742 if (intStrNdx > intStrLen) 1743 return cclErr_BadParameter; 1744 1745 /* parse out the number */ 1746 while ((intStrNdx++ <= intStrLen) && (*intStrPtr >= '0') && (*intStrPtr <= '9')) 1747 *theIntPtr = (*theIntPtr * 10) + (*intStrPtr++ - '0'); 1748 1749 if (intStrPtr == (intStr + 1)) // the string did not contain numbers 1750 return cclErr_BadParameter; 1751 1752 *theIntPtr *= sign; // set appropriate sign 1753 return 0; 1754} 1755 1756/* -------------------------------------------------------------------------- 1757Run the script, one line at a time, by reading the command and 1758dispatching to the proper routine. This continues until some command 1759defers the script execution. This could be due to encountering 1760the end of the script, or waiting for an asynchronous command to complete. 1761-------------------------------------------------------------------------- */ 1762void RunScript() 1763{ 1764 u_int8_t running, cmd; 1765 int code, result = 0; 1766 u_int32_t i, lval, jumpLabel; 1767 1768 if (SV.scriptLine == 0) { // no entry point, so terminate the script. 1769 SV.ctlFlags &= ~cclPlaying; // don't play this script. 1770 SV.theAbortErr = cclErr_EndOfScriptErr; // for WrapScript()->ScriptComplete(). 1771 terminate(cclErr_EndOfScriptErr); // a script syntax error was detected 1772 return; 1773 } 1774 1775 SV.ctlFlags |= cclPlaying; // turn on the cclPlaying flag 1776 running = 1; // keep running until SerReset or other interruption 1777 while (running) { // find the next script line 1778 if (!NextLine()) { // no more lines in the script, so we've hit the end without an exit. 1779 1780 SV.ctlFlags &= ~cclPlaying; // don't play this script. 1781 SV.theAbortErr = cclErr_EndOfScriptErr; // for WrapScript()->ScriptComplete(). 1782 terminate(cclErr_EndOfScriptErr); // a script syntax error was detected 1783 return; 1784 } 1785 1786 /* process script line */ 1787 cmd = NextCommand(); // get the next CCL Command from the script. 1788 switch (cmd) { 1789 case cAsk: 1790 running = !Ask(); // wait until User data arrives in ReceiveDataFromAbove(). 1791 break; 1792 1793 case cChrDelay: 1794 NextInt( &i ); // delay is specified in tenths of a second 1795 // if (i > 0) // CA : Why is it impossible to reset delay ? 1796 SV.chrDelayValue = i * 100; 1797 break; 1798 1799 case cCommunicatingAt: 1800 CommunicatingAt(); // tell upstairs what our modulation speed is 1801 break; 1802 1803 case cDecTries: 1804 SV.loopCounter--; // decrement loop counter 1805 break; 1806 1807 case cDTRClear: 1808 DTRCommand(DTR_CLEAR); // Issue the DTR Clear command 1809 break; 1810 1811 case cDTRSet: 1812 DTRCommand(DTR_SET); // Issue the DTR Set command 1813 break; 1814 1815 case cExit: 1816 running = 0; 1817 SV.ctlFlags &= ~cclPlaying; 1818 1819 NextInt((u_int32_t*)&code); // fetch the Exit code 1820 SV.theAbortErr = code; // not currently used 1821 1822 Note(); // handle optional string 1823 1824 terminate(code); // send an ARA_Notify message upstream. 1825 break; 1826 1827 case cFlush: 1828 Flush(); 1829 break; 1830 1831 case cHSReset: 1832 HSReset(); // Configure the serial driver handshake options 1833 break; 1834 1835 case cIfAnswer: 1836 if (SV.ctlFlags & cclAnswerMode) { 1837 NextInt(&i); 1838 SV.scriptLine = SV.labels[i - 1]; 1839 } 1840 break; 1841 1842 case cIfOriginate: 1843 if (SV.ctlFlags & cclOriginateMode) { 1844 NextInt(&i); 1845 SV.scriptLine = SV.labels[i - 1]; 1846 } 1847 break; 1848 1849 case cIfStr: 1850 // IfStr() will return either 0 if the comparison fails, 1851 // or the label to jump to if the comparison succeeds. 1852 jumpLabel = IfStr(); 1853 if (jumpLabel > 0) 1854 SV.scriptLine = jumpLabel; 1855 break; 1856 1857 case cIfTries: 1858 NextInt(&i); 1859 if (SV.loopCounter >= i) { 1860 NextInt(&i); 1861 SV.scriptLine = SV.labels[i - 1]; 1862 } 1863 break; 1864 1865 case cIncTries: 1866 SV.loopCounter++; // increment the loop counter 1867 break; 1868 1869 case cJump: 1870 NextInt(&i); 1871 SV.scriptLine = SV.labels[i - 1]; 1872 break; 1873 1874 case cJSR: 1875 if (SV.topOfStack == 0) { 1876 running = 0; 1877 SV.ctlFlags &= ~cclPlaying; 1878 SV.theAbortErr = cclErr_SubroutineOverFlow; // for WrapScript()->ScriptComplete(). 1879 terminate(cclErr_SubroutineOverFlow); 1880 } 1881 else { 1882 SV.stack[--SV.topOfStack] = SV.scriptLine; // save return line 1883 NextInt(&i); 1884 SV.scriptLine = SV.labels[i - 1]; 1885 } 1886 break; 1887 1888 case cLogMsg: // undocumented. use cNote. KW 1889 case cNote: 1890 Note(); // post a Note to the Status window and/or Log File. 1891 break; 1892 1893 case cMatchClr: 1894 MatchClr(); // clear the match buffer. 1895 break; 1896 1897 case cMatchRead: 1898 SV.ctlFlags |= cclMatchPending; // any Serial data is for CCL. 1899 for(i = 0; i < maxMatch; i++) { // reset match string indices 1900 SV.matchStr[i].matchStrIndex = 0; 1901 SV.matchStr[i].inVarStr = 0; 1902 } 1903 1904 NextInt(&i); // get the timeout value 1905 if( i > 0 ) { 1906 // post read to serial driver and set timer: 1907 running = 0; // stop running script til match or timeout 1908 ScheduleTimer(kMatchReadTimer, i * 100); 1909 StartRead(); 1910 } 1911 break; 1912 1913 case cMatchStr: 1914 result = MatchStr(); // add a string to the match buffer 1915 if (result) { // bad command and/or matchstr index 1916 running = 0; 1917 SV.ctlFlags &= ~cclPlaying; 1918 SV.theAbortErr = result; // for WrapScript()->ScriptComplete(). 1919 terminate(result); 1920 } 1921 break; 1922 1923 case cPause: 1924 NextInt(&i); 1925 if (i > 0) { 1926 lval = SV.pauseTimer = i; 1927 lval *= 100; // get it in milliseconds 1928 usleep(lval * 1000); 1929 } 1930 break; 1931 1932 case cReturn: 1933 if (SV.topOfStack == cclNestingLimit) { 1934 running = 0; 1935 SV.ctlFlags &= ~cclPlaying; 1936 SV.theAbortErr = cclErr_SubroutineOverFlow; // for WrapScript()->ScriptComplete(). 1937 terminate(cclErr_SubroutineOverFlow); 1938 } 1939 else 1940 SV.scriptLine = SV.stack[SV.topOfStack++]; 1941 break; 1942 1943 case cLBreak: 1944 Break(LONGBREAK); 1945 break; 1946 1947 case cSBreak: 1948 Break(SHORTBREAK); 1949 break; 1950 1951 case cMonitorLine: 1952 MonitorLine(); 1953 break; 1954 1955 case cDebugLevel: 1956 DebugLevel(); 1957 break; 1958 1959 case cSerReset: 1960 SerReset(); 1961 break; 1962 1963 case cSetSpeed: 1964 SetSpeed(); 1965 break; 1966 1967 case cSetTries: 1968 NextInt(&i); 1969 SV.loopCounter = i; 1970 break; 1971 1972 case cUserHook: 1973 UserHook(); // get an event and pass it up to the Client. 1974 break; 1975 1976 case cWrite: 1977 running = Write(); 1978 break; 1979 1980 default: // "!", "@CCLSCRIPT", "@ORIGINATE", "@ANSWER", "@HANGUP", "@LABEL" (cmd 1-6). 1981 { 1982 break; 1983 } 1984 1985 }/* end SWITCH */ 1986 }/* end WHILE running */ 1987} 1988 1989/* -------------------------------------------------------------------------- 1990Copy the match string into the approptiate MatchStr variable 1991-------------------------------------------------------------------------- */ 1992int MatchStr() 1993{ 1994 u_int32_t matchIndex, i; 1995 TPMatchStrInfo matchInfo; 1996 int result; 1997 1998 result = NextInt(&matchIndex); // Get the matchStr index 1999 if (result == 0) { 2000 matchIndex--; 2001 if ((matchIndex < 0) || (matchIndex >= maxMatch)) { // index out of bounds, abort the script play 2002 return cclErr_MatchStrIndxErr; 2003 } 2004 2005 matchInfo = &SV.matchStr[matchIndex]; 2006 // get script line index from specified label 2007 result = NextInt(&i); 2008 if (result == 0) { 2009 matchInfo->matchLine = SV.labels[i - 1]; 2010 2011 SkipBlanks(); 2012 2013 /* determine string terminators - default is space, tab, comma, semicolon */ 2014 if ((SV.scriptLinePtr[SV.scriptLineIndex] == chrDblQuote) 2015 || (SV.scriptLinePtr[SV.scriptLineIndex] == chrQuote)) { 2016 matchInfo->delimiterChar = SV.scriptLinePtr[SV.scriptLineIndex]; 2017 SV.scriptLineIndex++; 2018 } 2019 else 2020 matchInfo->delimiterChar = 0; 2021 matchInfo->matchStr = &SV.scriptLinePtr[SV.scriptLineIndex]; 2022 } 2023 } 2024 2025 return result; 2026} 2027 2028/* -------------------------------------------------------------------------- 2029Clear all the match strings 2030-------------------------------------------------------------------------- */ 2031void MatchClr() 2032{ 2033 int matchIndex; 2034 TPMatchStrInfo matchInfo; 2035 2036 for (matchIndex = 0; matchIndex < maxMatch; matchIndex++) { 2037 matchInfo = &SV.matchStr[matchIndex]; 2038 matchInfo->matchStr = 0; 2039 matchInfo->matchStrIndex = 0; 2040 matchInfo->delimiterChar = 0; 2041 matchInfo->varStr = 0; 2042 matchInfo->inVarStr = 0; 2043 matchInfo->varStrIndex = 0; 2044 matchInfo->matchLine = 0; 2045 } 2046} 2047 2048/* -------------------------------------------------------------------------- 2049Search each match string, at it's current index, for a match with 2050newChar. If newChar matches, andvance the strings index and see if 2051if the whole string has been matched. Else, reset the match string's 2052index. Returns which matchstring matched, if any 2053-------------------------------------------------------------------------- */ 2054int MatchFind(u_int8_t newChar) 2055{ 2056 int i, matchFound; 2057 TPMatchStrInfo matchInfo = NULL; 2058 char text[256]; 2059 u_int8_t matchStrChar, c; 2060 2061 matchFound = 0; // assume no match found 2062 newChar &= 0x7f; // some PADs have bogus high bit 2063 for (i = 0; i < maxMatch && !matchFound; i++) { 2064 matchInfo = &SV.matchStr[i]; 2065 if (matchInfo->matchStr) { // we have a matchStr, so test it 2066 if (matchInfo->inVarStr) { 2067 matchStrChar = matchInfo->varStr[matchInfo->varStrIndex]; 2068 matchInfo->varStrIndex++; 2069 if (matchStrChar == chrBackSlash) { 2070 matchStrChar = matchInfo->varStr[matchInfo->varStrIndex]; 2071 matchInfo->varStrIndex++; 2072 if ((matchStrChar != chrBackSlash) 2073 && (matchStrChar != chrCaret)) { 2074 if (matchStrChar == 'x') { 2075 c = matchInfo->matchStr[matchInfo->varStrIndex++]; 2076 matchStrChar = ((c - ((c <= '9') ? '0' : ('A' - 10))) * 16); 2077 c = matchInfo->matchStr[matchInfo->varStrIndex++]; 2078 matchStrChar += (c - ((c <= '9') ? '0' : ('A' - 10))); 2079 } 2080 else { 2081 matchStrChar = (matchStrChar - 0x30) * 10; 2082 matchStrChar += matchInfo->varStr[matchInfo->varStrIndex] - 0x30; 2083 matchInfo->varStrIndex++; 2084 } 2085 } 2086 } 2087 if (matchInfo->varStrIndex == (matchInfo->varStrSize + 1)) 2088 matchInfo->inVarStr = 0; 2089 } 2090 else { 2091 matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex]; 2092 matchInfo->matchStrIndex++; // move index to next char 2093 if (matchStrChar == chrBackSlash) { 2094 matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex]; 2095 matchInfo->matchStrIndex++; // update index for next char 2096 if ((matchStrChar != chrBackSlash) 2097 && (matchStrChar != chrCaret)) { 2098 if (matchStrChar == 'x') { 2099 c = matchInfo->matchStr[matchInfo->matchStrIndex++]; 2100 matchStrChar = ((c - ((c <= '9') ? '0' : ('A' - 10))) * 16); 2101 c = matchInfo->matchStr[matchInfo->matchStrIndex++]; 2102 matchStrChar += (c - ((c <= '9') ? '0' : ('A' - 10))); 2103 } 2104 else { 2105 matchStrChar = (matchStrChar - 0x30) * 10; 2106 matchStrChar += matchInfo->matchStr[matchInfo->matchStrIndex] - 0x30; 2107 matchInfo->matchStrIndex++; 2108 } 2109 } 2110 } 2111 else if (matchStrChar == chrCaret) { 2112 u_int8_t vs; 2113 2114 matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex]; 2115 matchInfo->matchStrIndex++; // skip past var string index 2116 if (matchStrChar == '*') 2117 vs = vsAsk; 2118 else if (matchStrChar == 'u' || matchStrChar == 'U') 2119 vs = vsUserName; 2120 else if (matchStrChar == 'p' || matchStrChar == 'P') 2121 vs = vsPassWord; 2122 else { 2123 vs = matchStrChar - '0'; 2124 matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex]; 2125 if (matchStrChar >= '0' && matchStrChar <= '9') { 2126 matchInfo->matchStrIndex++; // skip past var string index 2127 vs = 10 * vs + matchStrChar - '0'; 2128 } 2129 } 2130 matchInfo->varStr = GetVarString(vs); 2131 if (matchInfo->varStr) { 2132 matchStrChar = matchInfo->varStr[1]; 2133 matchInfo->varStrIndex = 2; 2134 matchInfo->inVarStr = 1; 2135 matchInfo->varStrSize = matchInfo->varStr[0]; 2136 if( (matchInfo->varStrSize + 1) == matchInfo->varStrIndex) 2137 // we are done with the var string, clear inVarStr flag 2138 matchInfo->inVarStr = 0; 2139 } 2140 else { 2141 matchStrChar = matchInfo->matchStr[matchInfo->matchStrIndex]; 2142 matchInfo->matchStrIndex++; 2143 } 2144 } 2145 } 2146 2147 //printf("MatchFind, expect = %d, newchar = %d\n", matchStrChar, newChar); 2148 //syslog(LOG_INFO, "MatchFind, expect = 0x%x '%c', newchar = 0x%x '%c'\n", matchStrChar, matchStrChar, newChar, newChar); 2149 if (newChar == matchStrChar) { // check to see if whole string matched 2150 if (!matchInfo->inVarStr) 2151 switch (matchInfo->matchStr[ matchInfo->matchStrIndex ]) { 2152 // special delimiters 2153 case chrDblQuote: 2154 case chrQuote: 2155 if (matchInfo->delimiterChar) 2156 matchFound = i + 1; 2157 break; 2158 2159 // default delimiters 2160 case chrHT: 2161 case chrSpace: 2162 case chrComma: 2163 case chrSemiColon: 2164 if (!matchInfo->delimiterChar) 2165 matchFound = i + 1; 2166 break; 2167 2168 case chrCR: 2169 case chrNL: 2170 matchFound = i + 1; 2171 break; 2172 }/* end SWITCH */ 2173 } 2174 else { // newChar does not match, reset matchInfo 2175 matchInfo->matchStrIndex = 0; 2176 matchInfo->inVarStr = 0; 2177 matchInfo->varStr = 0; 2178 } 2179 } 2180 } 2181 2182 if (matchFound) { 2183 int x; 2184 2185 if (matchInfo->inVarStr) { 2186 for (x = 0; x < matchInfo->varStrIndex; x++) 2187 VerboseBuffer[x+1] = matchInfo->varStr[x]; 2188 VerboseBuffer[0] = x; 2189 } 2190 else { 2191 for (x = 0; x < matchInfo->matchStrIndex; x++) 2192 VerboseBuffer[x+1] = matchInfo->matchStr[x]; 2193 VerboseBuffer[0] = x; 2194 } 2195 2196 // 2197 // Use the kUserMsgFAny flag. That allows the message 2198 // resource to determine the destination and log level. 2199 // 2200 2201 bcopy(&VerboseBuffer[1], &text[0], VerboseBuffer[0]); 2202 text[VerboseBuffer[0]] = 0; 2203 if (verbose) { 2204 sLog("CCLMatched : %s", text); 2205 } 2206 } 2207 2208 return matchFound; 2209} 2210 2211/* -------------------------------------------------------------------------- 2212-------------------------------------------------------------------------- */ 2213void Break(u_int32_t len) 2214{ 2215 /* 2216 Man pages : 2217 The tcsendbreak function transmits a continuous stream of zero-valued 2218 bits for four-tenths of a second to the terminal referenced by fd. The 2219 len parameter is ignored in this implementation. */ 2220 /* we need to fix the question of Short break vs. Long Break */ 2221 2222 tcsendbreak(outfd, len); 2223} 2224 2225/* -------------------------------------------------------------------------- 2226A macro to return a Boolean result for a given baud number. Only those 2227listed are valid 2228-------------------------------------------------------------------------- */ 2229#define IsValidBaudRate(a) \ 2230 ( ((a) == 300) || \ 2231 ((a) == 600) || \ 2232 ((a) == 1200) || \ 2233 ((a) == 1800) || \ 2234 ((a) == 2400) || \ 2235 ((a) == 3600) || \ 2236 ((a) == 4800) || \ 2237 ((a) == 7200) || \ 2238 ((a) == 9600) || \ 2239 ((a) == 14400) || \ 2240 ((a) == 19200) || \ 2241 ((a) == 28800) || \ 2242 ((a) == 38400) || \ 2243 ((a) == 57600) || \ 2244 ((a) == 115200) || \ 2245 ((a) == 230400) || \ 2246 ((a) == 460800) || \ 2247 ((a) == 921600)) 2248 2249/* -------------------------------------------------------------------------- 2250Reset the Serial driver transmission rate, parity, number of data bits, 2251and number of stop bits as specified on the command line 2252-------------------------------------------------------------------------- */ 2253void SerReset(void) 2254{ 2255 struct termios tios; 2256 u_int32_t temp; 2257 2258 if (tcgetattr(infd, &tios) < 0) 2259 return; 2260 2261 // Get the baud rate from the script and filter out invalid values. 2262 NextInt(&temp); 2263 SV.serialSpeed = IsValidBaudRate(temp) ? temp : 9600; 2264 // sLog("SerReset: setting serial speed to %d", SV.serialSpeed); 2265 cfsetispeed(&tios, SV.serialSpeed); 2266 cfsetospeed(&tios, SV.serialSpeed); 2267 2268 // Get the parity setting from the script; 2269 NextInt(&temp); 2270 switch (temp) { 2271 case 1: 2272 tios.c_cflag |= PARENB + PARODD; // parity enable + odd parity 2273 break; 2274 case 2: 2275 tios.c_cflag |= PARENB; 2276 tios.c_cflag &= ~PARODD; // parity enable + even parity 2277 break; 2278 default: 2279 tios.c_cflag &= ~(PARENB + PARODD); // parity enable + even parity 2280 break; 2281 } 2282 2283 // Get the data bits setting from the script. 2284 tios.c_cflag &= ~CSIZE; // parity enable + even parity 2285 NextInt(&temp); 2286 switch (temp) { 2287 case 5: 2288 tios.c_cflag |= CS5; // 5 data bits 2289 break; 2290 case 6: 2291 tios.c_cflag |= CS6; // 6 data bits 2292 break; 2293 case 7: 2294 tios.c_cflag |= CS7; // 7 data bits 2295 break; 2296 default: 2297 tios.c_cflag |= CS8; // 8 data bits 2298 } 2299 2300 // Get the stop bits setting... 2301 NextInt(&temp); 2302 switch (temp) { 2303 case 2: 2304 tios.c_cflag |= CSTOPB; // 2 stop bits 2305 break; 2306 default: 2307 tios.c_cflag &= ~CSTOPB; // 1 stop bit 2308 break; 2309 } 2310 2311 // set the same settings for inout and output fd 2312 tcsetattr(infd, TCSAFLUSH, &tios); 2313 tcsetattr(outfd, TCSAFLUSH, &tios); 2314} 2315 2316/* -------------------------------------------------------------------------- 2317Set the CLOCAL flag to receive or not SIGHUP when the line drops 2318-------------------------------------------------------------------------- */ 2319void MonitorLine() 2320{ 2321 struct termios tios; 2322 u_int32_t temp; 2323 2324 if (tcgetattr(infd, &tios) < 0) 2325 return; 2326 2327 // get the value. 2328 // bit mask : 1st flag -> on/off line drop 2329 NextInt(&temp); 2330 if (temp) 2331 tios.c_cflag &= ~CLOCAL; 2332 else 2333 tios.c_cflag |= CLOCAL; 2334 2335 // set the same settings for inout and output fd 2336 tcsetattr(infd, TCSAFLUSH, &tios); 2337 tcsetattr(outfd, TCSAFLUSH, &tios); 2338} 2339 2340/* -------------------------------------------------------------------------- 2341Set the debug level for the script 2342-------------------------------------------------------------------------- */ 2343void DebugLevel() 2344{ 2345 u_int32_t temp; 2346 2347 // get the value. 2348 // bit mask : 1st flag -> print incoming data 2349 NextInt(&temp); 2350 2351 debuglevel = temp; 2352} 2353 2354/* -------------------------------------------------------------------------- 2355* Get the serial speed from the CCL script and send it down as an 2356* XTI option to the layer below. 2357* 2358* Called by TCCLScript::RunScript(). 2359* 2360* NOTE on OT handling of speeds: 2361* For speeds that return false from the IsValidBaudRate macro, the OT 2362* serial module will usually return T_PARTSUCCESS and return the value 2363* it likes closest to the requested value. For OT 1.1 the exceptions are: 2364* 14400: sets rate to 19,200, returns T_PARTSUCCESS 2365* any speed btw 100k and 230k: sets rate to 19,200, returns T_FAILURE 2366* 230,400: T_SUCCESS 2367* any speed other speed btw 230k and 235,930, sets rate to 230,400 and 2368* returns T_PARTSUCCESS 2369* any speed > 235,930, set rate to 19,200 and returns T_FAILURE 2370-------------------------------------------------------------------------- */ 2371void SetSpeed(void) 2372{ 2373 u_int32_t temp; 2374 struct termios tios; 2375 2376 NextInt(&temp); 2377 SV.serialSpeed = IsValidBaudRate(temp) ? temp : 2400; 2378 2379 if (tcgetattr(infd, &tios) >= 0) { 2380 cfsetispeed(&tios, SV.serialSpeed); 2381 cfsetospeed(&tios, SV.serialSpeed); 2382 tcsetattr(infd, TCSAFLUSH, &tios); 2383 tcsetattr(outfd, TCSAFLUSH, &tios); 2384 } 2385} 2386 2387/* ----------------------------------------------------------------------------- 2388----------------------------------------------------------------------------- */ 2389CFStringRef copyUserLocalizedString(CFBundleRef bundle, 2390 CFStringRef key, CFStringRef value, CFArrayRef userLanguages) 2391{ 2392 CFStringRef result = NULL, errStr= NULL; 2393 CFDictionaryRef stringTable; 2394 CFDataRef tableData; 2395 SInt32 errCode; 2396 CFURLRef tableURL; 2397 CFArrayRef locArray, prefArray; 2398 2399 if (userLanguages == NULL) 2400 return CFBundleCopyLocalizedString(bundle, key, value, NULL); 2401 2402 if (key == NULL) 2403 return (value ? CFRetain(value) : CFRetain(CFSTR(""))); 2404 2405 locArray = CFBundleCopyBundleLocalizations(bundle); 2406 if (locArray) { 2407 prefArray = CFBundleCopyLocalizationsForPreferences(locArray, userLanguages); 2408 if (prefArray) { 2409 if (CFArrayGetCount(prefArray)) { 2410 tableURL = CFBundleCopyResourceURLForLocalization(bundle, CFSTR("Localizable"), CFSTR("strings"), NULL, 2411 CFArrayGetValueAtIndex(prefArray, 0)); 2412 if (tableURL) { 2413 if (CFURLCreateDataAndPropertiesFromResource(NULL, tableURL, &tableData, NULL, NULL, &errCode)) { 2414 stringTable = CFPropertyListCreateFromXMLData(NULL, tableData, kCFPropertyListImmutable, &errStr); 2415 if (errStr) 2416 CFRelease(errStr); 2417 if (stringTable) { 2418 result = CFDictionaryGetValue(stringTable, key); 2419 if (result) 2420 CFRetain(result); 2421 CFRelease(stringTable); 2422 } 2423 CFRelease(tableData); 2424 } 2425 CFRelease(tableURL); 2426 } 2427 } 2428 CFRelease(prefArray); 2429 } 2430 CFRelease(locArray); 2431 } 2432 2433 if (result == NULL) 2434 result = (value && !CFEqual(value, CFSTR(""))) ? CFRetain(value) : CFRetain(key); 2435 2436 return result; 2437} 2438 2439/* -------------------------------------------------------------------------- 2440-------------------------------------------------------------------------- */ 2441bool localizeStringWithBundle(u_char* inString, u_char* outString, CFIndex outSize, CFURLRef curURL) 2442{ 2443 bool retVal= false; 2444 if(curURL && inString && outString) 2445 { 2446 CFStringRef ref, loggedInUser, msg; 2447 CFPropertyListRef langRef; 2448 CFBundleRef bdl; 2449 2450 loggedInUser = SCDynamicStoreCopyConsoleUser(NULL, 0, 0); 2451 if (loggedInUser) 2452 { 2453 CFPreferencesSynchronize(kCFPreferencesAnyApplication, loggedInUser, kCFPreferencesAnyHost); 2454 langRef = CFPreferencesCopyValue(CFSTR("AppleLanguages"), kCFPreferencesAnyApplication, 2455 loggedInUser, kCFPreferencesAnyHost); 2456 if (langRef) 2457 { 2458 ref = CFStringCreateWithPascalString(NULL, inString, kCFStringEncodingUTF8); 2459 if (ref) 2460 { 2461 bdl = CFBundleCreate(0, curURL); 2462 if (bdl) 2463 { 2464 msg = copyUserLocalizedString(bdl, ref, ref, langRef); 2465 if (msg) 2466 { 2467 retVal= CFStringGetPascalString(msg, outString, outSize, kCFStringEncodingUTF8); 2468 CFRelease(msg); 2469 } 2470 CFRelease(bdl); 2471 } 2472 CFRelease(ref); 2473 } 2474 CFRelease(langRef); 2475 } 2476 CFRelease(loggedInUser); 2477 } 2478 } 2479 return retVal; 2480} 2481 2482/* -------------------------------------------------------------------------- 2483-------------------------------------------------------------------------- */ 2484void localizeString(u_char* inString, u_char* outString, CFIndex outSize) 2485{ 2486 bool success = true; 2487 outString[0]= 0; 2488 2489 if(!localizeStringWithBundle(inString, outString, outSize, cclBundleURL)) 2490 { 2491 if(!localizeStringWithBundle(inString, outString, outSize, appBundleURL)) 2492 { 2493 success = localizeStringWithBundle(inString, outString, outSize, localBundleURL); 2494 } 2495 } 2496 2497 // if we couldn't localize, just copy the non-localized version 2498 // (the routines above used to do that, but they're busted) 2499 if (!success) 2500 bcopy(inString, outString, inString[0]+1); 2501} 2502 2503#pragma mark - 2504 2505/* -------------------------------------------------------------------------- 2506pass a string up to the Client, along with where it should be displayed 2507-------------------------------------------------------------------------- */ 2508void Note() 2509{ 2510 u_char text[256]; 2511 u_char localText[256]; 2512 u_int32_t msgDestination, msgLevel = 0; // code -> destination 2513 CFStringRef ref; 2514 2515 SkipBlanks(); 2516 /* first get the string, do not perform variable substitution */ 2517 PrepStr(text, 0, 0, 0); 2518 /* then get the localized version of the string and perform variable substitution */ 2519 localizeString(text, localText, 256); 2520 2521 varSubstitution(localText, SV.strBuf, sizeof(SV.strBuf)); 2522 2523 NextInt(&msgLevel); // get the destination, if present 2524 switch (msgLevel) { 2525 case 1: 2526 msgDestination = kUserMsgFLog; 2527 break; 2528 case 2: 2529 default: 2530 msgDestination = kUserMsgFStatus; 2531 break; 2532 case 3: 2533 msgDestination = kUserMsgFLog | kUserMsgFStatus; 2534 break; 2535 } 2536 2537 // localText switching from P-string to C-string 2538 bcopy(&SV.strBuf[1], &localText[0], SV.strBuf[0]); 2539 localText[SV.strBuf[0]] = '\0'; 2540 2541 if (serviceID && (msgDestination & 2)) { 2542 ref = CFStringCreateWithCString(NULL, (char*) localText, kCFStringEncodingUTF8); 2543 if (ref) { 2544 publish_entry(serviceID, kSCPropNetModemNote, ref); 2545 CFRelease(ref); 2546 } 2547 } 2548 2549 if (msgDestination & 1) { 2550 sLog("%s", localText); 2551 } 2552} 2553 2554 2555/* -------------------------------------------------------------------------- 2556Returns "true" if entire buffer is written with character delays. Returns 2557"false" if write processing continues in WriteContinue because character 2558delays are being inserted 2559-------------------------------------------------------------------------- */ 2560u_int8_t Write() 2561{ 2562 u_int32_t isVarString; 2563 u_int16_t i, j; 2564 u_int32_t varIndex; 2565 char text[256]; 2566 2567 SkipBlanks(); 2568 PrepStr(SV.strBuf, &isVarString, &varIndex, 1); 2569 2570 if (isVarString && (varIndex == vsPassWord || ((varIndex == vsAsk) && LastAskedMasked))) { 2571 VerboseBuffer[0] = SV.strBuf[0]; 2572 for (i = 1; i <= SV.strBuf[0]; i++) 2573 VerboseBuffer[i] = '�'; 2574 } 2575 else { 2576 for (i = 1, j = 1; i <= SV.strBuf[0] && j < 256; i++) { 2577 u_int8_t c = SV.strBuf[i]; 2578 if (c < 0x20) { 2579 VerboseBuffer[j++] = '\\'; 2580 VerboseBuffer[j++] = '0' + c / 10; 2581 VerboseBuffer[j++] = '0' + c % 10; 2582 } 2583 else { 2584 VerboseBuffer[j++] = c; 2585 } 2586 } 2587 VerboseBuffer[0] = j - 1; 2588 } 2589 2590 bcopy(&VerboseBuffer[1], &text[0], VerboseBuffer[0]); 2591 text[VerboseBuffer[0]] = 0; 2592 if (verbose) { 2593 sLog("CCLWrite : %s", text); 2594 } 2595 2596 // 2597 // skip the pascal string length byte 2598 // 2599 SV.writeBufIndex = 1; 2600 2601 if (SV.chrDelayValue) { 2602 // complete asynchronously through WriteContinue 2603 WriteContinue(); 2604 ScheduleTimer(kCharDelayTimer, SV.chrDelayValue); 2605 return 0; 2606 } 2607 else { 2608 2609 write(outfd, &SV.strBuf[1], SV.strBuf[0]); 2610 if(signalerror) 2611 terminate(signalerror); 2612 return 1; 2613 } 2614} 2615 2616/* -------------------------------------------------------------------------- 2617Continue the current write command. This routine is called when 2618the char delay timer expires 2619-------------------------------------------------------------------------- */ 2620void WriteContinue() 2621{ 2622 2623 //syslog(LOG_INFO, " ----> delayed '%c'\n", SV.strBuf[SV.writeBufIndex]); 2624 2625 write(outfd, &SV.strBuf[SV.writeBufIndex], 1); 2626 if(signalerror) 2627 terminate(signalerror); 2628 if (SV.writeBufIndex < SV.strBuf[0] ) // if this char is not the last char 2629 SV.writeBufIndex++; // bump index to the next char 2630 2631 return; 2632} 2633 2634/* -------------------------------------------------------------------------- 2635the timer scheduled by fScript -> ScriptStartTimer() has expired. 2636only one timer can be active at a time. the timer type specified in 2637ScriptStartTimer is fed into TimerExpired as the long parameter 2638-------------------------------------------------------------------------- */ 2639void TimerExpired(long type) 2640{ 2641 2642 if (SV.ctlFlags & cclPlaying) { 2643 switch (type) { 2644 2645 case kMatchReadTimer: 2646 StopRead(); 2647 RunScript(); // go get the next ccl cmd 2648 break; 2649 2650 case kCharDelayTimer: 2651 if (SV.writeBufIndex == SV.strBuf[0] ) { // this is the last char to write 2652 WriteContinue(); // write the char 2653 RunScript(); // go get the next ccl cmd 2654 } 2655 else { 2656 if (SV.writeBufIndex < SV.strBuf[0]) { // this char is not the last char 2657 WriteContinue(); // write the char 2658 ScheduleTimer(kCharDelayTimer, SV.chrDelayValue); 2659 } 2660 } 2661 2662 break; 2663 } 2664 } 2665} 2666 2667/* -------------------------------------------------------------------------- 2668-------------------------------------------------------------------------- */ 2669u_int8_t Ask() 2670{ 2671 u_int32_t maskflag, flags = 0, label; 2672 CFStringRef ref, resp; 2673 CFUserNotificationRef alert; 2674 CFOptionFlags alertflags; 2675 CFMutableDictionaryRef dict; 2676 SInt32 error; 2677 CFMutableArrayRef array; 2678 u_char text[256]; 2679 u_char localText[256]; 2680 2681 if (alertname[0] == 0) 2682 return 0; // no alert to display 2683 2684#define kCmdAskAllowCancel 1 2685#define kCmdAskAllowEntry 2 2686#define kCmdAskMaskEntry 4 2687 2688 if (NextInt(&maskflag)) 2689 maskflag = 0; 2690 2691 SkipBlanks(); 2692 /* first get the string, do not perform variable substitution */ 2693 PrepStr(text, 0, 0, 0); 2694 2695 /* then get the localized version of the string and perform variable substitution */ 2696 localizeString(text, localText, 256); 2697 2698 varSubstitution(localText, SV.strBuf, sizeof(SV.strBuf)); 2699 2700 if (NextInt(&label)) { 2701 SV.askLabel = 0; 2702 flags &= ~kCmdAskAllowCancel; 2703 } 2704 else { 2705 SV.askLabel = (u_int16_t) label; 2706 flags |= kCmdAskAllowCancel; 2707 } 2708 2709 // default: allow user data entry 2710 flags |= kCmdAskAllowEntry; 2711 switch (maskflag) { 2712 // echo user's input 2713 case 0: 2714 flags &= ~kCmdAskMaskEntry; 2715 break; 2716 // mask user's input with bullets 2717 case 1: 2718 flags |= kCmdAskMaskEntry; 2719 // Set the fLastAskMasked here (gcg) 2720 LastAskedMasked = maskflag; 2721 break; 2722 // do not allow data entry 2723 case 2: 2724 flags &= ~kCmdAskAllowEntry; 2725 break; 2726 } 2727 2728 ref = CFStringCreateWithPascalString(NULL, SV.strBuf, kCFStringEncodingUTF8); 2729 if (ref) { 2730 dict = CFDictionaryCreateMutable(NULL, 0, 2731 &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 2732 if (dict) { 2733 2734 alertflags = 0; 2735 2736 if (alertNameRef) 2737 CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, alertNameRef); 2738 2739 // it is either a text field, or a message 2740 if (flags & kCmdAskAllowEntry) { 2741 array = CFArrayCreateMutable(NULL, 0, NULL); 2742 if (array) { 2743 CFArrayAppendValue(array, ref); 2744 CFDictionaryAddValue(dict, kCFUserNotificationTextFieldTitlesKey, array); 2745 CFRelease(array); 2746 if (flags & kCmdAskMaskEntry) 2747 alertflags = CFUserNotificationSecureTextField(0); 2748 } 2749 } 2750 else 2751 CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, ref); 2752 2753 if (cancelNameRef && (flags & kCmdAskAllowCancel)) 2754 CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, cancelNameRef); 2755 2756 if (iconURL) 2757 CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, iconURL); 2758 2759 if (localBundleURL) 2760 CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, localBundleURL); 2761 2762 alert = CFUserNotificationCreate(NULL, 0, alertflags, &error, dict); 2763 if (alert) { 2764 CFUserNotificationReceiveResponse(alert, 0, &alertflags); 2765 // the 2 lower bits of the response flags will give the button pressed 2766 // 0 --> default 2767 // 1 --> alternate 2768 // 3 --> exit on time out 2769 if ((alertflags & 3) == 1) { 2770 // user cancelled 2771 if (SV.askLabel) { 2772 SV.scriptLine = SV.labels[SV.askLabel - 1]; 2773 SV.askLabel = 0; 2774 } 2775 } 2776 else { 2777 // user clicked OK 2778 if (flags & kCmdAskAllowEntry) { 2779 resp = CFUserNotificationGetResponseValue(alert, kCFUserNotificationTextFieldValuesKey, 0); 2780 if (resp) { 2781 CFStringGetPascalString(resp, text, sizeof(text), kCFStringEncodingUTF8); 2782 SetVarString(vsAsk, text); 2783 } 2784 } 2785 } 2786 2787 CFRelease(alert); 2788 } 2789 CFRelease(dict); 2790 } 2791 CFRelease(ref); 2792 } 2793 2794 return 0; 2795} 2796 2797/* -------------------------------------------------------------------------- 2798report modem speed & negotiated compression to client 2799-------------------------------------------------------------------------- */ 2800void CommunicatingAt() 2801{ 2802 u_int32_t speed; 2803 CFNumberRef num; 2804 2805 NextInt(&speed); // modem speed reported by script 2806 sLog("Communicating at %d bps.", speed); 2807 2808 if (serviceID) { 2809 num = CFNumberCreate(NULL, kCFNumberIntType, &speed); 2810 if (num) { 2811 publish_entry(serviceID, kSCPropNetModemConnectSpeed, num); 2812 CFRelease(num); 2813 } 2814 } 2815} 2816 2817/* -------------------------------------------------------------------------- 2818report various events to client 2819// UserHook 1 == claim the Serial Port for an incoming/outgoing call. 2820// UserHook 2 == report that the modem is doing error correction (other than MNP 10). 2821// UserHook 3 == request that ARA turn off data compression. 2822// UserHook 4 == report that the modem is doing MNP 10 error correction. 2823-------------------------------------------------------------------------- */ 2824void UserHook() 2825{ 2826 u_int32_t event; // event reported by script 2827 2828 NextInt( &event ); // get the event 2829 2830#if 0 2831 // now use a PrivateMessage to send 'event' up the Stream, to whom it may concern. 2832 2833 UInt8 len = 0; 2834 TStreamMessage *mp; 2835 2836 len = sizeof( ARA_notify_msg ) + sizeof( UInt32 ); 2837 mp = new ( len ) TStreamMessage; 2838 if( !mp ) 2839 { 2840 DebugStop("\p TCCLScript::UserHook: no memory for message!"); 2841 return; // as if everything is OK, so we expect to fail gracefully later. 2842 } 2843 mp->Reset( len ); 2844 mp->SetType( M_PCPROTO ); 2845 2846 ARA_notify_msg* PrvMsg = (ARA_notify_msg*) mp->GetDataPointer(); 2847 2848 PrvMsg->PRIM_type = kARAPrvMsg; // always. 2849 PrvMsg->CODE_type = kNotifyMsg; // always. 2850 PrvMsg->MODL_dst = kAnyModuleId; // 'any '. 2851 PrvMsg->MODL_src = kScriptModuleId; // 'scri'. 2852 PrvMsg->MSG_type = kNotifyUserHook; 2853 PrvMsg->MSG_error = 0; 2854 PrvMsg->MSG_flags = 0; 2855 2856 *((UInt32*) (PrvMsg + 1)) = event; // put the event type just behind the header 2857 2858 fScriptMod -> ScriptSendDataUp( mp ); // send this up to the Controller. 2859#endif 2860} 2861 2862/* -------------------------------------------------------------------------- 2863-------------------------------------------------------------------------- */ 2864void ScheduleTimer(long type, u_int32_t milliseconds) 2865{ 2866 if (milliseconds) 2867 timeout((void*)TimerExpired, (void*)type, milliseconds); 2868 else 2869 untimeout((void*)TimerExpired, (void*)type); 2870} 2871 2872/* -------------------------------------------------------------------------- 2873Compare the specified VarStr with the parsed string. 2874If the strings match, return the parsed label, else return 0 2875-------------------------------------------------------------------------- */ 2876int IfStr() 2877{ 2878 u_int8_t noMatch = 0, *src; 2879 u_int32_t i = 0, strIndex, labelIndex; 2880 2881 NextInt( &strIndex ); // this is which varString to check 2882 2883 src = GetVarString(strIndex); 2884 2885 /* pull out the search string - if nil return state = continue */ 2886 2887 NextInt(&i); // parse the jump label, if strings match 2888 labelIndex = SV.labels[i - 1]; // save the jump label (??) 2889 SkipBlanks(); // skip to the match string 2890 PrepStr(SV.strBuf, 0, 0, 1); // fetch the match string 2891 2892 if (equalstring(&SV.strBuf[0], src)) { // returns TRUE if they match... 2893 return labelIndex; 2894 } 2895 else { 2896 return noMatch; 2897 } 2898} 2899 2900/* -------------------------------------------------------------------------- 2901-------------------------------------------------------------------------- */ 2902void ReceiveMatchData(u_int8_t nextChar) 2903{ 2904 int32_t matchIndex; 2905 2906 if (debuglevel & 0x1) { 2907 if (usestderr) { 2908 fprintf(stderr, "%c", nextChar); 2909 } 2910 } 2911 2912 if ((matchIndex = MatchFind(nextChar)) != 0) { 2913 // On a match we clear the pending flag and cancel the match timer 2914 SV.scriptLine = SV.matchStr[--matchIndex].matchLine; 2915 SV.ctlFlags &= ~cclMatchPending; 2916 ScheduleTimer(kMatchReadTimer, 0); 2917 StopRead(); 2918 RunScript(); 2919 } 2920} 2921 2922/* -------------------------------------------------------------------------- 2923flush the read queue. free all messages going upstream. 2924-------------------------------------------------------------------------- */ 2925void Flush() 2926{ 2927 tcflush(infd, TCIFLUSH); 2928} 2929 2930/* -------------------------------------------------------------------------- 2931Issue an asychronous call to the serial driver to set the handshaking options 2932-------------------------------------------------------------------------- */ 2933void HSReset() 2934{ 2935 u_int32_t outputXON_OFF, outputCTS, XonChar, XoffChar, inputXON_OFF, inputDTR; 2936 struct termios tios; 2937 2938 if (tcgetattr(outfd, &tios) < 0) 2939 return; 2940 2941 NextInt(&outputXON_OFF); 2942 // if this bit is set, use the XON/XOFF chars for in-band, output flow control. 2943 // users shouldn't be doing this. output of compressor could be the XOFF 2944 // flow control character, and thereby throttle the output stream. 2945 if (outputXON_OFF) 2946 tios.c_iflag |= IXON; 2947 else 2948 tios.c_iflag &= ~IXON; 2949 2950 NextInt(&outputCTS); 2951 // if this bit is set, use CTS for out-band, output flow control. 2952 // ARA calls this signal outputCTS, while Open Transport calls it kOTSrlCTSInputHandshake. 2953 // we both mean that if the modem deasserts CTS, the Mac 2954 // shouldn't send it more data, until the modem reasserts CTS. 2955 // this scheme depends on a correctly wired modem cable. 2956 if (outputCTS) 2957 tios.c_cflag |= CCTS_OFLOW; 2958 else 2959 tios.c_cflag &= ~CCTS_OFLOW; 2960 2961 // default onChar is 0x11, offChar is 0x13. 2962 // users should NEVER be specifying an alternate XonChar or XoffChar. 2963 // when the script is pre-flighted, any attempt to specify a char, 2964 // whether it's an alternate character or a default character, will barf. 2965 // therefore no error checking is done here. assume these parms will be zero. skip 'em. 2966 NextInt(&XonChar); 2967 NextInt(&XoffChar); 2968 tios.c_cc[VSTOP] = XoffChar; /* DC3 = XOFF = ^S */ 2969 tios.c_cc[VSTART] = XonChar; /* DC1 = XON = ^Q */ 2970 2971 NextInt(&inputXON_OFF); 2972 // if this bit is set, use the XON/XOFF chars for in-band, input flow control. 2973 // users shouldn't be doing this. output of compressor could be the XOFF 2974 // flow control character, and thereby throttle the input stream. 2975 if (inputXON_OFF) 2976 tios.c_iflag |= IXOFF; 2977 else 2978 tios.c_iflag &= ~IXOFF; 2979 2980 NextInt(&inputDTR); 2981 // if this bit is set, use DTR for out-band, input flow control. 2982 // ARA calls this signal inputDTR, while Open Transport calls it kOTSrlDTROutputHandshake. 2983 // we both mean that if the Mac deasserts DTR, 2984 // the modem shouldn't send it more data, until the Mac reasserts DTR. 2985 // this scheme depends on a correctly wired modem cable. 2986 if (inputDTR) 2987 tios.c_cflag |= CRTS_IFLOW; 2988 else 2989 tios.c_cflag &= ~CRTS_IFLOW; 2990 2991 // set the same settings for inout and output fd 2992 tcsetattr(infd, TCSAFLUSH, &tios); 2993 tcsetattr(outfd, TCSAFLUSH, &tios); 2994} 2995 2996/* -------------------------------------------------------------------------- 2997Issue an asychronous call to the serial driver to set 2998DTR or Clear DTR, depending on the opcode passed in 2999-------------------------------------------------------------------------- */ 3000void DTRCommand(short DTRCode) 3001{ 3002 ioctl(infd, DTRCode == DTR_SET ? TIOCSDTR : TIOCCDTR); 3003} 3004 3005/* -------------------------------------------------------------------------- 3006Send up a user notification about the current CCL exit error. 3007-------------------------------------------------------------------------- */ 3008static char* cclErrStrings[] = 3009{ 3010 "internal error used to abort match read", // cclErr_AbortMatchRead = -6000 3011 "Bad parameter given to the engine", // cclErr_BadParameter = -6001 3012 "Duplicate label", // cclErr_DuplicateLabel = -6002 3013 "Label undefined", // cclErr_LabelUndefined = -6003 3014 "Subroutine overflow", // cclErr_SubroutineOverFlow = -6004 3015 "No memory...", // cclErr_NoMemErr = -6005 3016 "CCL error base", // (in the middle?) cclErr = -6006 3017 "There is at least one script open", // cclErr_CloseError = -6007 3018 "Script Canceled", // cclErr_ScriptCancelled = -6008 3019 "Script contains too many lines", // cclErr_TooManyLines = -6009 3020 "Script contains too many characters", // cclErr_ScriptTooBig = -6010 3021 "CCL has not been initialized", // cclErr_NotInitialized = -6011 3022 "Cancel in progress.", // cclErr_CancelInProgress = -6012 3023 "Play command already in progress.", // cclErr_PlayInProgress = -6013 3024 "Exit with no error.", // cclErr_ExitOK = -6014 3025 "Label out of range.", // cclErr_BadLabel = -6015 3026 "Bad command.", // cclErr_BadCommand = -6016 3027 "End of script reached, expecting Exit.", // cclErr_EndOfScriptErr = -6017 3028 "Match string index is out of bounds.", // cclErr_MatchStrIndxErr = -6018 3029 "Modem error, modem not responding.", // cclErr_ModemErr = -6019 3030 "No dial tone.", // cclErr_NoDialTone = -6020 3031 "No carrier.", // cclErr_NoCarrierErr = -6021 3032 "Line busy.", // cclErr_LineBusyErr = -6022 3033 "No answer.", // cclErr_NoAnswerErr = -6023 3034 "No @ORIGINATE label", // cclErr_NoOriginateLabel = -6024 3035 "No @ANSWER label", // cclErr_NoAnswerLabel = -6025 3036 "No @HANGUP label", // cclErr_NoHangUpLabel = -6026 3037 "Can't connect because number is empty.", // cclErr_NoNumberErr = -6027 3038 "Incorrect script for the modem." // cclErr_BadScriptErr = -6028 3039}; 3040 3041void terminate(int exitError) 3042{ 3043 //u_long m = exitError; 3044#if 0 3045 CFNumberRef num; 3046#endif 3047#if 1 3048 if (verbose || exitError != 0) { 3049 char *errStr = NULL; 3050 3051 if (exitError == cclErr_DuplicateLabel) { 3052 // null terminate the P-string :P 3053 if (SV.strBuf[0]) { 3054 SV.strBuf[SV.strBuf[0]+1] = '\0'; 3055 errStr = (char*)(&SV.strBuf[0] + 1); 3056 } else { 3057 // empty custom error 3058 errStr = "<no custom error>"; 3059 } 3060 } else if (exitError <= cclErr_AbortMatchRead && 3061 exitError >= cclErr_BadScriptErr) 3062 errStr = cclErrStrings[cclErr_AbortMatchRead - exitError]; 3063 3064 if (errStr) 3065 sLog("CCLExit: %d (%s)", exitError, errStr); 3066 else 3067 sLog("CCLExit: %d", exitError); 3068 } 3069#endif 3070 //if (ppplink != -1) 3071 // sys_send_confd(csockfd, PPP_CCLRESULT, (u_char *)&m, sizeof(m), ppplink, 0); 3072 3073 // connect and listen mode always publish last cause 3074 // disconnect mode publish only non-null cause 3075 // last cause displays connection error, even when plays disconnect sequence 3076#if 0 3077 if (serviceID && (exitError || (enginemode != mode_disconnect))) { 3078 num = CFNumberCreate(NULL, kCFNumberIntType, &exitError); 3079 if (num) { 3080 publish_entry(serviceID, kSCPropNetPPPLastCause, num); 3081 CFRelease(num); 3082 } 3083 } 3084#endif 3085 3086 /* unpublish unnecessary information */ 3087 if (serviceID) { 3088 unpublish_entry(serviceID, kSCPropNetModemNote); 3089 } 3090 3091 close(infd); 3092 close(outfd); 3093 exit(exitError); 3094} 3095 3096/* -------------------------------------------------------------------------- 3097* timeout - Schedule a timeout. 3098* Note that this timeout takes the number of milliseconds, NOT hz (as in 3099* the kernel). 3100-------------------------------------------------------------------------- */ 3101void timeout(void (*func)(void *), void *arg, u_long time) 3102{ 3103 struct callout *newp, *p, **pp; 3104 3105 /* Allocate timeout. */ 3106 if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) 3107 terminate(cclErr_NoMemErr); 3108 3109 newp->c_arg = arg; 3110 newp->c_func = func; 3111 gettimeofday(&timenow, NULL); 3112 newp->c_time.tv_sec = timenow.tv_sec + time/1000; 3113 newp->c_time.tv_usec = timenow.tv_usec + ((time%1000)*1000); 3114 if (newp->c_time.tv_usec >= 1000000) { 3115 newp->c_time.tv_sec++; 3116 newp->c_time.tv_usec -= 1000000; 3117 } 3118 3119 /* 3120 * Find correct place and link it in. 3121 */ 3122 for (pp = &callout; (p = *pp); pp = &p->c_next) 3123 if (newp->c_time.tv_sec < p->c_time.tv_sec 3124 || (newp->c_time.tv_sec == p->c_time.tv_sec 3125 && newp->c_time.tv_usec < p->c_time.tv_usec)) 3126 break; 3127 newp->c_next = p; 3128 *pp = newp; 3129} 3130 3131 3132/* -------------------------------------------------------------------------- 3133* untimeout - Unschedule a timeout. 3134-------------------------------------------------------------------------- */ 3135void untimeout(void (*func)(void *), void *arg) 3136{ 3137 struct callout **copp, *freep; 3138 3139 /* 3140 * Find first matching timeout and remove it from the list. 3141 */ 3142 for (copp = &callout; (freep = *copp); copp = &freep->c_next) 3143 if (freep->c_func == func && freep->c_arg == arg) { 3144 *copp = freep->c_next; 3145 //printf("untimeout [ppp%d]\n", ((struct fsm *)arg)->unit); 3146 free((char *) freep); 3147 break; 3148 } 3149} 3150 3151/* -------------------------------------------------------------------------- 3152* calltimeout - Call any timeout routines which are now due. 3153-------------------------------------------------------------------------- */ 3154void calltimeout() 3155{ 3156 struct callout *p; 3157 3158 while (callout != NULL) { 3159 p = callout; 3160 3161 if (gettimeofday(&timenow, NULL) < 0) 3162 terminate(cclErr_NoMemErr); 3163 3164 if (!(p->c_time.tv_sec < timenow.tv_sec 3165 || (p->c_time.tv_sec == timenow.tv_sec 3166 && p->c_time.tv_usec <= timenow.tv_usec))) 3167 break; /* no, it's not time yet */ 3168 3169 callout = p->c_next; 3170 (*p->c_func)(p->c_arg); 3171 3172 free((char *) p); 3173 } 3174} 3175 3176/* -------------------------------------------------------------------------- 3177* timeleft - return the length of time until the next timeout is due. 3178-------------------------------------------------------------------------- */ 3179struct timeval *timeleft(struct timeval *tvp) 3180{ 3181 if (callout == NULL) 3182 return NULL; 3183 3184 gettimeofday(&timenow, NULL); 3185 tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec; 3186 tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec; 3187 if (tvp->tv_usec < 0) { 3188 tvp->tv_usec += 1000000; 3189 tvp->tv_sec -= 1; 3190 } 3191 if (tvp->tv_sec < 0) 3192 tvp->tv_sec = tvp->tv_usec = 0; 3193 3194 return tvp; 3195} 3196 3197/* ----------------------------------------------------------------------------- 3198publish a dictionnary entry in the cache 3199----------------------------------------------------------------------------- */ 3200int publish_entry(u_char *serviceid, CFStringRef entry, CFTypeRef value) 3201{ 3202 CFMutableDictionaryRef dict; 3203 CFStringRef key; 3204 SCDynamicStoreRef cfgCache; 3205 CFPropertyListRef ref; 3206 int ret = 0; 3207 3208 if (cfgCache = SCDynamicStoreCreate(0, CFSTR("CCLEngine"), 0, 0)) { 3209 if (key = SCDynamicStoreKeyCreate(0, CFSTR("%@/%@/%@/%s/%@"), 3210 kSCDynamicStoreDomainState, kSCCompNetwork, kSCCompService, serviceid, kSCEntNetModem)) { 3211 3212 if (ref = SCDynamicStoreCopyValue(cfgCache, key)) { 3213 dict = CFDictionaryCreateMutableCopy(0, 0, ref); 3214 CFRelease(ref); 3215 } 3216 else 3217 dict = CFDictionaryCreateMutable(0, 0, 3218 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 3219 3220 if (dict) { 3221 CFDictionarySetValue(dict, entry, value); 3222 ret = SCDynamicStoreSetValue(cfgCache, key, dict); 3223 CFRelease(dict); 3224 } 3225 3226 CFRelease(key); 3227 } 3228 CFRelease(cfgCache); 3229 } 3230 return ret; 3231} 3232 3233/* ----------------------------------------------------------------------------- 3234unpublish a disctionnary entry from the cache 3235----------------------------------------------------------------------------- */ 3236int unpublish_entry(u_char *serviceid, CFStringRef entry) 3237{ 3238 CFPropertyListRef ref; 3239 CFMutableDictionaryRef dict; 3240 CFStringRef key; 3241 SCDynamicStoreRef cfgCache; 3242 int ret = 0; 3243 3244 if (cfgCache = SCDynamicStoreCreate(0, CFSTR("CCLEngine"), 0, 0)) { 3245 3246 if (key = SCDynamicStoreKeyCreate(0, CFSTR("%@/%@/%@/%s/%@"), 3247 kSCDynamicStoreDomainState, kSCCompNetwork, kSCCompService, serviceid, kSCEntNetModem)) { 3248 3249 if (ref = SCDynamicStoreCopyValue(cfgCache, key)) { 3250 if (dict = CFDictionaryCreateMutableCopy(0, 0, ref)) { 3251 CFDictionaryRemoveValue(dict, entry); 3252 ret = SCDynamicStoreSetValue(cfgCache, key, dict); 3253 CFRelease(dict); 3254 } 3255 CFRelease(ref); 3256 } 3257 CFRelease(key); 3258 } 3259 CFRelease(cfgCache); 3260 } 3261 return ret; 3262} 3263 3264 3265