blob: 8cbc7a9a103ffaedf57b53e7291f75b52b1eabdb [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
Zesstra57cdbc32020-01-20 23:17:10 +010022#include <configuration.h>
MG Mud User88f12472016-06-24 23:31:02 +020023
24// unterstuetzte Optionen:
Zesstra57cdbc32020-01-20 23:17:10 +010025// TELOPT_EOR, TELOPT_NAWS, TELOPT_LINEMODE, TELOPT_TTYPE, TELOPT_BINARY,
26// TELOPT_CHARSET
MG Mud User88f12472016-06-24 23:31:02 +020027
28//#define __DEBUG__ 1
29
30#ifdef __DEBUG__
31#define DEBUG(x) if (interactive(this_object()))\
32 tell_object(this_object(),"TN: " + x + "\n")
33#define DTN(x,y) _debug_print(x,y)
34#else
35# define DEBUG(x)
36# define DTN(x,y)
37#endif
38
Zesstra57cdbc32020-01-20 23:17:10 +010039// first element "" to yield the separator
40#define OFFERED_CHARSETS ({"", "UTF-8", "ISO8859-15", "LATIN-9", "ISO8859-1",\
41 "LATIN1", "WINDOWS-1252", "US-ASCII"})
MG Mud User88f12472016-06-24 23:31:02 +020042
43// Aus mini_props.c:
44public varargs mixed Query( string str, int type );
45public varargs mixed Set( string str, mixed value, int type );
46
47private nosave mapping TN = ([]);
48nosave string *Terminals;
49
50// Prototypen
51private void eval_naws(int *optargs);
52
53#ifdef __DEBUG__
54// Gibts einige Konstanten mit sym. Namen aus.
55private string dtranslate(int i) {
56 switch(i) {
57 case IAC: return "IAC";
58 case DONT: return "DONT";
59 case DO: return "DO";
60 case WONT: return "WONT";
61 case WILL: return "WILL";
62 case SB: return "SB";
63 case SE: return "SE";
64 case EOR: return "EOR";
65 case TELOPT_LINEMODE: return "TELOPT_LINEMODE";
66 case TELOPT_XDISPLOC: return "TELOPT_XDISPLOC";
67 case TELOPT_ENVIRON: return "TELOPT_ENVIRON";
68 case TELOPT_NEWENV: return "TELOPT_NEWENV";
69 case TELOPT_EOR: return "TELOPT_EOR";
70 case TELOPT_NAWS: return "TELOPT_NAWS";
71 case TELOPT_TSPEED: return "TELOPT_TSPEED";
72 case TELOPT_TTYPE: return "TELOPT_TTYPE";
73 case TELOPT_ECHO: return "TELOPT_ECHO";
74 case TELOPT_SGA: return "TELOPT_SGA";
75 case TELOPT_NAMS: return "TELOPT_NAMS";
76 case TELOPT_STATUS: return "TELOPT_STATUS";
77 case TELOPT_TM: return "TELOPT_TM";
Zesstra65e9f1a2020-01-17 22:50:14 +010078 case TELOPT_BINARY: return "TELOPT_BINARY";
Zesstra57cdbc32020-01-20 23:17:10 +010079 case TELOPT_CHARSET: return "TELOPT_CHARSET";
MG Mud User88f12472016-06-24 23:31:02 +020080 case TELOPT_COMPRESS2: return "TELOPT_COMPRESS2";
81 case TELOPT_MSP: return "TELOPT_MSP";
82 case TELOPT_MXP: return "TELOPT_MXP";
83 case TELOPT_ATCP: return "TELOPT_ATCP";
84 case TELOPT_GMCP: return "TELOPT_GMCP";
85 case TELOPT_MSSP: return "TELOPT_MSSP";
86 }
87 return to_string(i);
88}
89
90// Gibt <arr> halbwegs lesbar an this_object() aus.
91private void _debug_print(string x, int *arr) {
Zesstra65e9f1a2020-01-17 22:50:14 +010092 if (sizeof(arr) >1 && arr[1] == SB && arr[<1] != SE)
MG Mud User88f12472016-06-24 23:31:02 +020093 arr += ({IAC, SE});
94 closure map_int = function string (int i)
95 { if (i >= 32 && i <= 126) return sprintf("%c",i);
96 return "["+to_string(i)+"]";
97 };
98 if (sizeof(arr)<=5) {
99 foreach(int c : arr)
100 x += " " + dtranslate(c);
101 }
102 else {
103 x += dtranslate(arr[0]) + " " + dtranslate(arr[1]) + " "
104 + dtranslate(arr[2]) + " "
105 + implode(map(arr[3..<3], map_int)," ")
106 + " " + dtranslate(arr[<2]) + " " + dtranslate(arr[<1]);
107 }
108 DEBUG(x);
109}
110#endif
111
112protected varargs int send_telnet_neg(int *arr, int bm_flags)
113{
114 if ( sizeof(arr) < 2 )
115 return efun::binary_message(arr,bm_flags);
116
117 struct telopt_s opt = TN[arr[1]];
118
119 switch (arr[0]){
120 case DO:
121 case DONT:
122 (opt->lo_wishes)->remoteside = arr[0];
123 arr = ({IAC}) + arr;
124 break;
125 case WILL:
126 case WONT:
127 (opt->lo_wishes)->localside = arr[0];
128 arr = ({IAC}) + arr;
129 break;
130 case SB:
131 (opt->lo_wishes)->sbdata = arr[0..];
132 arr = ({IAC}) + arr + ({IAC, SE});
133 break;
134 default:
135 break;
136 }
137 DTN("send_tn: ",arr);
138 return efun::binary_message(arr, bm_flags);
139}
140
Zesstra4c418f92020-02-03 20:14:35 +0100141protected varargs int send_telnet_neg_str(bytes str, int bm_flags)
142{
MG Mud User88f12472016-06-24 23:31:02 +0200143#ifdef __DEBUG__
144 // Debugausgaben zur Zeit nur fuer arraybasierte Variante
145 return send_telnet_neg(to_array(str), bm_flags);
146#else
147 if ( sizeof(str) < 2 )
148 return efun::binary_message(str, bm_flags);
149
150 struct telopt_s opt = TN[str[1]];
151
152 switch (str[0]) {
153 case DO:
154 case DONT:
155 (opt->lo_wishes)->remoteside = 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 WILL:
159 case WONT:
160 (opt->lo_wishes)->localside = str[0];
Zesstra9ebed822019-11-27 19:50:17 +0100161 str = to_bytes(({IAC})) + str;
MG Mud User88f12472016-06-24 23:31:02 +0200162 break;
163 case SB:
Zesstra9ebed822019-11-27 19:50:17 +0100164 (opt->lo_wishes)->sbdata = to_array(str[1..]);
165 str = to_bytes(({IAC})) + str + to_bytes(({IAC,SE}));
MG Mud User88f12472016-06-24 23:31:02 +0200166 break;
167 default:
168 break;
169 }
170
171 return efun::binary_message(str, bm_flags);
172#endif // __DEBUG__
173}
174
Zesstra4c418f92020-02-03 20:14:35 +0100175// Wenn der Client via STARTTLS eine TLS negotiation angestossen hat und
176// die noch laeuft, darf keine Ausgabe erfolgen. In diesem Fall wird das
177// Loginverfahren ausgesetzt, bis die TLS-Verhandlung abgeschlossen ist.
178// Danach wird es fortgesetzt bzw. neugestartet. Dies gilt auch fuer Fall,
179// dass STARTTLS verhandelt wurde, aber die TLS-Verhandlung noch nicht
180// laeuft. (Bemerkung: beides pruefen ist nicht ueberfluessig. Den Zustand
181// der Telnet-Option muss man pruefen, weil der Client evtl. seine
182// Verhandlung noch nicht signalisiert hat (FOLLOWS vom Client) und die
183// efun muss man pruefen, weil nach Empfang von FOLLOWS vom Client der
184// Status der Telnet-Optiosn resettet wurde - standardkonform.)
185protected int check_tls_negotiation()
186{
187 struct telopt_s s_tls = TN[TELOPT_STARTTLS];
188 if (tls_query_connection_state(this_object()) < 0
189 || (structp(s_tls) && s_tls->state->remoteside) )
190 {
191 debug_message("In TLS negotiation.\n");
192 return 1;
193 }
194 return 0;
195}
196
MG Mud User88f12472016-06-24 23:31:02 +0200197// Startet eine Verhandlung, um den Status einer Option zu aendern.
198// Wenn bereits eine Verhandlung laeuft, wird nichts gemacht und -1
199// zurueckgeben.
200// Wenn die Verhandlung keine Aenderung vom Status quo zum Ziel hat, wird
201// nichts gemacht und -2 zurueckgegeben.
202// Ansonsten ist die Rueckgabe die Anzahl der uebermittelten Zeichen.
203// <action>: WILL: Option soll auf dieser Seite eingeschaltet werden.
204// WONT: Option soll auf dieser Seite ausgeschaltet werden.
205// DO : Option soll auf der anderen Seite eingeschaltet werden.
206// DONT: Option soll auf der anderen Seite ausgeschaltet werden.
207protected int do_telnet_neg(int option, int action) {
Zesstra4c418f92020-02-03 20:14:35 +0100208
209 // ggf. muss TLS (initiiert durch STARTTLS) noch ausverhandelt werden. In
210 // dem Fall nix verhandeln/senden, was nicht STARTTLS ist.
211 if (option != TELOPT_STARTTLS && check_tls_negotiation())
212 return 0;
213
MG Mud User88f12472016-06-24 23:31:02 +0200214 struct telopt_s opt = TN[option];
215 if (!structp(opt))
216 {
217 opt = (<telopt_s> option: option,
218 re_wishes: (<to_state_s>),
219 lo_wishes: (<to_state_s>),
220 state: (<to_state_s>)
221 );
222 TN[option] = opt;
223 }
224 // es wird nur geprueft, ob wir bereits eine Verhandlung begonnen haben
225 // (lo_wishes), weil reinkommende remote Wuensche letztendlich sofort durch
226 // unsere Antwort erledigt sind.
227 switch(action)
228 {
229 case WILL:
230 if (opt->lo_wishes->localside != 0)
231 return -1;
232 if (opt->state->localside)
233 return -2;
234 return send_telnet_neg( ({ WILL, option }) );
MG Mud User88f12472016-06-24 23:31:02 +0200235 case WONT:
236 if (opt->lo_wishes->localside != 0)
237 return -1;
238 if (!opt->state->localside)
239 return -2;
240 return send_telnet_neg( ({ WONT, option }) );
MG Mud User88f12472016-06-24 23:31:02 +0200241 case DO:
242 if (opt->lo_wishes->remoteside != 0)
243 return -1;
244 if (opt->state->remoteside)
245 return -2;
246 return send_telnet_neg( ({ DO, option }) );
MG Mud User88f12472016-06-24 23:31:02 +0200247 case DONT:
248 if (opt->lo_wishes->remoteside != 0)
249 return -1;
250 if (!opt->state->remoteside)
251 return -2;
252 return send_telnet_neg( ({ DONT, option }) );
MG Mud User88f12472016-06-24 23:31:02 +0200253 }
254 raise_error(sprintf("Unsupported telnet negotation action in "
255 "do_telnet_neg(): %d\n",action));
256}
257
258// LOCAL Standard Handlers //
259private void _std_lo_handler_eor(struct telopt_s opt, int action) {
260 // tatsaechlich nix zu tun. Handler ist nur da, damit die Option auf dieser
261 // Seite aktiviert wird. Die Arbeit erledigt print_prompt.
262 return;
263}
264
265private void _std_lo_handler_mssp(struct telopt_s opt, int action) {
266 // nur einschalten ist interessant.
267 if (action != LOCALON)
268 return;
269 // Krams senden, wenn Objekt geladen. Sonst wieder abschalten (kommt
270 // hoffentlich nicht vor)...
271 object mssp = find_object("/secure/misc/mssp");
272 if (!mssp)
273 send_telnet_neg( ({WONT, TELOPT_MSSP }) );
274 else
275 {
Zesstra9ebed822019-11-27 19:50:17 +0100276 send_telnet_neg_str(
277 to_bytes(({SB, TELOPT_MSSP}))
bugfixd94d0932020-04-08 11:27:13 +0200278 + to_bytes(sprintf("%s", ({string})mssp->get_telnegs_str()),
Zesstra9ebed822019-11-27 19:50:17 +0100279 "ASCII//TRANSLIT"));
MG Mud User88f12472016-06-24 23:31:02 +0200280 // die Daten brauchen wir nicht mehr
281 opt->lo_wishes->sbdata = 0;
282 }
283}
284
285
286// REMOTE Standard Handlers //
287private void _std_re_handler_tm(struct telopt_s opt, int action,
288 int *data)
289{
290 // egal, was geantwortet wurde, es gibt nen Hinweis auf die round-trip-time.
291 // Wenn ein Array in opt->data[1] steht, rechnen wir das aus und schreiben es
292 // in opt->data[0] als Ergebnis rein.
293 if (pointerp(opt->data) && pointerp(opt->data[1]))
294 {
295 int *ut = utime();
296 int *start = opt->data[1];
297 int res = (ut[0] - start[0]) * 1000000;
298 res += ut[1] - start[1];
299 opt->data[0] = res;
300 opt->data[1] = 0;
301 DEBUG("RTT: "+res);
302 }
303 return;
304}
305
306private void _std_re_handler_naws(struct telopt_s opt, int action,
307 int *data)
308{
309 if (action == SB)
310 {
311 eval_naws(data);
312 }
313}
314
315private void _std_re_handler_linemode(struct telopt_s opt, int action,
316 int *data)
317{
318 if (action == REMOTEON)
319 {
320 // see /doc/concepts/negotiations. We use only the minimum
321 // needed for linemode: switching on local commandline-editing
322 // for the client.
323 send_telnet_neg(({ SB, TELOPT_LINEMODE, LM_MODE, MODE_EDIT }));
324 // flush on 0d and 0a...
325 // TODO: what does this exactly do?
326 send_telnet_neg(({ SB, TELOPT_LINEMODE, DO, LM_FORWARDMASK, 0,
327 0x40|0x08 }));
328 //Gna...
329 opt->lo_wishes->sbdata = ({MODE_EDIT});
330 }
331}
332
333private void _std_re_handler_ttype(struct telopt_s opt, int action,
334 int *data)
335{
336 if (action == SB)
337 {
338 //TODO: get rid of this hysterical stuff...
339 //NOTE: We do not do multiple SB SENDs due to some weird
340 //bugs in IBM3270 emulating telnets which crash if we
341 //do that.
342 if ( sizeof(data) < 1 )
343 return;
344
345 if ( data[0] != TELQUAL_IS )
346 return;
347
348 string tmpterminal = lower_case( to_string(data[1..]) );
349 if ( !Terminals )
350 Terminals = ({ tmpterminal });
351 else
352 Terminals += ({ tmpterminal });
353
354 if ( Query(P_TTY_TYPE) )
355 Set( P_TTY_TYPE, Terminals[0] );
356 }
357 else if (action == REMOTEON)
358 {
359 send_telnet_neg(({ SB, TELOPT_TTYPE, TELQUAL_SEND }));
360 }
361}
362
Zesstra65e9f1a2020-01-17 22:50:14 +0100363// Der Handler fuer die BINARY option, wenn sie auf Clientseite
364// aktiviert/deaktivert wird, d.h. der Client sendet jetzt Binaerdaten statt
365// NVT-ASCII. Im Normalfall muessen wir im Handler nix machen. (SB gibts hier
366// nicht.)
367private void _std_re_handler_binary(struct telopt_s opt, int action,
368 int *data)
369{
Zesstra57cdbc32020-01-20 23:17:10 +0100370 DTN("binary handler client",({action}));
Zesstra65e9f1a2020-01-17 22:50:14 +0100371}
372
373// Der Handler fuer die BINARY option, wenn sie auf unserer Seite
374// aktiviert/deaktivert wird, d.h. wir senden jetzt Binaerdaten statt
375// NVT-ASCII. Im Normalfall muessen wir im Handler nix machen. (SB gibts hier
376// nicht.)
377private void _std_lo_handler_binary(struct telopt_s opt, int action,
378 int *data)
379{
Zesstra57cdbc32020-01-20 23:17:10 +0100380 DTN("binary handler mg",({action}));
Zesstra65e9f1a2020-01-17 22:50:14 +0100381}
382
Zesstra57cdbc32020-01-20 23:17:10 +0100383private int activate_charset(struct telopt_s opt, string charset)
384{
385 // Wenn der Client die Option nicht BINARY nicht unterstuetzt/will, duerfen
386 // wir auch keine nicht-ASCII-Zeichen uebertragen. In diesem Fall ist der
387 // einzige akzeptable Zeichensatz (US-)ASCII.
388 struct telopt_s binary = TN[TELOPT_BINARY];
389 if ( (!binary->state->remoteside || !binary->state->localside)
390 && (upper_case(charset) != "US-ASCII"
391 && upper_case(charset) != "ASCII") )
392 {
393 return 0;
394 }
395 // Wenn der Zeichensatz keine //-Variante ist, machen wir den zu
396 // einer. Das verhindert letztlich eine Menge Laufzeitfehler, wenn ein
397 // Zeichen mal nicht darstellbar ist.
398 if (strstr(charset, "//") == -1)
399 charset += "//TRANSLIT";
400 // Falls das zu sehr scrollt, weil Clients staendig ungueltige/nicht
401 // verwendbare Zeichensaetz schicken, muss das publish weg und ggf. sogar
402 // ein nolog hin...
403 if (!catch(configure_interactive(this_object(), IC_ENCODING, charset);
404 publish))
405 {
406 m_delete(opt->data, "failed_negotiations");
407 opt->data["accepted_charset"] = interactive_info(this_player(),
408 IC_ENCODING);
409 return 1;
410 }
411 return 0;
412}
413#define REQUEST 1
414#define ACCEPTED 2
415#define REJECTED 3
416#define TTABLE_IS 4
417#define TTABLE_REJECTED 5
418// Der Handler fuer die CHARSET option, wenn sie auf/fuer Clientseite
419// aktiviert/deaktivert wird oder fuer empfangene SB.
420private void _std_re_handler_charset(struct telopt_s opt, int action,
421 int *data)
422{
423 DTN("charset handler client",({action}));
424
425 // Wenn action == REMOTEON: Ab diesem Moment darf uns der Client einen
426 // CHARSET REQUEST schicken (weil wir haben ihm auch schon ein DO
427 // geschickt).
428 if (action == REMOTEON)
429 {
430 if (!mappingp(opt->data))
431 opt->data = ([]);
432 }
433 else if (action == REMOTEOFF)
434 {
435 // Wenn auch auf mg-seite aus, kann data geloescht werden.
436 if (!opt->state->localside)
437 opt->data = 0;
438 }
439 else if (action == SB)
440 {
441 mapping statedata = opt->data;
442 // <data> is the part following IAC SB TELOPT_CHARSET
443 switch(data[0])
444 {
445 case REQUEST:
446 // is the client allowed to REQUEST?
447 if (opt->state->remoteside)
448 return;
449 // And enough data?
450 if (sizeof(data) > 1 )
451 {
452 DTN("re_charset request:",data);
453 string *suggestions = explode(to_text(data[2..], "ASCII"),
454 sprintf("%c",data[1]));
455 // Wenn UTF-8 drin vorkommt, nehmen wir das. (Gross-/Kleinschreibung
456 // ist egal, aber wir muessen einen identischen String
457 // zurueckschicken). (Gemischte Schreibweise: *ignorier* *stoehn*)
458 string *selected = suggestions & ({"UTF-8","utf-8"});
459 if (sizeof(selected)
460 && activate_charset(opt, selected[0]))
461 {
462 send_telnet_neg(({ SB, TELOPT_CHARSET, ACCEPTED,
463 to_array(selected[0]) }));
464 return;
465 }
466 else
467 {
468 // die ersten 10 Vorschlaege durchprobieren
469 foreach(string cs : suggestions[0..min(sizeof(suggestions)-1, 10)])
470 {
471 if (activate_charset(opt, cs))
472 {
473 send_telnet_neg(({ SB, TELOPT_CHARSET, ACCEPTED,
474 to_array(cs) }));
475 return; // yeay, found one!
476 }
477 }
478 // none acceptable
479 send_telnet_neg(({ SB, TELOPT_CHARSET, REJECTED }));
480 ++opt->data["failed_negotiations"];
481 // fall-through, no return;
482 }
483 }
484 else // malformed message
485 {
486 send_telnet_neg(({ SB, TELOPT_CHARSET, REJECTED }));
487 ++opt->data["failed_negotiations"];
488 // fall-through, no return;
489 }
490 // when arriving here, the negotiation was not successful. Check if
491 // too many unsuccesful tries in a row.
492 if (opt->data["failed_negotiations"] > 10)
493 {
494 send_telnet_neg(({ TELOPT_CHARSET, DONT }));
495 send_telnet_neg(({ TELOPT_CHARSET, WONT }));
496 }
497 break;
498 case ACCEPTED:
499 // great - the client accepted one of our suggested charsets.
500 // Negotiation concluded. However, check if we REQUESTed a charset in
501 // the first place... And if the accepted one is one of our
502 // suggestions
503 if (sizeof(data) > 1)
504 {
505 DTN("re_charset accepted:",data);
506 string charset = upper_case(to_text(data[1..], "ASCII"));
507 string *offered = statedata["offered"];
508 // in any case, we don't need the key in the future.
509 m_delete(statedata, "offered");
510 if (pointerp(offered) && member(offered, charset) > -1)
511 {
512 activate_charset(opt, charset);
513 return;
514 }
515 // else: client did not sent us back one of our suggestions or we
516 // did not REQUEST. :-(
517 }
518 ++opt->data["failed_negotiations"];
519 // else? Huh. malformed message.
520 break;
521 case REJECTED:
522 // none of our suggested charsets were acceptable. Negotiation is
523 // concluded, we keep the current charset (and maybe we will receive a
524 // suggestion of the client)
525 if (member(statedata, "offered"))
526 m_delete(statedata, "offered");
527 ++opt->data["failed_negotiations"];
528 DTN("re_charset_rejected:",data);
529 break;
530 case TTABLE_IS:
531 // we plainly don't support TTABLES
532 send_telnet_neg(({ SB, TELOPT_CHARSET, TTABLE_REJECTED }));
533 ++opt->data["failed_negotiations"];
534 break;
535 }
536 }
537}
538
Zesstrae037c1d2020-02-02 22:02:14 +0100539// Der Handler fuer die CHARSET option, wenn sie auf/fuer unserere Seite
Zesstra57cdbc32020-01-20 23:17:10 +0100540// aktiviert/deaktivert wird.
541private void _std_lo_handler_charset(struct telopt_s opt, int action,
542 int *data)
543{
544 DTN("charset handler mg",({action}));
545 if (action == LOCALON)
546 {
547 // Ab diesem Moment duerfen wir dem Client einen CHARSET REQUEST schicken
548 // (denn wir haben auch schon ein DO erhalten). Und das tun wir auch
549 // direkt.
550 if (!mappingp(opt->data))
551 opt->data = ([ "offered": OFFERED_CHARSETS ]);
552 else
553 opt->data["offered"] = OFFERED_CHARSETS;
554 send_telnet_neg(({ SB, TELOPT_CHARSET, REQUEST })
555 + to_array(implode(opt->data["offered"], ";"))) ;
556 }
557 else if (action == LOCALOFF)
558 {
559 // ok, keine REQUESTS mehr nach dem LOCALOFF, aber viel muss nicht getan
560 // werden. Wenn auch auf client-seite aus, kann data geloescht werden.
561 if (!opt->state->remoteside)
562 opt->data = 0;
563 }
564 // und SB gibt es nicht in diesem Handler.
565}
566#undef REQUEST
567#undef ACCEPTED
568#undef REJECTED
569#undef TTABLE-IS
570#undef TTABLE-REJECTED
571
Zesstrae037c1d2020-02-02 22:02:14 +0100572// Called from the telnetneg handler for TELOPT_STARTTLS to initiate the TLS
573// connection negotiation.
574protected void init_tls()
575{
576 // Dabei muss unser ganzer Telnet-Option-State muss zurueckgesetzt werden.
577 // Ja, wirklich! (Keine Sorge, der client muss das auch tun.)
578 TN = ([]);
579}
580
581#ifdef __TLS__
582// Der Handler fuer STARTTLS, wenn es auf der Clientseite
583// deaktiviert/aktiviert wird. Es wird nur auf der Clientseite aktiviert, der
584// Server darf kein WILL senden. Nach Aktivierung muessen wir ein FOLLOWS
585// senden.
586#define FOLLOWS 1
587private void _std_re_handler_starttls(struct telopt_s opt, int action,
588 int *data)
589{
590 DTN("starttls handler client",({action}));
591
592 // Wenn action == REMOTEON: Ab diesem Moment darf uns der Client einen
593 // STARTTLS FOLLOWS senden (weil wir haben ihm auch schon ein DO
594 // geschickt). Wir sollen ihm aber jetzt auch ein FOLLOWS senden. Sobald wir
595 // das gesendet haben und ein FOLLOWS erhalten haben, geht die Negotiation
596 // los.
597 if (action == REMOTEON)
598 {
599 send_telnet_neg(({ SB, TELOPT_STARTTLS, FOLLOWS }));
600 opt->data = 1; // Nur ein Flag, dass wir es gesendet haben.
601 }
602 else if (action == REMOTEOFF)
603 {
604 // data zuruecksetzen, sonst muessen wir nix machen.
605 opt->data = 0;
606 }
607 else if (action == SB)
608 {
609 if (data[0] == FOLLOWS)
610 {
611 // FOLLOWS empfangen. Wenn wir noch kein FOLLOWS gesendet haben, tun wir
612 // das jetzt.
613 if (!opt->data)
614 send_telnet_neg(({ SB, TELOPT_STARTTLS, FOLLOWS }));
615 // Jetzt wird die Verhandlung auf unserer Seite gestartet, der Client
616 // macht das entweder schon oder spaetestens, wenn er unser FOLLOWS
617 // empfangen kann.
618 init_tls();
619 }
620 }
621}
622#undef FOLLOWS
623#endif // __TLS__
Zesstra57cdbc32020-01-20 23:17:10 +0100624
MG Mud User88f12472016-06-24 23:31:02 +0200625// Bindet/registriert Handler fuer die jew. Telnet Option. (Oder loescht sie
626// auch wieder.) Je nach <initneg> wird versucht, die Option neu zu
627// verhandeln.
628protected int bind_telneg_handler(int option, closure re, closure lo,
629 int initneg)
630{
631 struct telopt_s opt = TN[option];
632 if (!structp(opt))
633 {
634 opt = (<telopt_s> option: option,
635 re_wishes: (<to_state_s>),
636 lo_wishes: (<to_state_s>),
637 state: (<to_state_s>)
638 );
639 TN[option] = opt;
640 }
641
642 opt->remotehandler = re;
643 if (initneg)
644 {
645 if (re)
646 do_telnet_neg(option, DO);
647 else
648 do_telnet_neg(option, DONT );
649 }
650
651 opt->localhandler = lo;
652 if (initneg)
653 {
654 if (lo)
655 do_telnet_neg(option, WILL);
656 else
657 do_telnet_neg(option, WONT);
658 }
659 return 1;
660}
661
662
663// Mal unsere Wuensche an den Client schicken und die Standardhandler
Zesstra01b2b742020-01-20 23:53:01 +0100664// registrieren. Hierbei bei Bedarf neue Verhandlungen starten. Es wird hier
665// aber nur ein Basissatz an Optionen verhandelt, der Rest kommt spaeter
666// nachdem das Spielerobjekt die Verbindung hat (in startup_telnet_negs())
MG Mud User88f12472016-06-24 23:31:02 +0200667// Gerufen aus login.c nach Verbindungsaufbau.
668// Bemerkung: das Spielerobjekt bietet evt. noch zusaetzliche Telnetoptionen
669// an, die dann ueber startup_telnet_negs() (im Spielerobjekt)
670// laufen.
671protected void SendTelopts()
672{
Zesstrae037c1d2020-02-02 22:02:14 +0100673#if __TLS__
674 // If this is a non-TLS-connection, we offer STARTTLS, but wait for the
675 // client to ask for it.
676 if (tls_available() && tls_query_connection_state() == 0)
677 {
678 bind_telneg_handler(TELOPT_STARTTLS, #'_std_re_handler_starttls,
Zesstra56493232020-02-03 20:15:51 +0100679 0, 1);
Zesstrae037c1d2020-02-02 22:02:14 +0100680 }
681#endif
Zesstra65e9f1a2020-01-17 22:50:14 +0100682 bind_telneg_handler(TELOPT_BINARY, #'_std_re_handler_binary,
683 #'_std_lo_handler_binary, 1);
MG Mud User88f12472016-06-24 23:31:02 +0200684 bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 1);
MG Mud User88f12472016-06-24 23:31:02 +0200685 bind_telneg_handler(TELOPT_NAWS, #'_std_re_handler_naws, 0, 1);
686 bind_telneg_handler(TELOPT_LINEMODE, #'_std_re_handler_linemode, 0, 1);
687 bind_telneg_handler(TELOPT_TTYPE, #'_std_re_handler_ttype, 0, 1);
Zesstra65e9f1a2020-01-17 22:50:14 +0100688 if (find_object("/secure/misc/mssp"))
689 bind_telneg_handler(TELOPT_MSSP, 0, #'_std_lo_handler_mssp, 1);
Zesstra57cdbc32020-01-20 23:17:10 +0100690 // und auch CHARSET wird verzoegert bis das Spielerobjekt da ist.
MG Mud User88f12472016-06-24 23:31:02 +0200691}
692
MG Mud User88f12472016-06-24 23:31:02 +0200693// Bindet die Standardhandler _aus diesem_ Programm (und ueberschreibt dabei
694// ggf. andere). Hierbei werden nur die Handler neu gebunden, keine neuen
695// Verhandlungen initiiert.
696// gerufen aus base.c indirekt via startup_telnet_negs().
Zesstrae037c1d2020-02-02 22:02:14 +0100697protected void _bind_telneg_std_handlers()
698{
699 // BTW: es ist absicht, im Spielerobjekt keinen Support fuer STARTTLS mehr
700 // anzubieten.
Zesstra65e9f1a2020-01-17 22:50:14 +0100701 bind_telneg_handler(TELOPT_BINARY, #'_std_re_handler_binary,
702 #'_std_lo_handler_binary, 0);
MG Mud User88f12472016-06-24 23:31:02 +0200703 bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 0);
MG Mud User88f12472016-06-24 23:31:02 +0200704 bind_telneg_handler(TELOPT_NAWS, #'_std_re_handler_naws, 0, 0);
705 bind_telneg_handler(TELOPT_LINEMODE, #'_std_re_handler_linemode, 0, 0);
706 bind_telneg_handler(TELOPT_TTYPE, #'_std_re_handler_ttype, 0, 0);
Zesstra65e9f1a2020-01-17 22:50:14 +0100707 // Besondere Situation: MSSP ist nach Spielerlogin eigentlich uninteressant.
708 // Daher sparen wir uns das im Kontext des Spielerobjekts und schalten es
709 // einfach wieder aus.
710 bind_telneg_handler(TELOPT_MSSP, 0, 0, 0);
MG Mud User88f12472016-06-24 23:31:02 +0200711}
712
713
714// Ruft die entsprechenden handler von der Telnet Option.
715// Wenn es keinen handler (mehr) gibt, wird die Option auch auf der jeweiligen
716// Seite ausgeschaltet. Deshalb MUSS lo_wishes und re_wishes vom Aufrufer VOR
717// DEM AUFRUF zurueckgesetzt worden sein!
718// <action>: 'LOCALON': Option wurde auf unserer Seite eingeschaltet
719// 'LOCALOFF': Option wurde auf unserer Seite ausgeschaltet
720// 'REMOTEON': Option wurde auf Clientseite eingeschaltet
721// 'REMOTEOFF': Option wurde auf Clientseite ausgeschaltet
722// 'SB': Suboption negotiation Daten wurden empfangen
723// <data>: die per SB empfangenen Daten (unverarbeitet)
724private void _call_handler(struct telopt_s opt, int action, int *data) {
725 switch(action)
726 {
727 case REMOTEON:
728 case REMOTEOFF:
729 case SB:
730 if (opt->remotehandler)
731 {
732 funcall(opt->remotehandler, opt, action, data);
733 }
734 else
735 {
736 // ok, geht nicht. Ggf. Abschalten (do_telnet_neg passt auf,
737 // dass nur verhandelt wird, wenn die Option an ist.)
738 do_telnet_neg( opt->option, DONT );
739 }
740 break;
741 case LOCALON:
742 case LOCALOFF:
743 if (opt->localhandler)
744 {
745 funcall(opt->localhandler, opt, action);
746 }
747 else
748 {
749 // ok, geht nicht. Ggf. Abschalten (do_telnet_neg passt auf,
750 // dass nur verhandelt wird, wenn die Option an ist.)
751 do_telnet_neg( opt->option, WONT );
752 }
753 break;
754 }
755}
756
757// Gerufen vom Driver, wenn neue telnet options reinkommen.
758void
759telnet_neg(int command, int option, int *optargs)
760{
761 DTN("recv_tn: ", ({IAC, command, option}) + (optargs||({})));
762
Zesstra4c418f92020-02-03 20:14:35 +0100763 // ggf. muss TLS (initiiert durch STARTTLS) noch ausverhandelt werden. In
764 // dem Fall muessen wir alles ignorieren, was nicht STARTTLS ist.
765 if (option != TELOPT_STARTTLS && check_tls_negotiation())
766 return;
767
MG Mud User88f12472016-06-24 23:31:02 +0200768 struct telopt_s opt = TN[option];
769 if (!structp(opt))
770 {
771 opt = (<telopt_s> option: option,
772 re_wishes: (<to_state_s>),
773 lo_wishes: (<to_state_s>),
774 state: (<to_state_s>)
775 );
776 TN[option] = opt;
777 }
778
779 // Was will der Client tun?
780 if (command == WONT)
781 {
782 // Client will die Option auf seiner Seite abschalten. Wir MUESSEN das
783 // akzeptieren.
784 // Wir muessen das allerdings ignorieren, wenn die Option bereits aus
785 // ist.
786 if (opt->state->remoteside==0)
787 {
788 // Ausnahme fuer TELOPT_TM, da das kaum ein Client kann und fuer RTT
789 // es eigentlich auch egal ist, was zurueck kommt: der handler wird
790 // zumindest doch gerufen zum Ausrechnen der RTT
791 if (option == TELOPT_TM)
792 _call_handler(opt, REMOTEOFF, 0);
793 // ansonsten aber wirklich ignorieren. ;)
794 return;
795 }
796 opt->re_wishes->remoteside = command;
797 // Bestaetigung auf ein WONT senden, wenn wir nicht selber schon ein
798 // DONT geschickt hatten.
799 if (opt->lo_wishes->remoteside != DONT) {
800 send_telnet_neg( ({DONT, option}) );
801 }
802 // Wir haben jetzt auf jeden Fall ein DONT gesendet und ein WONT
803 // erhalten. Damit ist die Option jetzt auf der clientseite aus.
804 // Ausserdem setzen wir die Wishes zurueck.
805 opt->re_wishes->remoteside = 0;
806 opt->lo_wishes->remoteside = 0;
807 if (opt->state->remoteside != 0)
808 {
809 opt->state->remoteside = 0;
810 _call_handler(opt, REMOTEOFF, 0);
811 }
812 } // WONT vom Client verarbeitet
813 else if ( command == WILL)
814 {
815 // Wenn die Option bereits an ist, muessen wir dies ignorieren.
816 if (opt->state->remoteside == 1)
817 {
818 // Ausnahme fuer TELOPT_TM, der handler wird zumindest doch gerufen
819 // zum Ausrechnen der RTT. Diese Option laesst sich ohnehin
820 // aktivieren, auch wenn sie schon an ist.
821 if (option == TELOPT_TM)
822 _call_handler(opt, REMOTEON, 0);
823 // sonst aber wirklich ignorieren. ;-)
824 return;
825 }
826 opt->re_wishes->remoteside = command;
827 if ( opt->lo_wishes->remoteside == 0 )
828 {
829 // Der Client will, wir haben noch nix dazu gesagt. (Mit unserer
830 // Antwort ist die Verhandlung uebrigens beendet.)
831 // Wenn es einen remotehandler fuer die Option gibt, schalten wir
832 // sie ein...
833 if (opt->remotehandler)
834 {
835 send_telnet_neg(({DO, option}));
836 // Option jetzt an der Clientseite an.
837 opt->re_wishes->remoteside = 0;
838 opt->lo_wishes->remoteside = 0;
839 if (opt->state->remoteside != 1)
840 {
841 opt->state->remoteside = 1;
842 _call_handler(opt, REMOTEON, 0);
843 }
844 }
845 else
846 {
847 // sonst verweigern wir das einschalten (die meisten Optionen
848 // auf Clientseite sind fuer uns eh egal).
849 send_telnet_neg(({DONT, option}));
850 // Option jetzt an der Clientseite aus.
851 opt->re_wishes->remoteside = 0;
852 opt->lo_wishes->remoteside = 0;
853 if (opt->state->remoteside != 0)
854 {
855 opt->state->remoteside = 0;
856 _call_handler(opt, REMOTEOFF, 0);
857 }
858 }
859 }
860 else if ( opt->lo_wishes->remoteside == DO)
861 {
862 // Wir haben haben bereits per DO angefordert, d.h. das ist die
863 // Clientbestaetigung - wir duerfen nicht bestaetigen und die
864 // Option ist jetzt clientseitig aktiv. Verhandlung beendet.
865 opt->re_wishes->remoteside = 0;
866 opt->lo_wishes->remoteside = 0;
867 if (opt->state->remoteside != 1)
868 {
869 opt->state->remoteside = 1;
870 _call_handler(opt, REMOTEON, 0);
871 }
872 } // if (DO)
873 else {
874 // Mhmm. Wir hatten ein DONT gesendet, aber der Client hat mit WILL
875 // geantwortet. Das darf er eigentlich gar nicht.
876 //TODO: was sollte man jetzt eigentlich tun? Erstmal wiederholen wir
877 //das DONT...
878 send_telnet_neg( ({DONT, option}) );
879 }
880
881 return;
882 } // WILL vom Client verarbeitet
883 // Was sollen wir (nicht) fuer den Client tun?
884 else if ( command == DONT)
885 {
886 // Client will, dass wir etwas nicht tun. Wir MUESSEN das akzeptieren.
887 // wenn die Option auf unserer Seite aber schon aus ist, muessen wir
888 // dies ignorieren.
889 if (opt->state->localside == 0)
890 return;
891
892 opt->re_wishes->localside = command;
893 // Wenn wir noch kein WONT gesendet haben, senden wir das jetzt als
894 // Bestaetigung.
895 if (opt->lo_wishes->localside = WONT)
896 send_telnet_neg( ({WONT, option}) );
897 // Verhandlung beendet, Option is auf unserer Seite jetzt aus.
898 // Wuensche auch wieder zuruecksetzen.
899 opt->re_wishes->localside = 0;
900 opt->lo_wishes->localside = 0;
901 if (opt->state->localside != 0)
902 {
903 opt->state->localside = 0;
904 _call_handler(opt, LOCALOFF, 0);
905 }
906 }
907 else if ( command == DO )
908 {
909 // Client will, dass wir option tun. Mal schauen, wie wir dazu stehen.
910 // wenn die Option auf unserer Seite aber schon an ist, muessen wir
911 // dies ignorieren.
912 if (opt->state->localside == 1)
913 return;
914
915 opt->re_wishes->localside = command;
916
917 if ( opt->lo_wishes->localside == 0 ) {
918 // wir haben unsere Wuensche noch nicht geaeussert. Sobald wir
919 // bestaetigen, ist die Option auf unserer Seite an/aus und die
920 // Verhandlungen beendet.
921 // in jedem Fall die Wuensche zuruecksetzen
922 opt->re_wishes->localside = 0;
923 opt->lo_wishes->localside = 0;
924 if (opt->localhandler)
925 {
926 send_telnet_neg(({WILL, option}));
927 opt->state->localside = 1;
928 _call_handler(opt, LOCALON, 0);
929 }
930 else
931 {
932 send_telnet_neg(({WONT, option}));
933 opt->state->localside = 0;
934 _call_handler(opt, LOCALOFF, 0);
935 }
936 }
937 else if (opt->lo_wishes->localside == WILL ) {
938 // wir haben schon WILL gesendet, welches der Client jetzt
939 // bestaetigt hat (d.h. die Option ist jetzt auf dieser Seite an),
940 // wir bestaetigen das aber nicht (nochmal).
941 opt->re_wishes->localside = 0;
942 opt->lo_wishes->localside = 0;
943 if (opt->state->localside != 1)
944 {
945 opt->state->localside = 1;
946 _call_handler(opt, LOCALON, 0);
947 }
948 }
949 else {
950 // Mhmm. Wir haben ein WONT gesendet, der Client hat mit DO
951 // geantwortet. Das darf er eigentlich nicht.
952 // TODO: Was tun?
953 send_telnet_neg ( ({WONT, option}) );
954 }
955 // fertig mit DO
956 return;
957 }
958 // bleibt noch SB ueber
959 else if ( command == SB )
960 {
961 opt->re_wishes->sbdata = optargs;
962 _call_handler(opt, SB, optargs);
963 return;
964 } // if ( command == SB )
965}
966
967// wird nur in base.c gerufen, wenn die Verbindung an das Spielerobjekt
968// uebergeben wurde.
969// es uebertraegt unter anderem den Telnet Option Zustand aus login.c (das ist
970// dann previous_object()) in das Spielerobjekt (welches dann this_object())
971// ist!
972protected void
973startup_telnet_negs()
974{
975 int* optargs;
976
977 Set( P_TTY_TYPE, 0 ); //avoid ANY mistakes... Wird unten neu gesetzt.
978 // Daten aus dem Loginobjekt uebertragen. Das ist wichtig! (Dabei wird dann
979 // auch der Status von der letzten Session ueberschrieben.)
Vanion50652322020-03-10 21:13:25 +0100980 TN = ({mapping}) previous_object()->query_telnet_neg();
MG Mud User88f12472016-06-24 23:31:02 +0200981 // bevor irgendwas anderes gemacht wird, werden erstmal die Standardhandler
982 // gesetzt. Die sind naemlich in diesem Objekt jetzt erstmal kaputt, weil
983 // sie im Loginobjekt gerufen werden.
984 _bind_telneg_std_handlers();
985 // dann restliche Daten aus dem Loginobjekt holen.
Vanion50652322020-03-10 21:13:25 +0100986 Terminals = ({string *}) previous_object()->query_terminals();
bugfixd94d0932020-04-08 11:27:13 +0200987 Set( P_TTY_COLS, ({int})previous_object()->Query(P_TTY_COLS) );
988 Set( P_TTY_ROWS, ({int})previous_object()->Query(P_TTY_ROWS) );
MG Mud User88f12472016-06-24 23:31:02 +0200989
990 struct telopt_s opt = TN[TELOPT_NAWS];
991 if (optargs = (opt->re_wishes)->sbdata) {
992 eval_naws(optargs);
993 }
994
995 if ( pointerp(Terminals) && sizeof(Terminals)) {
996 if ( Terminals[0][0..3] == "dec-" )
997 Terminals[0] = Terminals[0][4..];
998
999 if ( Terminals[0] == "linux" )
1000 Terminals[0] = "vt100";
1001
1002 Set( P_TTY_TYPE, Terminals[0] );
1003 }
Zesstra01b2b742020-01-20 23:53:01 +01001004 // fuer TELOPT_TM jetzt keine Verhandlung anstossen, aber Handler
1005 // registrieren.
1006 bind_telneg_handler(TELOPT_TM, #'_std_re_handler_tm, 0, 0);
Zesstra57cdbc32020-01-20 23:17:10 +01001007 // und zum Schluss wird der Support fuer CHARSET aktiviert.
1008 bind_telneg_handler(TELOPT_CHARSET, #'_std_re_handler_charset,
1009 #'_std_lo_handler_charset, 1);
MG Mud User88f12472016-06-24 23:31:02 +02001010}
1011
1012// somehow completely out of the ordinary options processing/negotiation. But
1013// the only purpose is to transmit something over the wire which is not shown,
1014// but (likely) answered by the other device.
1015protected void send_telnet_timing_mark() {
1016 struct telopt_s opt = TN[TELOPT_TM];
1017 if (pointerp(opt->data))
1018 opt->data[1] = utime();
1019 else
1020 opt->data = ({ 0, utime() });
1021 // absichtlich nicht do_telnet_ne() verwendet, da dies nicht senden wuerde,
1022 // haette der Client schonmal mit WILL geantwortet. TELOPT_TM ist aber eine
1023 // Option, bei der man das darf...
1024 send_telnet_neg( ({DO, TELOPT_TM}) );
1025}
1026
1027/* Is called from the H_PRINT_PROMPT driver hook and appends the IAC EOR if
1028 * the client supports it.
1029 */
1030void print_prompt(string prompt) {
1031// if (extern_call() && previous_object()!=this_object())
1032// return;
1033
1034 // ggf. Uhrzeit in den prompt reinschreiben.
1035 prompt = regreplace(prompt,"\\t",strftime("%H:%M"),0);
1036 // Prompt senden
1037 tell_object(this_object(), prompt);
1038 // Und EOR senden, falls vom Client gewuenscht.
1039 struct telopt_s opt = TN[TELOPT_EOR];
Zesstra0c8df412020-02-03 20:26:50 +01001040 if (structp(opt) && opt->state->localside == 1)
MG Mud User88f12472016-06-24 23:31:02 +02001041 {
1042 binary_message(({IAC, EOR}), 1);
1043 DTN("tn_eor ",({IAC,EOR}));
1044 }
1045}
1046
1047// Helper
1048private void eval_naws(int *optargs) {
1049 int l, c;
1050
1051 if ( sizeof(optargs) != 4 )
1052 {
1053 tell_object(this_object(),
1054 break_string( sprintf("Dein Client hat einen Fehler beim"
1055 +"Aushandeln der TELOPT_NAWS - er hat"
1056 +"IAC SB %O IAC SE gesendet!\n",
1057 optargs), 78,
1058 "Der GameDriver teilt Dir mit: " ));
1059 // und dem Client sagen, dass er den Schrott nicht mehr uebertragen
1060 // soll (falls wir das nicht schon gemacht haben).
1061 struct telopt_s opt = TN[TELOPT_NAWS];
1062 if (opt->state->remoteside == WILL
1063 && opt->lo_wishes->remoteside != DONT)
1064 send_telnet_neg(( {DONT, TELOPT_NAWS}) );
1065 return;
1066 }
1067
1068 if ( interactive(this_object()) ){
1069 if ( !optargs[1] )
1070 c = optargs[0];
1071 else
1072 c = optargs[1] + optargs[0] * 256;
1073
1074 if ( c < 35 ){
1075 if (Query(P_TTY_SHOW))
1076 tell_object( this_object(),
1077 break_string("Dein Fenster ist schmaler als"
1078 +" 35 Zeichen? Du scherzt. ;-)"
1079 +" Ich benutze den Standardwert"
1080 +" von 80 Zeichen.\n", 78,
1081 "Der GameDriver teilt Dir mit: ")
1082 );
1083 c = 80;
1084 }
1085
1086 if ( !optargs[3] )
1087 l = optargs[2];
1088 else
1089 l = 256 * optargs[2] + optargs[3];
1090
1091 if ( l > 100 ){
1092 //TODO: remove
1093 l = 100;
1094 if (Query(P_TTY_SHOW))
1095 tell_object( this_object(),
1096 break_string("Tut mir leid, aber ich kann"
1097 +" nur bis zu 100 Zeilen"
1098 +" verwalten.\n", (c ? c-2 : 78),
1099 "Der GameDriver teilt Dir mit: " )
1100 );
1101 }
1102
1103 if ( l < 3 ){
1104 if (Query(P_TTY_SHOW))
1105 tell_object( this_object(),
1106 break_string("Du willst weniger als drei"
1107 +" Zeilen benutzen? Glaub ich"
1108 +" Dir nicht - ich benutze den"
1109 +" Standardwert von 24"
1110 +" Zeilen.\n", (c ? c-2 : 78),
1111 "Der GameDriver teilt Dir mit: " )
1112 );
1113 l = 24;
1114 }
1115
Vanion50652322020-03-10 21:13:25 +01001116 if ( (({int}) Query(P_TTY_ROWS) != l) ||
1117 (({int}) Query(P_TTY_COLS) != c) ){
MG Mud User88f12472016-06-24 23:31:02 +02001118 Set( P_TTY_ROWS, l );
1119 Set( P_TTY_COLS, c );
1120
1121 if (Query(P_TTY_SHOW))
1122 tell_object( this_object(),
1123 break_string("Du hast Deine Fenstergroesse auf"
1124 +" "+l+" Zeilen und "+c+
1125 " Spalten geaendert.\n", c-2,
1126 "Der GameDriver teilt Dir mit: ")
1127 );
1128 }
1129 }
1130}
1131
MG Mud User88f12472016-06-24 23:31:02 +02001132// Query-/Set-Methoden
1133// Und wenn hier einer von aussen dran rumpfuscht, werde ich sauer.
1134mapping
1135query_telnet_neg()
1136{
MG Mud User88f12472016-06-24 23:31:02 +02001137 return TN;
1138}
1139
1140// siehe oben
1141string *
1142query_terminals() {
1143 return Terminals;
1144}
1145
1146public int _query_p_lib_telnet_rttime()
1147{
1148 struct telopt_s opt = TN[TELOPT_TM];
1149 if (opt && pointerp(opt->data))
1150 return (opt->data)[0];
1151 return 0;
1152}
1153