blob: 341e1a0cdac0dc9b304b1f3c1d75a550d7c118a4 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// telnetneg.c -- Verwaltung von Telnet-Negotiations
4//
5// $Id$
6
7/* Das Original wurde von Marcus@Tapp zur Verfuegung gestellt. */
8/* Angepasst fuer die MG-Mudlib von Ringor@MG */
9/* Weitgehend ueberarbeitet von Zesstra@MG */
10
11#pragma strict_types,save_types
12#pragma range_check
13#pragma no_clone
14#pragma no_shadow
15#pragma pedantic
16
17inherit "/secure/telnetneg-structs.c";
18
19#define NEED_PROTOTYPES
20#include "/secure/telnetneg.h"
21#undef NEED_PROTOTYPES
22
23// unterstuetzte Optionen:
24// TELOPT_EOR, TELOPT_NAWS, TELOPT_LINEMODE, TELOPT_TTYPE
25
26//#define __DEBUG__ 1
27
28#ifdef __DEBUG__
29#define DEBUG(x) if (interactive(this_object()))\
30 tell_object(this_object(),"TN: " + x + "\n")
31#define DTN(x,y) _debug_print(x,y)
32#else
33# define DEBUG(x)
34# define DTN(x,y)
35#endif
36
37
38
39// Aus mini_props.c:
40public varargs mixed Query( string str, int type );
41public varargs mixed Set( string str, mixed value, int type );
42
43private nosave mapping TN = ([]);
44nosave string *Terminals;
45
46// Prototypen
47private void eval_naws(int *optargs);
48
49#ifdef __DEBUG__
50// Gibts einige Konstanten mit sym. Namen aus.
51private string dtranslate(int i) {
52 switch(i) {
53 case IAC: return "IAC";
54 case DONT: return "DONT";
55 case DO: return "DO";
56 case WONT: return "WONT";
57 case WILL: return "WILL";
58 case SB: return "SB";
59 case SE: return "SE";
60 case EOR: return "EOR";
61 case TELOPT_LINEMODE: return "TELOPT_LINEMODE";
62 case TELOPT_XDISPLOC: return "TELOPT_XDISPLOC";
63 case TELOPT_ENVIRON: return "TELOPT_ENVIRON";
64 case TELOPT_NEWENV: return "TELOPT_NEWENV";
65 case TELOPT_EOR: return "TELOPT_EOR";
66 case TELOPT_NAWS: return "TELOPT_NAWS";
67 case TELOPT_TSPEED: return "TELOPT_TSPEED";
68 case TELOPT_TTYPE: return "TELOPT_TTYPE";
69 case TELOPT_ECHO: return "TELOPT_ECHO";
70 case TELOPT_SGA: return "TELOPT_SGA";
71 case TELOPT_NAMS: return "TELOPT_NAMS";
72 case TELOPT_STATUS: return "TELOPT_STATUS";
73 case TELOPT_TM: return "TELOPT_TM";
74
75 case TELOPT_MSDP: return "TELOPT_MSDP";
76 case TELOPT_COMPRESS2: return "TELOPT_COMPRESS2";
77 case TELOPT_MSP: return "TELOPT_MSP";
78 case TELOPT_MXP: return "TELOPT_MXP";
79 case TELOPT_ATCP: return "TELOPT_ATCP";
80 case TELOPT_GMCP: return "TELOPT_GMCP";
81 case TELOPT_MSSP: return "TELOPT_MSSP";
82 }
83 return to_string(i);
84}
85
86// Gibt <arr> halbwegs lesbar an this_object() aus.
87private void _debug_print(string x, int *arr) {
88 if (arr[1] == SB && arr[<1] != SE)
89 arr += ({IAC, SE});
90 closure map_int = function string (int i)
91 { if (i >= 32 && i <= 126) return sprintf("%c",i);
92 return "["+to_string(i)+"]";
93 };
94 if (sizeof(arr)<=5) {
95 foreach(int c : arr)
96 x += " " + dtranslate(c);
97 }
98 else {
99 x += dtranslate(arr[0]) + " " + dtranslate(arr[1]) + " "
100 + dtranslate(arr[2]) + " "
101 + implode(map(arr[3..<3], map_int)," ")
102 + " " + dtranslate(arr[<2]) + " " + dtranslate(arr[<1]);
103 }
104 DEBUG(x);
105}
106#endif
107
108protected varargs int send_telnet_neg(int *arr, int bm_flags)
109{
110 if ( sizeof(arr) < 2 )
111 return efun::binary_message(arr,bm_flags);
112
113 struct telopt_s opt = TN[arr[1]];
114
115 switch (arr[0]){
116 case DO:
117 case DONT:
118 (opt->lo_wishes)->remoteside = arr[0];
119 arr = ({IAC}) + arr;
120 break;
121 case WILL:
122 case WONT:
123 (opt->lo_wishes)->localside = arr[0];
124 arr = ({IAC}) + arr;
125 break;
126 case SB:
127 (opt->lo_wishes)->sbdata = arr[0..];
128 arr = ({IAC}) + arr + ({IAC, SE});
129 break;
130 default:
131 break;
132 }
133 DTN("send_tn: ",arr);
134 return efun::binary_message(arr, bm_flags);
135}
136
137protected varargs int send_telnet_neg_str(string str, int bm_flags) {
138#ifdef __DEBUG__
139 // Debugausgaben zur Zeit nur fuer arraybasierte Variante
140 return send_telnet_neg(to_array(str), bm_flags);
141#else
142 if ( sizeof(str) < 2 )
143 return efun::binary_message(str, bm_flags);
144
145 struct telopt_s opt = TN[str[1]];
146
147 switch (str[0]) {
148 case DO:
149 case DONT:
150 (opt->lo_wishes)->remoteside = str[0];
151 str=sprintf("%c%s",IAC,str);
152 break;
153 case WILL:
154 case WONT:
155 (opt->lo_wishes)->localside = str[0];
156 str=sprintf("%c%s",IAC,str);
157 break;
158 case SB:
159 (opt->lo_wishes)->sbdata = map(explode(str[0..],""),#'to_int);
160 str=sprintf("%c%s%c%c", IAC, str, IAC, SE);
161 break;
162 default:
163 break;
164 }
165
166 return efun::binary_message(str, bm_flags);
167#endif // __DEBUG__
168}
169
170// Startet eine Verhandlung, um den Status einer Option zu aendern.
171// Wenn bereits eine Verhandlung laeuft, wird nichts gemacht und -1
172// zurueckgeben.
173// Wenn die Verhandlung keine Aenderung vom Status quo zum Ziel hat, wird
174// nichts gemacht und -2 zurueckgegeben.
175// Ansonsten ist die Rueckgabe die Anzahl der uebermittelten Zeichen.
176// <action>: WILL: Option soll auf dieser Seite eingeschaltet werden.
177// WONT: Option soll auf dieser Seite ausgeschaltet werden.
178// DO : Option soll auf der anderen Seite eingeschaltet werden.
179// DONT: Option soll auf der anderen Seite ausgeschaltet werden.
180protected int do_telnet_neg(int option, int action) {
181 struct telopt_s opt = TN[option];
182 if (!structp(opt))
183 {
184 opt = (<telopt_s> option: option,
185 re_wishes: (<to_state_s>),
186 lo_wishes: (<to_state_s>),
187 state: (<to_state_s>)
188 );
189 TN[option] = opt;
190 }
191 // es wird nur geprueft, ob wir bereits eine Verhandlung begonnen haben
192 // (lo_wishes), weil reinkommende remote Wuensche letztendlich sofort durch
193 // unsere Antwort erledigt sind.
194 switch(action)
195 {
196 case WILL:
197 if (opt->lo_wishes->localside != 0)
198 return -1;
199 if (opt->state->localside)
200 return -2;
201 return send_telnet_neg( ({ WILL, option }) );
202 break;
203 case WONT:
204 if (opt->lo_wishes->localside != 0)
205 return -1;
206 if (!opt->state->localside)
207 return -2;
208 return send_telnet_neg( ({ WONT, option }) );
209 break;
210 case DO:
211 if (opt->lo_wishes->remoteside != 0)
212 return -1;
213 if (opt->state->remoteside)
214 return -2;
215 return send_telnet_neg( ({ DO, option }) );
216 break;
217 case DONT:
218 if (opt->lo_wishes->remoteside != 0)
219 return -1;
220 if (!opt->state->remoteside)
221 return -2;
222 return send_telnet_neg( ({ DONT, option }) );
223 break;
224 }
225 raise_error(sprintf("Unsupported telnet negotation action in "
226 "do_telnet_neg(): %d\n",action));
227}
228
229// LOCAL Standard Handlers //
230private void _std_lo_handler_eor(struct telopt_s opt, int action) {
231 // tatsaechlich nix zu tun. Handler ist nur da, damit die Option auf dieser
232 // Seite aktiviert wird. Die Arbeit erledigt print_prompt.
233 return;
234}
235
236private void _std_lo_handler_mssp(struct telopt_s opt, int action) {
237 // nur einschalten ist interessant.
238 if (action != LOCALON)
239 return;
240 // Krams senden, wenn Objekt geladen. Sonst wieder abschalten (kommt
241 // hoffentlich nicht vor)...
242 object mssp = find_object("/secure/misc/mssp");
243 if (!mssp)
244 send_telnet_neg( ({WONT, TELOPT_MSSP }) );
245 else
246 {
247 send_telnet_neg_str(sprintf("%c%c%s",
248 SB, TELOPT_MSSP, mssp->get_telnegs_str()));
249 // die Daten brauchen wir nicht mehr
250 opt->lo_wishes->sbdata = 0;
251 }
252}
253
254
255// REMOTE Standard Handlers //
256private void _std_re_handler_tm(struct telopt_s opt, int action,
257 int *data)
258{
259 // egal, was geantwortet wurde, es gibt nen Hinweis auf die round-trip-time.
260 // Wenn ein Array in opt->data[1] steht, rechnen wir das aus und schreiben es
261 // in opt->data[0] als Ergebnis rein.
262 if (pointerp(opt->data) && pointerp(opt->data[1]))
263 {
264 int *ut = utime();
265 int *start = opt->data[1];
266 int res = (ut[0] - start[0]) * 1000000;
267 res += ut[1] - start[1];
268 opt->data[0] = res;
269 opt->data[1] = 0;
270 DEBUG("RTT: "+res);
271 }
272 return;
273}
274
275private void _std_re_handler_naws(struct telopt_s opt, int action,
276 int *data)
277{
278 if (action == SB)
279 {
280 eval_naws(data);
281 }
282}
283
284private void _std_re_handler_linemode(struct telopt_s opt, int action,
285 int *data)
286{
287 if (action == REMOTEON)
288 {
289 // see /doc/concepts/negotiations. We use only the minimum
290 // needed for linemode: switching on local commandline-editing
291 // for the client.
292 send_telnet_neg(({ SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT }));
293 // flush on 0d and 0a...
294 // TODO: what does this exactly do?
295 send_telnet_neg(({ SB, TELOPT_LINEMODE, DO, LM_FORWARDMASK, 0,
296 0x40|0x08 }));
297 //Gna...
298 opt->lo_wishes->sbdata = ({MODE_EDIT});
299 }
300}
301
302private void _std_re_handler_ttype(struct telopt_s opt, int action,
303 int *data)
304{
305 if (action == SB)
306 {
307 //TODO: get rid of this hysterical stuff...
308 //NOTE: We do not do multiple SB SENDs due to some weird
309 //bugs in IBM3270 emulating telnets which crash if we
310 //do that.
311 if ( sizeof(data) < 1 )
312 return;
313
314 if ( data[0] != TELQUAL_IS )
315 return;
316
317 string tmpterminal = lower_case( to_string(data[1..]) );
318 if ( !Terminals )
319 Terminals = ({ tmpterminal });
320 else
321 Terminals += ({ tmpterminal });
322
323 if ( Query(P_TTY_TYPE) )
324 Set( P_TTY_TYPE, Terminals[0] );
325 }
326 else if (action == REMOTEON)
327 {
328 send_telnet_neg(({ SB, TELOPT_TTYPE, TELQUAL_SEND }));
329 }
330}
331
332// Bindet/registriert Handler fuer die jew. Telnet Option. (Oder loescht sie
333// auch wieder.) Je nach <initneg> wird versucht, die Option neu zu
334// verhandeln.
335protected int bind_telneg_handler(int option, closure re, closure lo,
336 int initneg)
337{
338 struct telopt_s opt = TN[option];
339 if (!structp(opt))
340 {
341 opt = (<telopt_s> option: option,
342 re_wishes: (<to_state_s>),
343 lo_wishes: (<to_state_s>),
344 state: (<to_state_s>)
345 );
346 TN[option] = opt;
347 }
348
349 opt->remotehandler = re;
350 if (initneg)
351 {
352 if (re)
353 do_telnet_neg(option, DO);
354 else
355 do_telnet_neg(option, DONT );
356 }
357
358 opt->localhandler = lo;
359 if (initneg)
360 {
361 if (lo)
362 do_telnet_neg(option, WILL);
363 else
364 do_telnet_neg(option, WONT);
365 }
366 return 1;
367}
368
369
370// Mal unsere Wuensche an den Client schicken und die Standardhandler
371// registrieren. Hierbei bei Bedarf neue Verhandlungen starten.
372// Gerufen aus login.c nach Verbindungsaufbau.
373// Bemerkung: das Spielerobjekt bietet evt. noch zusaetzliche Telnetoptionen
374// an, die dann ueber startup_telnet_negs() (im Spielerobjekt)
375// laufen.
376protected void SendTelopts()
377{
378 bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 1);
379 if (find_object("/secure/misc/mssp"))
380 bind_telneg_handler(TELOPT_MSSP, 0, #'_std_lo_handler_mssp, 1);
381
382 bind_telneg_handler(TELOPT_NAWS, #'_std_re_handler_naws, 0, 1);
383 bind_telneg_handler(TELOPT_LINEMODE, #'_std_re_handler_linemode, 0, 1);
384 bind_telneg_handler(TELOPT_TTYPE, #'_std_re_handler_ttype, 0, 1);
385 // fuer TELOPT_TM jetzt keine Verhandlung anstossen.
386 bind_telneg_handler(TELOPT_TM, #'_std_re_handler_tm, 0, 0);
387}
388
389
390// Bindet die Standardhandler _aus diesem_ Programm (und ueberschreibt dabei
391// ggf. andere). Hierbei werden nur die Handler neu gebunden, keine neuen
392// Verhandlungen initiiert.
393// gerufen aus base.c indirekt via startup_telnet_negs().
394protected void _bind_telneg_std_handlers() {
395 bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 0);
396 // Besondere Situation: MSSP ist nach Spielerlogin eigentlich uninteressant.
397 // Daher sparen wir uns das im Kontext des Spielerobjekts und schalten es
398 // einfach wieder aus.
399 bind_telneg_handler(TELOPT_MSSP, 0, 0, 0);
400
401 bind_telneg_handler(TELOPT_NAWS, #'_std_re_handler_naws, 0, 0);
402 bind_telneg_handler(TELOPT_LINEMODE, #'_std_re_handler_linemode, 0, 0);
403 bind_telneg_handler(TELOPT_TTYPE, #'_std_re_handler_ttype, 0, 0);
404 bind_telneg_handler(TELOPT_TM, #'_std_re_handler_tm, 0, 0);
405}
406
407
408// Ruft die entsprechenden handler von der Telnet Option.
409// Wenn es keinen handler (mehr) gibt, wird die Option auch auf der jeweiligen
410// Seite ausgeschaltet. Deshalb MUSS lo_wishes und re_wishes vom Aufrufer VOR
411// DEM AUFRUF zurueckgesetzt worden sein!
412// <action>: 'LOCALON': Option wurde auf unserer Seite eingeschaltet
413// 'LOCALOFF': Option wurde auf unserer Seite ausgeschaltet
414// 'REMOTEON': Option wurde auf Clientseite eingeschaltet
415// 'REMOTEOFF': Option wurde auf Clientseite ausgeschaltet
416// 'SB': Suboption negotiation Daten wurden empfangen
417// <data>: die per SB empfangenen Daten (unverarbeitet)
418private void _call_handler(struct telopt_s opt, int action, int *data) {
419 switch(action)
420 {
421 case REMOTEON:
422 case REMOTEOFF:
423 case SB:
424 if (opt->remotehandler)
425 {
426 funcall(opt->remotehandler, opt, action, data);
427 }
428 else
429 {
430 // ok, geht nicht. Ggf. Abschalten (do_telnet_neg passt auf,
431 // dass nur verhandelt wird, wenn die Option an ist.)
432 do_telnet_neg( opt->option, DONT );
433 }
434 break;
435 case LOCALON:
436 case LOCALOFF:
437 if (opt->localhandler)
438 {
439 funcall(opt->localhandler, opt, action);
440 }
441 else
442 {
443 // ok, geht nicht. Ggf. Abschalten (do_telnet_neg passt auf,
444 // dass nur verhandelt wird, wenn die Option an ist.)
445 do_telnet_neg( opt->option, WONT );
446 }
447 break;
448 }
449}
450
451// Gerufen vom Driver, wenn neue telnet options reinkommen.
452void
453telnet_neg(int command, int option, int *optargs)
454{
455 DTN("recv_tn: ", ({IAC, command, option}) + (optargs||({})));
456
457 struct telopt_s opt = TN[option];
458 if (!structp(opt))
459 {
460 opt = (<telopt_s> option: option,
461 re_wishes: (<to_state_s>),
462 lo_wishes: (<to_state_s>),
463 state: (<to_state_s>)
464 );
465 TN[option] = opt;
466 }
467
468 // Was will der Client tun?
469 if (command == WONT)
470 {
471 // Client will die Option auf seiner Seite abschalten. Wir MUESSEN das
472 // akzeptieren.
473 // Wir muessen das allerdings ignorieren, wenn die Option bereits aus
474 // ist.
475 if (opt->state->remoteside==0)
476 {
477 // Ausnahme fuer TELOPT_TM, da das kaum ein Client kann und fuer RTT
478 // es eigentlich auch egal ist, was zurueck kommt: der handler wird
479 // zumindest doch gerufen zum Ausrechnen der RTT
480 if (option == TELOPT_TM)
481 _call_handler(opt, REMOTEOFF, 0);
482 // ansonsten aber wirklich ignorieren. ;)
483 return;
484 }
485 opt->re_wishes->remoteside = command;
486 // Bestaetigung auf ein WONT senden, wenn wir nicht selber schon ein
487 // DONT geschickt hatten.
488 if (opt->lo_wishes->remoteside != DONT) {
489 send_telnet_neg( ({DONT, option}) );
490 }
491 // Wir haben jetzt auf jeden Fall ein DONT gesendet und ein WONT
492 // erhalten. Damit ist die Option jetzt auf der clientseite aus.
493 // Ausserdem setzen wir die Wishes zurueck.
494 opt->re_wishes->remoteside = 0;
495 opt->lo_wishes->remoteside = 0;
496 if (opt->state->remoteside != 0)
497 {
498 opt->state->remoteside = 0;
499 _call_handler(opt, REMOTEOFF, 0);
500 }
501 } // WONT vom Client verarbeitet
502 else if ( command == WILL)
503 {
504 // Wenn die Option bereits an ist, muessen wir dies ignorieren.
505 if (opt->state->remoteside == 1)
506 {
507 // Ausnahme fuer TELOPT_TM, der handler wird zumindest doch gerufen
508 // zum Ausrechnen der RTT. Diese Option laesst sich ohnehin
509 // aktivieren, auch wenn sie schon an ist.
510 if (option == TELOPT_TM)
511 _call_handler(opt, REMOTEON, 0);
512 // sonst aber wirklich ignorieren. ;-)
513 return;
514 }
515 opt->re_wishes->remoteside = command;
516 if ( opt->lo_wishes->remoteside == 0 )
517 {
518 // Der Client will, wir haben noch nix dazu gesagt. (Mit unserer
519 // Antwort ist die Verhandlung uebrigens beendet.)
520 // Wenn es einen remotehandler fuer die Option gibt, schalten wir
521 // sie ein...
522 if (opt->remotehandler)
523 {
524 send_telnet_neg(({DO, option}));
525 // Option jetzt an der Clientseite an.
526 opt->re_wishes->remoteside = 0;
527 opt->lo_wishes->remoteside = 0;
528 if (opt->state->remoteside != 1)
529 {
530 opt->state->remoteside = 1;
531 _call_handler(opt, REMOTEON, 0);
532 }
533 }
534 else
535 {
536 // sonst verweigern wir das einschalten (die meisten Optionen
537 // auf Clientseite sind fuer uns eh egal).
538 send_telnet_neg(({DONT, option}));
539 // Option jetzt an der Clientseite aus.
540 opt->re_wishes->remoteside = 0;
541 opt->lo_wishes->remoteside = 0;
542 if (opt->state->remoteside != 0)
543 {
544 opt->state->remoteside = 0;
545 _call_handler(opt, REMOTEOFF, 0);
546 }
547 }
548 }
549 else if ( opt->lo_wishes->remoteside == DO)
550 {
551 // Wir haben haben bereits per DO angefordert, d.h. das ist die
552 // Clientbestaetigung - wir duerfen nicht bestaetigen und die
553 // Option ist jetzt clientseitig aktiv. Verhandlung beendet.
554 opt->re_wishes->remoteside = 0;
555 opt->lo_wishes->remoteside = 0;
556 if (opt->state->remoteside != 1)
557 {
558 opt->state->remoteside = 1;
559 _call_handler(opt, REMOTEON, 0);
560 }
561 } // if (DO)
562 else {
563 // Mhmm. Wir hatten ein DONT gesendet, aber der Client hat mit WILL
564 // geantwortet. Das darf er eigentlich gar nicht.
565 //TODO: was sollte man jetzt eigentlich tun? Erstmal wiederholen wir
566 //das DONT...
567 send_telnet_neg( ({DONT, option}) );
568 }
569
570 return;
571 } // WILL vom Client verarbeitet
572 // Was sollen wir (nicht) fuer den Client tun?
573 else if ( command == DONT)
574 {
575 // Client will, dass wir etwas nicht tun. Wir MUESSEN das akzeptieren.
576 // wenn die Option auf unserer Seite aber schon aus ist, muessen wir
577 // dies ignorieren.
578 if (opt->state->localside == 0)
579 return;
580
581 opt->re_wishes->localside = command;
582 // Wenn wir noch kein WONT gesendet haben, senden wir das jetzt als
583 // Bestaetigung.
584 if (opt->lo_wishes->localside = WONT)
585 send_telnet_neg( ({WONT, option}) );
586 // Verhandlung beendet, Option is auf unserer Seite jetzt aus.
587 // Wuensche auch wieder zuruecksetzen.
588 opt->re_wishes->localside = 0;
589 opt->lo_wishes->localside = 0;
590 if (opt->state->localside != 0)
591 {
592 opt->state->localside = 0;
593 _call_handler(opt, LOCALOFF, 0);
594 }
595 }
596 else if ( command == DO )
597 {
598 // Client will, dass wir option tun. Mal schauen, wie wir dazu stehen.
599 // wenn die Option auf unserer Seite aber schon an ist, muessen wir
600 // dies ignorieren.
601 if (opt->state->localside == 1)
602 return;
603
604 opt->re_wishes->localside = command;
605
606 if ( opt->lo_wishes->localside == 0 ) {
607 // wir haben unsere Wuensche noch nicht geaeussert. Sobald wir
608 // bestaetigen, ist die Option auf unserer Seite an/aus und die
609 // Verhandlungen beendet.
610 // in jedem Fall die Wuensche zuruecksetzen
611 opt->re_wishes->localside = 0;
612 opt->lo_wishes->localside = 0;
613 if (opt->localhandler)
614 {
615 send_telnet_neg(({WILL, option}));
616 opt->state->localside = 1;
617 _call_handler(opt, LOCALON, 0);
618 }
619 else
620 {
621 send_telnet_neg(({WONT, option}));
622 opt->state->localside = 0;
623 _call_handler(opt, LOCALOFF, 0);
624 }
625 }
626 else if (opt->lo_wishes->localside == WILL ) {
627 // wir haben schon WILL gesendet, welches der Client jetzt
628 // bestaetigt hat (d.h. die Option ist jetzt auf dieser Seite an),
629 // wir bestaetigen das aber nicht (nochmal).
630 opt->re_wishes->localside = 0;
631 opt->lo_wishes->localside = 0;
632 if (opt->state->localside != 1)
633 {
634 opt->state->localside = 1;
635 _call_handler(opt, LOCALON, 0);
636 }
637 }
638 else {
639 // Mhmm. Wir haben ein WONT gesendet, der Client hat mit DO
640 // geantwortet. Das darf er eigentlich nicht.
641 // TODO: Was tun?
642 send_telnet_neg ( ({WONT, option}) );
643 }
644 // fertig mit DO
645 return;
646 }
647 // bleibt noch SB ueber
648 else if ( command == SB )
649 {
650 opt->re_wishes->sbdata = optargs;
651 _call_handler(opt, SB, optargs);
652 return;
653 } // if ( command == SB )
654}
655
656// wird nur in base.c gerufen, wenn die Verbindung an das Spielerobjekt
657// uebergeben wurde.
658// es uebertraegt unter anderem den Telnet Option Zustand aus login.c (das ist
659// dann previous_object()) in das Spielerobjekt (welches dann this_object())
660// ist!
661protected void
662startup_telnet_negs()
663{
664 int* optargs;
665
666 Set( P_TTY_TYPE, 0 ); //avoid ANY mistakes... Wird unten neu gesetzt.
667 // Daten aus dem Loginobjekt uebertragen. Das ist wichtig! (Dabei wird dann
668 // auch der Status von der letzten Session ueberschrieben.)
669 TN = (mapping) previous_object()->query_telnet_neg();
670 // bevor irgendwas anderes gemacht wird, werden erstmal die Standardhandler
671 // gesetzt. Die sind naemlich in diesem Objekt jetzt erstmal kaputt, weil
672 // sie im Loginobjekt gerufen werden.
673 _bind_telneg_std_handlers();
674 // dann restliche Daten aus dem Loginobjekt holen.
675 Terminals = (string *) previous_object()->query_terminals();
676 Set( P_TTY_COLS, previous_object()->Query(P_TTY_COLS) );
677 Set( P_TTY_ROWS, previous_object()->Query(P_TTY_ROWS) );
678
679 struct telopt_s opt = TN[TELOPT_NAWS];
680 if (optargs = (opt->re_wishes)->sbdata) {
681 eval_naws(optargs);
682 }
683
684 if ( pointerp(Terminals) && sizeof(Terminals)) {
685 if ( Terminals[0][0..3] == "dec-" )
686 Terminals[0] = Terminals[0][4..];
687
688 if ( Terminals[0] == "linux" )
689 Terminals[0] = "vt100";
690
691 Set( P_TTY_TYPE, Terminals[0] );
692 }
693}
694
695// somehow completely out of the ordinary options processing/negotiation. But
696// the only purpose is to transmit something over the wire which is not shown,
697// but (likely) answered by the other device.
698protected void send_telnet_timing_mark() {
699 struct telopt_s opt = TN[TELOPT_TM];
700 if (pointerp(opt->data))
701 opt->data[1] = utime();
702 else
703 opt->data = ({ 0, utime() });
704 // absichtlich nicht do_telnet_ne() verwendet, da dies nicht senden wuerde,
705 // haette der Client schonmal mit WILL geantwortet. TELOPT_TM ist aber eine
706 // Option, bei der man das darf...
707 send_telnet_neg( ({DO, TELOPT_TM}) );
708}
709
710/* Is called from the H_PRINT_PROMPT driver hook and appends the IAC EOR if
711 * the client supports it.
712 */
713void print_prompt(string prompt) {
714// if (extern_call() && previous_object()!=this_object())
715// return;
716
717 // ggf. Uhrzeit in den prompt reinschreiben.
718 prompt = regreplace(prompt,"\\t",strftime("%H:%M"),0);
719 // Prompt senden
720 tell_object(this_object(), prompt);
721 // Und EOR senden, falls vom Client gewuenscht.
722 struct telopt_s opt = TN[TELOPT_EOR];
723 if (opt->state->localside == 1)
724 {
725 binary_message(({IAC, EOR}), 1);
726 DTN("tn_eor ",({IAC,EOR}));
727 }
728}
729
730// Helper
731private void eval_naws(int *optargs) {
732 int l, c;
733
734 if ( sizeof(optargs) != 4 )
735 {
736 tell_object(this_object(),
737 break_string( sprintf("Dein Client hat einen Fehler beim"
738 +"Aushandeln der TELOPT_NAWS - er hat"
739 +"IAC SB %O IAC SE gesendet!\n",
740 optargs), 78,
741 "Der GameDriver teilt Dir mit: " ));
742 // und dem Client sagen, dass er den Schrott nicht mehr uebertragen
743 // soll (falls wir das nicht schon gemacht haben).
744 struct telopt_s opt = TN[TELOPT_NAWS];
745 if (opt->state->remoteside == WILL
746 && opt->lo_wishes->remoteside != DONT)
747 send_telnet_neg(( {DONT, TELOPT_NAWS}) );
748 return;
749 }
750
751 if ( interactive(this_object()) ){
752 if ( !optargs[1] )
753 c = optargs[0];
754 else
755 c = optargs[1] + optargs[0] * 256;
756
757 if ( c < 35 ){
758 if (Query(P_TTY_SHOW))
759 tell_object( this_object(),
760 break_string("Dein Fenster ist schmaler als"
761 +" 35 Zeichen? Du scherzt. ;-)"
762 +" Ich benutze den Standardwert"
763 +" von 80 Zeichen.\n", 78,
764 "Der GameDriver teilt Dir mit: ")
765 );
766 c = 80;
767 }
768
769 if ( !optargs[3] )
770 l = optargs[2];
771 else
772 l = 256 * optargs[2] + optargs[3];
773
774 if ( l > 100 ){
775 //TODO: remove
776 l = 100;
777 if (Query(P_TTY_SHOW))
778 tell_object( this_object(),
779 break_string("Tut mir leid, aber ich kann"
780 +" nur bis zu 100 Zeilen"
781 +" verwalten.\n", (c ? c-2 : 78),
782 "Der GameDriver teilt Dir mit: " )
783 );
784 }
785
786 if ( l < 3 ){
787 if (Query(P_TTY_SHOW))
788 tell_object( this_object(),
789 break_string("Du willst weniger als drei"
790 +" Zeilen benutzen? Glaub ich"
791 +" Dir nicht - ich benutze den"
792 +" Standardwert von 24"
793 +" Zeilen.\n", (c ? c-2 : 78),
794 "Der GameDriver teilt Dir mit: " )
795 );
796 l = 24;
797 }
798
799 if ( ((int) Query(P_TTY_ROWS) != l) ||
800 ((int) Query(P_TTY_COLS) != c) ){
801 Set( P_TTY_ROWS, l );
802 Set( P_TTY_COLS, c );
803
804 if (Query(P_TTY_SHOW))
805 tell_object( this_object(),
806 break_string("Du hast Deine Fenstergroesse auf"
807 +" "+l+" Zeilen und "+c+
808 " Spalten geaendert.\n", c-2,
809 "Der GameDriver teilt Dir mit: ")
810 );
811 }
812 }
813}
814
815private void _call_old_SendTelOpts(object po) {
816 if (!objectp(po) || !interactive(po)) return;
817/*
818 closure cl=unbound_lambda( ({}),
819 ({ #'funcall, ({#'symbol_function, "SendTelopts"}) }) );
820
821 funcall(bind_lambda(cl, po));
822*/
823 // Bloody temporary Hack until next reboot...
824
825 funcall( bind_lambda( #'efun::binary_message, po ),
826 ({ IAC, WILL, TELOPT_EOR,
827 IAC, DO, TELOPT_TTYPE,
828 IAC, DO, TELOPT_NAWS,
829 IAC, DO, TELOPT_LINEMODE,
830 }), 0x1 );
831}
832
833// Query-/Set-Methoden
834// Und wenn hier einer von aussen dran rumpfuscht, werde ich sauer.
835mapping
836query_telnet_neg()
837{
838 if (interactive(previous_object())
839 && program_time(previous_object()) < 1359926079
840 && load_name(this_object()) == "/secure/login" )
841 {
842 call_out(#'_call_old_SendTelOpts, 0, previous_object());
843 // alte Datenstruktur zurueckgeben... Leider leer...
844 return (["sent": m_allocate(3,3), "received": m_allocate(3,3) ]);
845 }
846
847 return TN;
848}
849
850// siehe oben
851string *
852query_terminals() {
853 return Terminals;
854}
855
856public int _query_p_lib_telnet_rttime()
857{
858 struct telopt_s opt = TN[TELOPT_TM];
859 if (opt && pointerp(opt->data))
860 return (opt->data)[0];
861 return 0;
862}
863