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