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#import <AppKit/NSTextView.h> 26//#import <Carbon/CarbonPriv.h> 27#import <Carbon/Carbon.h> 28 29#import "MiniTerm.h" 30 31#import "../../Controller/ppp_privmsg.h" 32#include <sys/types.h> 33#include <unistd.h> 34#include <sys/socket.h> /* struct msghdr */ 35#include <sys/uio.h> /* struct iovec */ 36#include <sys/un.h> 37#include <sys/syslog.h> 38 39 40@implementation PromptChat 41 42enum { 43 MATCH_NONE, 44 MATCH_7E, 45 MATCH_FF, 46 MATCH_7D, 47 MATCH_23, 48 MATCH_03, 49 MATCH_COMPLETE 50}; 51 52NSFileHandle *file_tty, *pppd_socket; 53 54/* ----------------------------------------------------------------------------- 55 Receive a file descriptor from another process (a server). 56 We have a 2-byte protocol for receiving the fd from send_fd(). 57----------------------------------------------------------------------------- */ 58int recv_fd(int servfd) 59{ 60 struct cmsg { 61 struct cmsghdr hdr; 62 int fd; 63 } cmsg; 64 int newfd, nread, status; 65 char *ptr, buf[2]; /* send_fd()/recv_fd() 2-byte protocol */ 66 struct iovec iov[1]; 67 struct msghdr msg; 68 69 newfd = -1; 70 status = -1; 71 for ( ; ; ) { 72 iov[0].iov_base = buf; 73 iov[0].iov_len = sizeof(buf); 74 msg.msg_iov = iov; 75 msg.msg_iovlen = 1; 76 msg.msg_name = NULL; 77 msg.msg_namelen = 0; 78 msg.msg_control = (caddr_t) &cmsg; 79 msg.msg_controllen = sizeof(struct cmsg); 80 81 nread = recvmsg(servfd, &msg, 0); 82 if (nread == 0) { 83 return -1; 84 } 85 /* See if this is the final data with null & status. 86 Null must be next to last byte of buffer, status 87 byte is last byte. Zero status means there must 88 be a file descriptor to receive. */ 89 for (ptr = buf; ptr < &buf[nread]; ) { 90 if (*ptr++ == 0) { 91 status = *ptr & 255; 92 if (status == 0) { 93 newfd = cmsg.fd; /* new descriptor */ 94 } 95 else 96 newfd = -status; 97 nread -= 2; 98 } 99 } 100 if (status >= 0) /* final data has arrived */ 101 return newfd; /* descriptor, or -status */ 102 } 103 return -1; 104} 105 106/* ------------------------------------------------------------------------------------------ 107------------------------------------------------------------------------------------------ */ 108- (void)awakeFromNib { 109 110 NSRange range; 111 int err, ttyfd, sockfd; 112 struct sockaddr_un adr; 113 114 // init vars 115 fromline = 0; 116 match = MATCH_NONE; 117 118 // affect self as delegate to intercept input 119 range.location = 0; 120 range.length = 0; 121 [text setSelectedRange: range]; 122 [text setDelegate: self]; 123 [[text window] makeFirstResponder:text]; 124 125 // bring the app to the front, window centered 126 [NSApp activateIgnoringOtherApps:YES]; 127 [[text window] center]; 128 [[text window] makeKeyAndOrderFront:self]; 129 [[text window] setLevel:NSFloatingWindowLevel]; 130 131 // enable only roman keyboard 132#if 0 133 KeyScript(smKeyEnableRomanOnly); 134#else 135 TISInputSourceRef asciiInpSrc = TISCopyCurrentASCIICapableKeyboardInputSource(); 136 if (asciiInpSrc != NULL) { 137 TISSelectInputSource( asciiInpSrc ); 138 CFRelease( asciiInpSrc ); 139 } 140#endif /* __LP64__ */ 141 142 // contact pppd to get serial descriptor and exit code communication channel 143 sockfd = socket(AF_LOCAL, SOCK_STREAM, 0); 144 if (sockfd < 0) { 145 exit(0); // should probably display an alert 146 } 147 148 bzero(&adr, sizeof(adr)); 149 adr.sun_family = AF_LOCAL; 150 strlcpy(adr.sun_path, "/var/run/pppd-miniterm", sizeof(adr.sun_path)); 151 152 if ((err = connect(sockfd, (struct sockaddr *)&adr, sizeof(adr)) < 0)) { 153 exit(0); // should probably display an alert 154 } 155 156 ttyfd = recv_fd(sockfd); 157 158 file_tty = [[NSFileHandle alloc] initWithFileDescriptor: ttyfd]; 159 160 // install notification and read asynchronously on file_tty 161 [[NSNotificationCenter defaultCenter] addObserver:self 162 selector:@selector(input:) 163 name:NSFileHandleReadCompletionNotification 164 object:file_tty]; 165 166 [file_tty readInBackgroundAndNotify]; 167 168 // there is nothing to read on pppd_socket fd 169 // but we need to catch when pipe is closed 170 pppd_socket = [[NSFileHandle alloc] initWithFileDescriptor: sockfd]; 171 [pppd_socket readInBackgroundAndNotify]; 172 173} 174 175/* ------------------------------------------------------------------------------------------ 176------------------------------------------------------------------------------------------ */ 177- (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString 178{ 179 180 u_char c, *p; 181 int i, len = [replacementString length]; 182 NSMutableData *data; 183 184 // are we inserting the incoming char from the line ? 185 // could be a critical section here... not sure about messaging system 186 if (fromline) { 187 return YES; 188 } 189 190 data = [NSMutableData alloc]; 191 if (data) { 192 193 if (len == 0) { 194 // send the delete char 195 c = 8; 196 [data initWithBytes: &c length: 1]; 197 } 198 else { 199 [data initWithData:[replacementString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]]; 200 // can the len change during conversion ? 201 len = [data length]; 202 // replace 10 by 13 in the string 203 p = [data mutableBytes]; 204 for (i = 0; i < len; i++) 205 if (p[i] == 10) 206 p[i] = 13; 207 } 208 209 // write the data to the output file 210 [file_tty writeData: data]; 211 [data release]; 212 } 213 214 return NO; 215} 216 217/* ------------------------------------------------------------------------------------------ 218------------------------------------------------------------------------------------------ */ 219#if 0 220- (NSRange)textView:(NSTextView *)textView willChangeSelectionFromCharacterRange:(NSRange)oldSelectedCharRange toCharacterRange:(NSRange)newSelectedCharRange 221{ 222 // Don't allow the selection to change 223 return NSMakeRange([[textView string] length], 0); 224} 225#endif 226 227/* ------------------------------------------------------------------------------------------ 228------------------------------------------------------------------------------------------ */ 229- (void)display:(u_char *)data 230 length:(u_int)length { 231 232 NSString *str; 233 234 if (length) { 235 str = (NSString *)CFStringCreateWithBytes(NULL, data, length, kCFStringEncodingASCII, NO); 236 if (str) { 237 fromline = 1; 238 [text insertText:str]; 239 fromline = 0; 240 [str release]; 241 } 242 } 243} 244 245/* ------------------------------------------------------------------------------------------ 246------------------------------------------------------------------------------------------ */ 247- (void)input:(NSNotification *)notification { 248 249 NSData *data; 250 u_char *p, *p0; 251 u_long len; 252 253 // move the selection to the end 254 [text setSelectedRange: NSMakeRange([[text string] length], 0)]; 255 256 data = [[notification userInfo] objectForKey: NSFileHandleNotificationDataItem]; 257 258 p0 = p = (u_char *)[data bytes]; 259 len = [data length]; 260 261 if (len == 0) { 262 // pipe has been closed (happens when pppd quits) 263 exit(0); 264 } 265 266 while (len) { 267 268 // look for ppp frame 269 // match for 7E-FF-03 270 // match for 7E-FF-7D-23 271 switch (*p) { 272 case 0x7e: match = MATCH_7E; break; 273 case 0xff: match = (match == MATCH_7E) ? MATCH_FF : MATCH_NONE; break; 274 case 0x7d: match = (match == MATCH_FF) ? MATCH_7D : MATCH_NONE; break; 275 case 0x03: match = (match == MATCH_FF) ? MATCH_COMPLETE : MATCH_NONE; break; 276 case 0x23: match = (match == MATCH_7D) ? MATCH_COMPLETE : MATCH_NONE; break; 277 default: match = MATCH_NONE; 278 } 279 280 if (match == MATCH_COMPLETE) { 281 // display what was valid before we exit 282 [self display:p0 length:p - p0]; 283 // will quit app, successfully 284 [self continuechat: self]; 285 return; 286 } 287 288 if (*p >= 128) 289 *p -= 128; 290 291 if ((*p >= 0x20) 292 || (*p == 9) 293 || (*p == 10) 294 || (*p == 13)) { 295 296 // valid bytes, they will be display later, in one chunck 297 } 298 else { 299 // got a non printable byte, display what was valid so far 300 [self display:p0 length:p - p0]; 301 p0 = p + 1; 302 303 // check for delete char 304 if (*p == 8) { 305 if ([[text string] length] > 0) 306 [text replaceCharactersInRange: NSMakeRange([[text string] length] - 1, 1) 307 withString:@""]; 308 } 309 } 310 311 p++; 312 len--; 313 } 314 315 // display all the undisplayed bytes 316 [self display:p0 length:p - p0]; 317 318 // post an other read 319 [file_tty readInBackgroundAndNotify]; 320} 321 322/* ------------------------------------------------------------------------------------------ 323------------------------------------------------------------------------------------------ */ 324- (IBAction)cancelchat:(id)sender 325{ 326 u_char c = (unsigned char)cclErr_ScriptCancelled; 327 NSData *data; 328 329 data = [NSData dataWithBytes: &c length: 1]; 330 if (data) { 331 [pppd_socket writeData: data]; 332 [data release]; 333 } 334 335 // time to quit 336 exit(0); 337} 338 339/* ------------------------------------------------------------------------------------------ 340------------------------------------------------------------------------------------------ */ 341- (IBAction)continuechat:(id)sender 342{ 343 u_char c = 0; 344 NSData *data; 345 346 data = [NSData dataWithBytes: &c length: 1]; 347 if (data) { 348 [pppd_socket writeData: data]; 349 [data release]; 350 } 351 352 // time to quit 353 exit(0); 354} 355 356@end 357