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