blob: 706979102776eed89cbbe041d488a700c35a5dcd [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
Zesstrae06d75a2019-09-26 21:02:53 +0200137protected varargs int send_telnet_neg_str(bytes str, int bm_flags) {
MG Mud User88f12472016-06-24 23:31:02 +0200138#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];
Zesstra9ebed822019-11-27 19:50:17 +0100151 str = to_bytes(({IAC})) + str;
MG Mud User88f12472016-06-24 23:31:02 +0200152 break;
153 case WILL:
154 case WONT:
155 (opt->lo_wishes)->localside = str[0];
Zesstra9ebed822019-11-27 19:50:17 +0100156 str = to_bytes(({IAC})) + str;
MG Mud User88f12472016-06-24 23:31:02 +0200157 break;
158 case SB:
Zesstra9ebed822019-11-27 19:50:17 +0100159 (opt->lo_wishes)->sbdata = to_array(str[1..]);
160 str = to_bytes(({IAC})) + str + to_bytes(({IAC,SE}));
MG Mud User88f12472016-06-24 23:31:02 +0200161 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 {
Zesstra9ebed822019-11-27 19:50:17 +0100247 send_telnet_neg_str(
248 to_bytes(({SB, TELOPT_MSSP}))
249 + to_bytes(sprintf("%s", mssp->get_telnegs_str()),
250 "ASCII//TRANSLIT"));
MG Mud User88f12472016-06-24 23:31:02 +0200251 // die Daten brauchen wir nicht mehr
252 opt->lo_wishes->sbdata = 0;
253 }
254}
255
256
257// REMOTE Standard Handlers //
258private void _std_re_handler_tm(struct telopt_s opt, int action,
259 int *data)
260{
261 // egal, was geantwortet wurde, es gibt nen Hinweis auf die round-trip-time.
262 // Wenn ein Array in opt->data[1] steht, rechnen wir das aus und schreiben es
263 // in opt->data[0] als Ergebnis rein.
264 if (pointerp(opt->data) && pointerp(opt->data[1]))
265 {
266 int *ut = utime();
267 int *start = opt->data[1];
268 int res = (ut[0] - start[0]) * 1000000;
269 res += ut[1] - start[1];
270 opt->data[0] = res;
271 opt->data[1] = 0;
272 DEBUG("RTT: "+res);
273 }
274 return;
275}
276
277private void _std_re_handler_naws(struct telopt_s opt, int action,
278 int *data)
279{
280 if (action == SB)
281 {
282 eval_naws(data);
283 }
284}
285
286private void _std_re_handler_linemode(struct telopt_s opt, int action,
287 int *data)
288{
289 if (action == REMOTEON)
290 {
291 // see /doc/concepts/negotiations. We use only the minimum
292 // needed for linemode: switching on local commandline-editing
293 // for the client.
294 send_telnet_neg(({ SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT }));
295 // flush on 0d and 0a...
296 // TODO: what does this exactly do?
297 send_telnet_neg(({ SB, TELOPT_LINEMODE, DO, LM_FORWARDMASK, 0,
298 0x40|0x08 }));
299 //Gna...
300 opt->lo_wishes->sbdata = ({MODE_EDIT});
301 }
302}
303
304private void _std_re_handler_ttype(struct telopt_s opt, int action,
305 int *data)
306{
307 if (action == SB)
308 {
309 //TODO: get rid of this hysterical stuff...
310 //NOTE: We do not do multiple SB SENDs due to some weird
311 //bugs in IBM3270 emulating telnets which crash if we
312 //do that.
313 if ( sizeof(data) < 1 )
314 return;
315
316 if ( data[0] != TELQUAL_IS )
317 return;
318
319 string tmpterminal = lower_case( to_string(data[1..]) );
320 if ( !Terminals )
321 Terminals = ({ tmpterminal });
322 else
323 Terminals += ({ tmpterminal });
324
325 if ( Query(P_TTY_TYPE) )
326 Set( P_TTY_TYPE, Terminals[0] );
327 }
328 else if (action == REMOTEON)
329 {
330 send_telnet_neg(({ SB, TELOPT_TTYPE, TELQUAL_SEND }));
331 }
332}
333
334// Bindet/registriert Handler fuer die jew. Telnet Option. (Oder loescht sie
335// auch wieder.) Je nach <initneg> wird versucht, die Option neu zu
336// verhandeln.
337protected int bind_telneg_handler(int option, closure re, closure lo,
338 int initneg)
339{
340 struct telopt_s opt = TN[option];
341 if (!structp(opt))
342 {
343 opt = (<telopt_s> option: option,
344 re_wishes: (<to_state_s>),
345 lo_wishes: (<to_state_s>),
346 state: (<to_state_s>)
347 );
348 TN[option] = opt;
349 }
350
351 opt->remotehandler = re;
352 if (initneg)
353 {
354 if (re)
355 do_telnet_neg(option, DO);
356 else
357 do_telnet_neg(option, DONT );
358 }
359
360 opt->localhandler = lo;
361 if (initneg)
362 {
363 if (lo)
364 do_telnet_neg(option, WILL);
365 else
366 do_telnet_neg(option, WONT);
367 }
368 return 1;
369}
370
371
372// Mal unsere Wuensche an den Client schicken und die Standardhandler
373// registrieren. Hierbei bei Bedarf neue Verhandlungen starten.
374// Gerufen aus login.c nach Verbindungsaufbau.
375// Bemerkung: das Spielerobjekt bietet evt. noch zusaetzliche Telnetoptionen
376// an, die dann ueber startup_telnet_negs() (im Spielerobjekt)
377// laufen.
378protected void SendTelopts()
379{
380 bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 1);
381 if (find_object("/secure/misc/mssp"))
382 bind_telneg_handler(TELOPT_MSSP, 0, #'_std_lo_handler_mssp, 1);
383
384 bind_telneg_handler(TELOPT_NAWS, #'_std_re_handler_naws, 0, 1);
385 bind_telneg_handler(TELOPT_LINEMODE, #'_std_re_handler_linemode, 0, 1);
386 bind_telneg_handler(TELOPT_TTYPE, #'_std_re_handler_ttype, 0, 1);
387 // fuer TELOPT_TM jetzt keine Verhandlung anstossen.
388 bind_telneg_handler(TELOPT_TM, #'_std_re_handler_tm, 0, 0);
389}
390
391
392// Bindet die Standardhandler _aus diesem_ Programm (und ueberschreibt dabei
393// ggf. andere). Hierbei werden nur die Handler neu gebunden, keine neuen
394// Verhandlungen initiiert.
395// gerufen aus base.c indirekt via startup_telnet_negs().
396protected void _bind_telneg_std_handlers() {
397 bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 0);
398 // Besondere Situation: MSSP ist nach Spielerlogin eigentlich uninteressant.
399 // Daher sparen wir uns das im Kontext des Spielerobjekts und schalten es
400 // einfach wieder aus.
401 bind_telneg_handler(TELOPT_MSSP, 0, 0, 0);
402
403 bind_telneg_handler(TELOPT_NAWS, #'_std_re_handler_naws, 0, 0);
404 bind_telneg_handler(TELOPT_LINEMODE, #'_std_re_handler_linemode, 0, 0);
405 bind_telneg_handler(TELOPT_TTYPE, #'_std_re_handler_ttype, 0, 0);
406 bind_telneg_handler(TELOPT_TM, #'_std_re_handler_tm, 0, 0);
407}
408
409
410// Ruft die entsprechenden handler von der Telnet Option.
411// Wenn es keinen handler (mehr) gibt, wird die Option auch auf der jeweiligen
412// Seite ausgeschaltet. Deshalb MUSS lo_wishes und re_wishes vom Aufrufer VOR
413// DEM AUFRUF zurueckgesetzt worden sein!
414// <action>: 'LOCALON': Option wurde auf unserer Seite eingeschaltet
415// 'LOCALOFF': Option wurde auf unserer Seite ausgeschaltet
416// 'REMOTEON': Option wurde auf Clientseite eingeschaltet
417// 'REMOTEOFF': Option wurde auf Clientseite ausgeschaltet
418// 'SB': Suboption negotiation Daten wurden empfangen
419// <data>: die per SB empfangenen Daten (unverarbeitet)
420private void _call_handler(struct telopt_s opt, int action, int *data) {
421 switch(action)
422 {
423 case REMOTEON:
424 case REMOTEOFF:
425 case SB:
426 if (opt->remotehandler)
427 {
428 funcall(opt->remotehandler, opt, action, data);
429 }
430 else
431 {
432 // ok, geht nicht. Ggf. Abschalten (do_telnet_neg passt auf,
433 // dass nur verhandelt wird, wenn die Option an ist.)
434 do_telnet_neg( opt->option, DONT );
435 }
436 break;
437 case LOCALON:
438 case LOCALOFF:
439 if (opt->localhandler)
440 {
441 funcall(opt->localhandler, opt, action);
442 }
443 else
444 {
445 // ok, geht nicht. Ggf. Abschalten (do_telnet_neg passt auf,
446 // dass nur verhandelt wird, wenn die Option an ist.)
447 do_telnet_neg( opt->option, WONT );
448 }
449 break;
450 }
451}
452
453// Gerufen vom Driver, wenn neue telnet options reinkommen.
454void
455telnet_neg(int command, int option, int *optargs)
456{
457 DTN("recv_tn: ", ({IAC, command, option}) + (optargs||({})));
458
459 struct telopt_s opt = TN[option];
460 if (!structp(opt))
461 {
462 opt = (<telopt_s> option: option,
463 re_wishes: (<to_state_s>),
464 lo_wishes: (<to_state_s>),
465 state: (<to_state_s>)
466 );
467 TN[option] = opt;
468 }
469
470 // Was will der Client tun?
471 if (command == WONT)
472 {
473 // Client will die Option auf seiner Seite abschalten. Wir MUESSEN das
474 // akzeptieren.
475 // Wir muessen das allerdings ignorieren, wenn die Option bereits aus
476 // ist.
477 if (opt->state->remoteside==0)
478 {
479 // Ausnahme fuer TELOPT_TM, da das kaum ein Client kann und fuer RTT
480 // es eigentlich auch egal ist, was zurueck kommt: der handler wird
481 // zumindest doch gerufen zum Ausrechnen der RTT
482 if (option == TELOPT_TM)
483 _call_handler(opt, REMOTEOFF, 0);
484 // ansonsten aber wirklich ignorieren. ;)
485 return;
486 }
487 opt->re_wishes->remoteside = command;
488 // Bestaetigung auf ein WONT senden, wenn wir nicht selber schon ein
489 // DONT geschickt hatten.
490 if (opt->lo_wishes->remoteside != DONT) {
491 send_telnet_neg( ({DONT, option}) );
492 }
493 // Wir haben jetzt auf jeden Fall ein DONT gesendet und ein WONT
494 // erhalten. Damit ist die Option jetzt auf der clientseite aus.
495 // Ausserdem setzen wir die Wishes zurueck.
496 opt->re_wishes->remoteside = 0;
497 opt->lo_wishes->remoteside = 0;
498 if (opt->state->remoteside != 0)
499 {
500 opt->state->remoteside = 0;
501 _call_handler(opt, REMOTEOFF, 0);
502 }
503 } // WONT vom Client verarbeitet
504 else if ( command == WILL)
505 {
506 // Wenn die Option bereits an ist, muessen wir dies ignorieren.
507 if (opt->state->remoteside == 1)
508 {
509 // Ausnahme fuer TELOPT_TM, der handler wird zumindest doch gerufen
510 // zum Ausrechnen der RTT. Diese Option laesst sich ohnehin
511 // aktivieren, auch wenn sie schon an ist.
512 if (option == TELOPT_TM)
513 _call_handler(opt, REMOTEON, 0);
514 // sonst aber wirklich ignorieren. ;-)
515 return;
516 }
517 opt->re_wishes->remoteside = command;
518 if ( opt->lo_wishes->remoteside == 0 )
519 {
520 // Der Client will, wir haben noch nix dazu gesagt. (Mit unserer
521 // Antwort ist die Verhandlung uebrigens beendet.)
522 // Wenn es einen remotehandler fuer die Option gibt, schalten wir
523 // sie ein...
524 if (opt->remotehandler)
525 {
526 send_telnet_neg(({DO, option}));
527 // Option jetzt an der Clientseite an.
528 opt->re_wishes->remoteside = 0;
529 opt->lo_wishes->remoteside = 0;
530 if (opt->state->remoteside != 1)
531 {
532 opt->state->remoteside = 1;
533 _call_handler(opt, REMOTEON, 0);
534 }
535 }
536 else
537 {
538 // sonst verweigern wir das einschalten (die meisten Optionen
539 // auf Clientseite sind fuer uns eh egal).
540 send_telnet_neg(({DONT, option}));
541 // Option jetzt an der Clientseite aus.
542 opt->re_wishes->remoteside = 0;
543 opt->lo_wishes->remoteside = 0;
544 if (opt->state->remoteside != 0)
545 {
546 opt->state->remoteside = 0;
547 _call_handler(opt, REMOTEOFF, 0);
548 }
549 }
550 }
551 else if ( opt->lo_wishes->remoteside == DO)
552 {
553 // Wir haben haben bereits per DO angefordert, d.h. das ist die
554 // Clientbestaetigung - wir duerfen nicht bestaetigen und die
555 // Option ist jetzt clientseitig aktiv. Verhandlung beendet.
556 opt->re_wishes->remoteside = 0;
557 opt->lo_wishes->remoteside = 0;
558 if (opt->state->remoteside != 1)
559 {
560 opt->state->remoteside = 1;
561 _call_handler(opt, REMOTEON, 0);
562 }
563 } // if (DO)
564 else {
565 // Mhmm. Wir hatten ein DONT gesendet, aber der Client hat mit WILL
566 // geantwortet. Das darf er eigentlich gar nicht.
567 //TODO: was sollte man jetzt eigentlich tun? Erstmal wiederholen wir
568 //das DONT...
569 send_telnet_neg( ({DONT, option}) );
570 }
571
572 return;
573 } // WILL vom Client verarbeitet
574 // Was sollen wir (nicht) fuer den Client tun?
575 else if ( command == DONT)
576 {
577 // Client will, dass wir etwas nicht tun. Wir MUESSEN das akzeptieren.
578 // wenn die Option auf unserer Seite aber schon aus ist, muessen wir
579 // dies ignorieren.
580 if (opt->state->localside == 0)
581 return;
582
583 opt->re_wishes->localside = command;
584 // Wenn wir noch kein WONT gesendet haben, senden wir das jetzt als
585 // Bestaetigung.
586 if (opt->lo_wishes->localside = WONT)
587 send_telnet_neg( ({WONT, option}) );
588 // Verhandlung beendet, Option is auf unserer Seite jetzt aus.
589 // Wuensche auch wieder zuruecksetzen.
590 opt->re_wishes->localside = 0;
591 opt->lo_wishes->localside = 0;
592 if (opt->state->localside != 0)
593 {
594 opt->state->localside = 0;
595 _call_handler(opt, LOCALOFF, 0);
596 }
597 }
598 else if ( command == DO )
599 {
600 // Client will, dass wir option tun. Mal schauen, wie wir dazu stehen.
601 // wenn die Option auf unserer Seite aber schon an ist, muessen wir
602 // dies ignorieren.
603 if (opt->state->localside == 1)
604 return;
605
606 opt->re_wishes->localside = command;
607
608 if ( opt->lo_wishes->localside == 0 ) {
609 // wir haben unsere Wuensche noch nicht geaeussert. Sobald wir
610 // bestaetigen, ist die Option auf unserer Seite an/aus und die
611 // Verhandlungen beendet.
612 // in jedem Fall die Wuensche zuruecksetzen
613 opt->re_wishes->localside = 0;
614 opt->lo_wishes->localside = 0;
615 if (opt->localhandler)
616 {
617 send_telnet_neg(({WILL, option}));
618 opt->state->localside = 1;
619 _call_handler(opt, LOCALON, 0);
620 }
621 else
622 {
623 send_telnet_neg(({WONT, option}));
624 opt->state->localside = 0;
625 _call_handler(opt, LOCALOFF, 0);
626 }
627 }
628 else if (opt->lo_wishes->localside == WILL ) {
629 // wir haben schon WILL gesendet, welches der Client jetzt
630 // bestaetigt hat (d.h. die Option ist jetzt auf dieser Seite an),
631 // wir bestaetigen das aber nicht (nochmal).
632 opt->re_wishes->localside = 0;
633 opt->lo_wishes->localside = 0;
634 if (opt->state->localside != 1)
635 {
636 opt->state->localside = 1;
637 _call_handler(opt, LOCALON, 0);
638 }
639 }
640 else {
641 // Mhmm. Wir haben ein WONT gesendet, der Client hat mit DO
642 // geantwortet. Das darf er eigentlich nicht.
643 // TODO: Was tun?
644 send_telnet_neg ( ({WONT, option}) );
645 }
646 // fertig mit DO
647 return;
648 }
649 // bleibt noch SB ueber
650 else if ( command == SB )
651 {
652 opt->re_wishes->sbdata = optargs;
653 _call_handler(opt, SB, optargs);
654 return;
655 } // if ( command == SB )
656}
657
658// wird nur in base.c gerufen, wenn die Verbindung an das Spielerobjekt
659// uebergeben wurde.
660// es uebertraegt unter anderem den Telnet Option Zustand aus login.c (das ist
661// dann previous_object()) in das Spielerobjekt (welches dann this_object())
662// ist!
663protected void
664startup_telnet_negs()
665{
666 int* optargs;
667
668 Set( P_TTY_TYPE, 0 ); //avoid ANY mistakes... Wird unten neu gesetzt.
669 // Daten aus dem Loginobjekt uebertragen. Das ist wichtig! (Dabei wird dann
670 // auch der Status von der letzten Session ueberschrieben.)
671 TN = (mapping) previous_object()->query_telnet_neg();
672 // bevor irgendwas anderes gemacht wird, werden erstmal die Standardhandler
673 // gesetzt. Die sind naemlich in diesem Objekt jetzt erstmal kaputt, weil
674 // sie im Loginobjekt gerufen werden.
675 _bind_telneg_std_handlers();
676 // dann restliche Daten aus dem Loginobjekt holen.
677 Terminals = (string *) previous_object()->query_terminals();
678 Set( P_TTY_COLS, previous_object()->Query(P_TTY_COLS) );
679 Set( P_TTY_ROWS, previous_object()->Query(P_TTY_ROWS) );
680
681 struct telopt_s opt = TN[TELOPT_NAWS];
682 if (optargs = (opt->re_wishes)->sbdata) {
683 eval_naws(optargs);
684 }
685
686 if ( pointerp(Terminals) && sizeof(Terminals)) {
687 if ( Terminals[0][0..3] == "dec-" )
688 Terminals[0] = Terminals[0][4..];
689
690 if ( Terminals[0] == "linux" )
691 Terminals[0] = "vt100";
692
693 Set( P_TTY_TYPE, Terminals[0] );
694 }
695}
696
697// somehow completely out of the ordinary options processing/negotiation. But
698// the only purpose is to transmit something over the wire which is not shown,
699// but (likely) answered by the other device.
700protected void send_telnet_timing_mark() {
701 struct telopt_s opt = TN[TELOPT_TM];
702 if (pointerp(opt->data))
703 opt->data[1] = utime();
704 else
705 opt->data = ({ 0, utime() });
706 // absichtlich nicht do_telnet_ne() verwendet, da dies nicht senden wuerde,
707 // haette der Client schonmal mit WILL geantwortet. TELOPT_TM ist aber eine
708 // Option, bei der man das darf...
709 send_telnet_neg( ({DO, TELOPT_TM}) );
710}
711
712/* Is called from the H_PRINT_PROMPT driver hook and appends the IAC EOR if
713 * the client supports it.
714 */
715void print_prompt(string prompt) {
716// if (extern_call() && previous_object()!=this_object())
717// return;
718
719 // ggf. Uhrzeit in den prompt reinschreiben.
720 prompt = regreplace(prompt,"\\t",strftime("%H:%M"),0);
721 // Prompt senden
722 tell_object(this_object(), prompt);
723 // Und EOR senden, falls vom Client gewuenscht.
724 struct telopt_s opt = TN[TELOPT_EOR];
725 if (opt->state->localside == 1)
726 {
727 binary_message(({IAC, EOR}), 1);
728 DTN("tn_eor ",({IAC,EOR}));
729 }
730}
731
732// Helper
733private void eval_naws(int *optargs) {
734 int l, c;
735
736 if ( sizeof(optargs) != 4 )
737 {
738 tell_object(this_object(),
739 break_string( sprintf("Dein Client hat einen Fehler beim"
740 +"Aushandeln der TELOPT_NAWS - er hat"
741 +"IAC SB %O IAC SE gesendet!\n",
742 optargs), 78,
743 "Der GameDriver teilt Dir mit: " ));
744 // und dem Client sagen, dass er den Schrott nicht mehr uebertragen
745 // soll (falls wir das nicht schon gemacht haben).
746 struct telopt_s opt = TN[TELOPT_NAWS];
747 if (opt->state->remoteside == WILL
748 && opt->lo_wishes->remoteside != DONT)
749 send_telnet_neg(( {DONT, TELOPT_NAWS}) );
750 return;
751 }
752
753 if ( interactive(this_object()) ){
754 if ( !optargs[1] )
755 c = optargs[0];
756 else
757 c = optargs[1] + optargs[0] * 256;
758
759 if ( c < 35 ){
760 if (Query(P_TTY_SHOW))
761 tell_object( this_object(),
762 break_string("Dein Fenster ist schmaler als"
763 +" 35 Zeichen? Du scherzt. ;-)"
764 +" Ich benutze den Standardwert"
765 +" von 80 Zeichen.\n", 78,
766 "Der GameDriver teilt Dir mit: ")
767 );
768 c = 80;
769 }
770
771 if ( !optargs[3] )
772 l = optargs[2];
773 else
774 l = 256 * optargs[2] + optargs[3];
775
776 if ( l > 100 ){
777 //TODO: remove
778 l = 100;
779 if (Query(P_TTY_SHOW))
780 tell_object( this_object(),
781 break_string("Tut mir leid, aber ich kann"
782 +" nur bis zu 100 Zeilen"
783 +" verwalten.\n", (c ? c-2 : 78),
784 "Der GameDriver teilt Dir mit: " )
785 );
786 }
787
788 if ( l < 3 ){
789 if (Query(P_TTY_SHOW))
790 tell_object( this_object(),
791 break_string("Du willst weniger als drei"
792 +" Zeilen benutzen? Glaub ich"
793 +" Dir nicht - ich benutze den"
794 +" Standardwert von 24"
795 +" Zeilen.\n", (c ? c-2 : 78),
796 "Der GameDriver teilt Dir mit: " )
797 );
798 l = 24;
799 }
800
801 if ( ((int) Query(P_TTY_ROWS) != l) ||
802 ((int) Query(P_TTY_COLS) != c) ){
803 Set( P_TTY_ROWS, l );
804 Set( P_TTY_COLS, c );
805
806 if (Query(P_TTY_SHOW))
807 tell_object( this_object(),
808 break_string("Du hast Deine Fenstergroesse auf"
809 +" "+l+" Zeilen und "+c+
810 " Spalten geaendert.\n", c-2,
811 "Der GameDriver teilt Dir mit: ")
812 );
813 }
814 }
815}
816
MG Mud User88f12472016-06-24 23:31:02 +0200817// Query-/Set-Methoden
818// Und wenn hier einer von aussen dran rumpfuscht, werde ich sauer.
819mapping
820query_telnet_neg()
821{
MG Mud User88f12472016-06-24 23:31:02 +0200822 return TN;
823}
824
825// siehe oben
826string *
827query_terminals() {
828 return Terminals;
829}
830
831public int _query_p_lib_telnet_rttime()
832{
833 struct telopt_s opt = TN[TELOPT_TM];
834 if (opt && pointerp(opt->data))
835 return (opt->data)[0];
836 return 0;
837}
838