First working version of new event dispatching framwork.
[screen-lua.git] / src / teln.c
blob1764dbcceafcdb8e8df46b3abe758a2dc27d5e21
1 /* Copyright (c) 2008, 2009
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Micah Cowan (micah@cowan.name)
5 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
6 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
7 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
8 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
9 * Copyright (c) 1987 Oliver Laumann
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3, or (at your option)
14 * any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program (see the file COPYING); if not, see
23 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 ****************************************************************
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <fcntl.h>
32 #include <netdb.h>
34 #include "config.h"
36 #ifdef BUILTIN_TELNET
38 #include "screen.h"
39 #include "extern.h"
41 extern struct win *fore;
42 extern struct layer *flayer;
43 extern int visual_bell;
44 extern char screenterm[];
46 static void TelReply __P((struct win *, char *, int));
47 static void TelDocmd __P((struct win *, int, int));
48 static void TelDosub __P((struct win *));
50 #define TEL_DEFPORT 23
51 #define TEL_CONNECTING (-2)
53 #define TC_IAC 255
54 #define TC_DONT 254
55 #define TC_DO 253
56 #define TC_WONT 252
57 #define TC_WILL 251
58 #define TC_SB 250
59 #define TC_BREAK 243
60 #define TC_SE 240
62 #define TC_S "S b swWdDc"
64 #define TO_BINARY 0
65 #define TO_ECHO 1
66 #define TO_SGA 3
67 #define TO_TM 6
68 #define TO_TTYPE 24
69 #define TO_NAWS 31
70 #define TO_TSPEED 32
71 #define TO_LFLOW 33
72 #define TO_LINEMODE 34
73 #define TO_XDISPLOC 35
74 #define TO_NEWENV 39
76 #define TO_S "be c t wsf xE E"
79 static unsigned char tn_init[] = {
80 TC_IAC, TC_DO, TO_SGA,
81 TC_IAC, TC_WILL, TO_TTYPE,
82 TC_IAC, TC_WILL, TO_NAWS,
83 TC_IAC, TC_WILL, TO_LFLOW,
86 static void
87 tel_connev_fn(ev, data)
88 struct event *ev;
89 char *data;
91 struct win *p = (struct win *)data;
92 if (connect(p->w_ptyfd, (struct sockaddr *)&p->w_telsa, sizeof(p->w_telsa)) && errno != EISCONN)
94 char buf[1024];
95 buf[0] = ' ';
96 strncpy(buf + 1, strerror(errno), sizeof(buf) - 2);
97 buf[sizeof(buf) - 1] = 0;
98 WriteString(p, buf, strlen(buf));
99 WindowDied(p, 0, 0);
100 return;
102 WriteString(p, "connected.\r\n", 12);
103 evdeq(&p->w_telconnev);
104 p->w_telstate = 0;
108 TelOpen(args)
109 char **args;
111 int fd;
112 int on = 1;
114 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
116 Msg(errno, "TelOpen: socket");
117 return -1;
119 if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)))
120 Msg(errno, "TelOpen: setsockopt SO_OOBINLINE");
121 return fd;
125 TelConnect(p)
126 struct win *p;
128 int port = TEL_DEFPORT;
129 struct hostent *hp;
130 char **args;
131 char buf[256];
133 args = p->w_cmdargs + 1;
135 if (!*args)
137 Msg(0, "Usage: screen //telnet host [port]");
138 return -1;
140 if (args[1])
141 port = atoi(args[1]);
142 p->w_telsa.sin_family = AF_INET;
143 if((p->w_telsa.sin_addr.s_addr = inet_addr(*args)) == -1)
145 if ((hp = gethostbyname(*args)) == NULL)
147 Msg(0, "unknown host: %s", *args);
148 return -1;
150 if (hp->h_length != sizeof(p->w_telsa.sin_addr.s_addr) || hp->h_addrtype != AF_INET)
152 Msg(0, "Bad address type for %s", hp->h_name);
153 return -1;
155 bcopy((char *)hp->h_addr,(char *)&p->w_telsa.sin_addr.s_addr, hp->h_length);
156 p->w_telsa.sin_family = hp->h_addrtype;
158 p->w_telsa.sin_port = htons(port);
159 if (port != TEL_DEFPORT)
160 sprintf(buf, "Trying %s %d...", inet_ntoa(p->w_telsa.sin_addr), port);
161 else
162 sprintf(buf, "Trying %s...", inet_ntoa(p->w_telsa.sin_addr));
163 WriteString(p, buf, strlen(buf));
164 if (connect(p->w_ptyfd, (struct sockaddr *)&p->w_telsa, sizeof(p->w_telsa)))
166 if (errno == EINPROGRESS)
168 p->w_telstate = TEL_CONNECTING;
169 p->w_telconnev.fd = p->w_ptyfd;
170 p->w_telconnev.handler = tel_connev_fn;
171 p->w_telconnev.data = (char *)p;
172 p->w_telconnev.type = EV_WRITE;
173 p->w_telconnev.pri = 1;
174 debug("telnet connect in progress...\n");
175 evenq(&p->w_telconnev);
177 else
179 Msg(errno, "TelOpen: connect");
180 return -1;
183 else
184 WriteString(p, "connected.\r\n", 12);
185 if (port == TEL_DEFPORT)
186 TelReply(p, (char *)tn_init, sizeof(tn_init));
187 return 0;
191 TelIsline(p)
192 struct win *p;
194 return !fore->w_telropts[TO_SGA];
197 void
198 TelProcessLine(bufpp, lenp)
199 char **bufpp;
200 int *lenp;
202 int echo = !fore->w_telropts[TO_ECHO];
203 unsigned char c;
204 char *tb;
205 int tl;
207 char *buf = *bufpp;
208 int l = *lenp;
209 while (l--)
211 c = *(unsigned char *)buf++;
212 if (fore->w_telbufl + 2 >= IOSIZE)
214 WBell(fore, visual_bell);
215 continue;
217 if (c == '\r')
219 if (echo)
220 WriteString(fore, "\r\n", 2);
221 fore->w_telbuf[fore->w_telbufl++] = '\r';
222 fore->w_telbuf[fore->w_telbufl++] = '\n';
223 tb = fore->w_telbuf;
224 tl = fore->w_telbufl;
225 LayProcess(&tb, &tl);
226 fore->w_telbufl = 0;
227 continue;
229 if (c == '\b' && fore->w_telbufl > 0)
231 if (echo)
233 WriteString(fore, (char *)&c, 1);
234 WriteString(fore, " ", 1);
235 WriteString(fore, (char *)&c, 1);
237 fore->w_telbufl--;
239 if ((c >= 0x20 && c <= 0x7e) || c >= 0xa0)
241 if (echo)
242 WriteString(fore, (char *)&c, 1);
243 fore->w_telbuf[fore->w_telbufl++] = c;
246 *lenp = 0;
250 DoTelnet(buf, lenp, f)
251 char *buf;
252 int *lenp;
253 int f;
255 int echo = !fore->w_telropts[TO_ECHO];
256 int cmode = fore->w_telropts[TO_SGA];
257 int bin = fore->w_telropts[TO_BINARY];
258 char *p = buf, *sbuf;
259 int trunc = 0;
260 int c;
261 int l = *lenp;
263 sbuf = p;
264 while (l-- > 0)
266 c = *(unsigned char *)p++;
267 if (c == TC_IAC || (c == '\r' && (l ==0 || *p != '\n') && cmode && !bin))
269 if (cmode && echo)
271 WriteString(fore, sbuf, p - sbuf);
272 sbuf = p;
274 if (f-- <= 0)
276 trunc++;
277 l--;
279 if (l < 0)
281 p--; /* drop char */
282 break;
284 if (l)
285 bcopy(p, p + 1, l);
286 if (c == TC_IAC)
287 *p++ = c;
288 else if (c == '\r')
289 *p++ = 0;
290 else if (c == '\n')
292 p[-1] = '\r';
293 *p++ = '\n';
297 *lenp = p - buf;
298 return trunc;
301 /* modifies data in-place, returns new length */
303 TelIn(p, buf, len, free)
304 struct win *p;
305 char *buf;
306 int len;
307 int free;
309 char *rp, *wp;
310 int c;
312 rp = wp = buf;
313 while (len-- > 0)
315 c = *(unsigned char *)rp++;
317 if (p->w_telstate >= TC_WILL && p->w_telstate <= TC_DONT)
319 TelDocmd(p, p->w_telstate, c);
320 p->w_telstate = 0;
321 continue;
323 if (p->w_telstate == TC_SB || p->w_telstate == TC_SE)
325 if (p->w_telstate == TC_SE && c == TC_IAC)
326 p->w_telsubidx--;
327 if (p->w_telstate == TC_SE && c == TC_SE)
329 p->w_telsubidx--;
330 TelDosub(p);
331 p->w_telstate = 0;
332 continue;
334 if (p->w_telstate == TC_SB && c == TC_IAC)
335 p->w_telstate = TC_SE;
336 else
337 p->w_telstate = TC_SB;
338 p->w_telsubbuf[p->w_telsubidx] = c;
339 if (p->w_telsubidx < sizeof(p->w_telsubbuf) - 1)
340 p->w_telsubidx++;
341 continue;
343 if (p->w_telstate == TC_IAC)
345 if ((c >= TC_WILL && c <= TC_DONT) || c == TC_SB)
347 p->w_telsubidx = 0;
348 p->w_telstate = c;
349 continue;
351 p->w_telstate = 0;
352 if (c != TC_IAC)
353 continue;
355 else if (c == TC_IAC)
357 p->w_telstate = c;
358 continue;
360 if (p->w_telstate == '\r')
362 p->w_telstate = 0;
363 if (c == 0)
364 continue; /* suppress trailing \0 */
366 else if (c == '\n' && !p->w_telropts[TO_SGA])
368 /* oops... simulate terminal line mode: insert \r */
369 if (wp + 1 == rp)
371 if (free-- > 0)
373 if (len)
374 bcopy(rp, rp + 1, len);
375 rp++;
376 *wp++ = '\r';
379 else
380 *wp++ = '\r';
382 if (c == '\r')
383 p->w_telstate = c;
384 *wp++ = c;
386 return wp - buf;
389 static void
390 TelReply(p, str, len)
391 struct win *p;
392 char *str;
393 int len;
395 if (len <= 0)
396 return;
397 if (p->w_inlen + len > IOSIZE)
399 Msg(0, "Warning: telnet protocol overrun!");
400 return;
402 bcopy(str, p->w_inbuf + p->w_inlen, len);
403 p->w_inlen += len;
406 static void
407 TelDocmd(p, cmd, opt)
408 struct win *p;
409 int cmd, opt;
411 unsigned char b[3];
412 int repl = 0;
414 if (cmd == TC_WONT)
415 debug2("[<-WONT %c %d]\n", TO_S[opt], opt);
416 if (cmd == TC_WILL)
417 debug2("[<-WILL %c %d]\n", TO_S[opt], opt);
418 if (cmd == TC_DONT)
419 debug2("[<-DONT %c %d]\n", TO_S[opt], opt);
420 if (cmd == TC_DO)
421 debug2("[<-DO %c %d]\n", TO_S[opt], opt);
423 switch(cmd)
425 case TC_WILL:
426 if (p->w_telropts[opt] || opt == TO_TM)
427 return;
428 repl = TC_DONT;
429 if (opt == TO_ECHO || opt == TO_SGA || opt == TO_BINARY)
431 p->w_telropts[opt] = 1;
432 /* setcon(); */
433 repl = TC_DO;
435 break;
436 case TC_WONT:
437 if (!p->w_telropts[opt] || opt == TO_TM)
438 return;
439 repl = TC_DONT;
440 #if 0
441 if (opt == TO_ECHO || opt == TO_SGA)
442 setcon();
443 #endif
444 p->w_telropts[opt] = 0;
445 break;
446 case TC_DO:
447 if (p->w_telmopts[opt])
448 return;
449 repl = TC_WONT;
450 if (opt == TO_TTYPE || opt == TO_SGA || opt == TO_BINARY || opt == TO_NAWS || opt == TO_TM || opt == TO_LFLOW)
452 repl = TC_WILL;
453 p->w_telmopts[opt] = 1;
455 p->w_telmopts[TO_TM] = 0;
456 break;
457 case TC_DONT:
458 if (!p->w_telmopts[opt])
459 return;
460 repl = TC_WONT;
461 p->w_telmopts[opt] = 0;
462 break;
464 b[0] = TC_IAC;
465 b[1] = repl;
466 b[2] = opt;
468 if (repl == TC_WONT)
469 debug2("[->WONT %c %d]\n", TO_S[opt], opt);
470 if (repl == TC_WILL)
471 debug2("[->WILL %c %d]\n", TO_S[opt], opt);
472 if (repl == TC_DONT)
473 debug2("[->DONT %c %d]\n", TO_S[opt], opt);
474 if (repl == TC_DO)
475 debug2("[->DO %c %d]\n", TO_S[opt], opt);
477 TelReply(p, (char *)b, 3);
478 if (cmd == TC_DO && opt == TO_NAWS)
479 TelWindowSize(p);
483 static void
484 TelDosub(p)
485 struct win *p;
487 char trepl[20 + 6 + 1];
488 int l;
490 switch(p->w_telsubbuf[0])
492 case TO_TTYPE:
493 if (p->w_telsubidx != 2 || p->w_telsubbuf[1] != 1)
494 return;
495 l = strlen(screenterm);
496 if (l >= 20)
497 break;
498 sprintf(trepl, "%c%c%c%c%s%c%c", TC_IAC, TC_SB, TO_TTYPE, 0, screenterm, TC_IAC, TC_SE);
499 TelReply(p, trepl, l + 6);
500 break;
501 case TO_LFLOW:
502 if (p->w_telsubidx != 2)
503 return;
504 debug1("[FLOW %d]\r\n", p->w_telsubbuf[1]);
505 break;
506 default:
507 break;
511 void
512 TelBreak(p)
513 struct win *p;
515 static unsigned char tel_break[] = { TC_IAC, TC_BREAK };
516 TelReply(p, (char *)tel_break, 2);
519 void
520 TelWindowSize(p)
521 struct win *p;
523 char s[20], trepl[20], *t;
524 int i;
526 debug2("TelWindowSize %d %d\n", p->w_width, p->w_height);
527 if (p->w_width == 0 || p->w_height == 0 || !p->w_telmopts[TO_NAWS])
528 return;
529 sprintf(s, "%c%c%c%c%c%c%c%c%c", TC_SB, TC_SB, TO_NAWS, p->w_width / 256, p->w_width & 255, p->w_height / 256, p->w_height & 255, TC_SE, TC_SE);
530 t = trepl;
531 for (i = 0; i < 9; i++)
532 if ((unsigned char)(*t++ = s[i]) == TC_IAC)
533 *t++ = TC_IAC;
534 trepl[0] = TC_IAC;
535 t[-2] = TC_IAC;
536 debug(" - sending");
537 for (i = 0; trepl + i < t; i++)
538 debug1(" %02x", (unsigned char)trepl[i]);
539 debug("\n");
540 TelReply(p, trepl, t - trepl);
543 static char tc_s[] = TC_S;
544 static char to_s[] = TO_S;
546 void
547 TelStatus(p, buf, l)
548 struct win *p;
549 char *buf;
550 int l;
552 int i;
554 *buf++ = '[';
555 for (i = 0; to_s[i]; i++)
557 if (to_s[i] == ' ' || p->w_telmopts[i] == 0)
558 continue;
559 *buf++ = to_s[i];
561 *buf++ = ':';
562 for (i = 0; to_s[i]; i++)
564 if (to_s[i] == ' ' || p->w_telropts[i] == 0)
565 continue;
566 *buf++ = to_s[i];
568 if (p->w_telstate == TEL_CONNECTING)
569 buf[-1] = 'C';
570 else if (p->w_telstate && p->w_telstate != '\r')
572 *buf++ = ':';
573 *buf++ = tc_s[p->w_telstate - TC_SE];
575 *buf++ = ']';
576 *buf = 0;
577 return;
580 #endif /* BUILTIN_TELNET */