Upgrade libgit2
[TortoiseGit.git] / src / TortoisePlink / LDISC.C
blob2b0eabfaa7d6de8f7a7e20909ade2efa1f10e373
1 /*\r
2  * ldisc.c: PuTTY line discipline. Sits between the input coming\r
3  * from keypresses in the window, and the output channel leading to\r
4  * the back end. Implements echo and/or local line editing,\r
5  * depending on what's currently configured.\r
6  */\r
7 \r
8 #include <stdio.h>\r
9 #include <ctype.h>\r
10 #include <assert.h>\r
12 #include "putty.h"\r
13 #include "terminal.h"\r
15 struct Ldisc_tag {\r
16     Terminal *term;\r
17     Backend *backend;\r
18     Seat *seat;\r
20     /*\r
21      * When the backend is not reporting true from sendok(), terminal\r
22      * input that comes here is stored in this bufchain instead. When\r
23      * the backend later decides it wants session input, we empty the\r
24      * queue in ldisc_check_sendok_callback(), passing its contents on\r
25      * to the backend. Before then, we also provide data from this\r
26      * queue to term_get_userpass_input() via ldisc_get_input_token(),\r
27      * to be interpreted as user responses to username and password\r
28      * prompts during authentication.\r
29      *\r
30      * Unfortunately, the data stored in this queue is not all of the\r
31      * same type: our output to the backend consists of both raw bytes\r
32      * sent to backend_send(), and also session specials such as\r
33      * SS_EOL and SS_EC. So we have to encode our queued data in a way\r
34      * that can represent both.\r
35      *\r
36      * The encoding is private to this source file, so we can change\r
37      * it if necessary and only have to worry about the encode and\r
38      * decode functions here. Currently, it is:\r
39      *\r
40      *  - Bytes other than 0xFF are stored literally.\r
41      *  - The byte 0xFF itself is stored as 0xFF 0xFF.\r
42      *  - A session special (code, arg) is stored as 0xFF, followed by\r
43      *    a big-endian 4-byte integer containing code, followed by\r
44      *    another big-endian 4-byte integer containing arg.\r
45      *\r
46      * (This representation relies on session special codes being at\r
47      * most 0xFEFFFFFF when represented in 32 bits, so that the first\r
48      * byte of the 'code' integer can't be confused with the 0xFF\r
49      * followup byte indicating a literal 0xFF, But since session\r
50      * special codes are defined by an enum counting up from zero, and\r
51      * there are only a couple of dozen of them, that shouldn't be a\r
52      * problem! Even so, just in case, an assertion checks that at\r
53      * encode time.)\r
54      */\r
55     bufchain input_queue;\r
57     IdempotentCallback input_queue_callback;\r
58     prompts_t *prompts;\r
60     /*\r
61      * Values cached out of conf.\r
62      */\r
63     bool telnet_keyboard, telnet_newline;\r
64     int protocol, localecho, localedit;\r
66     char *buf;\r
67     size_t buflen, bufsiz;\r
68     bool quotenext;\r
69 };\r
71 #define ECHOING (ldisc->localecho == FORCE_ON || \\r
72                  (ldisc->localecho == AUTO && \\r
73                       (backend_ldisc_option_state(ldisc->backend, LD_ECHO))))\r
74 #define EDITING (ldisc->localedit == FORCE_ON || \\r
75                  (ldisc->localedit == AUTO && \\r
76                       (backend_ldisc_option_state(ldisc->backend, LD_EDIT))))\r
78 static void c_write(Ldisc *ldisc, const void *buf, int len)\r
79 {\r
80     seat_stdout(ldisc->seat, buf, len);\r
81 }\r
83 static int plen(Ldisc *ldisc, unsigned char c)\r
84 {\r
85     if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term)))\r
86         return 1;\r
87     else if (c < 128)\r
88         return 2;                      /* ^x for some x */\r
89     else if (in_utf(ldisc->term) && c >= 0xC0)\r
90         return 1;                      /* UTF-8 introducer character\r
91                                         * (FIXME: combining / wide chars) */\r
92     else if (in_utf(ldisc->term) && c >= 0x80 && c < 0xC0)\r
93         return 0;                      /* UTF-8 followup character */\r
94     else\r
95         return 4;                      /* <XY> hex representation */\r
96 }\r
98 static void pwrite(Ldisc *ldisc, unsigned char c)\r
99 {\r
100     if ((c >= 32 && c <= 126) ||\r
101         (!in_utf(ldisc->term) && c >= 0xA0) ||\r
102         (in_utf(ldisc->term) && c >= 0x80)) {\r
103         c_write(ldisc, &c, 1);\r
104     } else if (c < 128) {\r
105         char cc[2];\r
106         cc[1] = (c == 127 ? '?' : c + 0x40);\r
107         cc[0] = '^';\r
108         c_write(ldisc, cc, 2);\r
109     } else {\r
110         char cc[5];\r
111         sprintf(cc, "<%02X>", c);\r
112         c_write(ldisc, cc, 4);\r
113     }\r
116 static bool char_start(Ldisc *ldisc, unsigned char c)\r
118     if (in_utf(ldisc->term))\r
119         return (c < 0x80 || c >= 0xC0);\r
120     else\r
121         return true;\r
124 static void bsb(Ldisc *ldisc, int n)\r
126     while (n--)\r
127         c_write(ldisc, "\010 \010", 3);\r
130 static void ldisc_input_queue_callback(void *ctx);\r
132 #define CTRL(x) (x^'@')\r
133 #define KCTRL(x) ((x^'@') | 0x100)\r
135 Ldisc *ldisc_create(Conf *conf, Terminal *term, Backend *backend, Seat *seat)\r
137     Ldisc *ldisc = snew(Ldisc);\r
139     ldisc->buf = NULL;\r
140     ldisc->buflen = 0;\r
141     ldisc->bufsiz = 0;\r
142     ldisc->quotenext = false;\r
144     ldisc->backend = backend;\r
145     ldisc->term = term;\r
146     ldisc->seat = seat;\r
148     bufchain_init(&ldisc->input_queue);\r
150     ldisc->prompts = NULL;\r
151     ldisc->input_queue_callback.fn = ldisc_input_queue_callback;\r
152     ldisc->input_queue_callback.ctx = ldisc;\r
153     ldisc->input_queue_callback.queued = false;\r
154     bufchain_set_callback(&ldisc->input_queue, &ldisc->input_queue_callback);\r
156     ldisc_configure(ldisc, conf);\r
158     /* Link ourselves into the backend and the terminal */\r
159     if (term)\r
160         term->ldisc = ldisc;\r
161     if (backend)\r
162         backend_provide_ldisc(backend, ldisc);\r
164     return ldisc;\r
167 void ldisc_configure(Ldisc *ldisc, Conf *conf)\r
169     ldisc->telnet_keyboard = conf_get_bool(conf, CONF_telnet_keyboard);\r
170     ldisc->telnet_newline = conf_get_bool(conf, CONF_telnet_newline);\r
171     ldisc->protocol = conf_get_int(conf, CONF_protocol);\r
172     ldisc->localecho = conf_get_int(conf, CONF_localecho);\r
173     ldisc->localedit = conf_get_int(conf, CONF_localedit);\r
176 void ldisc_free(Ldisc *ldisc)\r
178     bufchain_clear(&ldisc->input_queue);\r
179     if (ldisc->term)\r
180         ldisc->term->ldisc = NULL;\r
181     if (ldisc->backend)\r
182         backend_provide_ldisc(ldisc->backend, NULL);\r
183     if (ldisc->buf)\r
184         sfree(ldisc->buf);\r
185     if (ldisc->prompts && ldisc->prompts->ldisc_ptr_to_us == &ldisc->prompts)\r
186         ldisc->prompts->ldisc_ptr_to_us = NULL;\r
187     delete_callbacks_for_context(ldisc);\r
188     sfree(ldisc);\r
191 void ldisc_echoedit_update(Ldisc *ldisc)\r
193     seat_echoedit_update(ldisc->seat, ECHOING, EDITING);\r
196 void ldisc_enable_prompt_callback(Ldisc *ldisc, prompts_t *prompts)\r
198     /*\r
199      * Called by the terminal to indicate that there's a prompts_t\r
200      * currently in flight, or to indicate that one has just finished\r
201      * (by passing NULL). When ldisc->prompts is not null, we notify\r
202      * the terminal whenever new data arrives in our input queue, so\r
203      * that it can continue the interactive prompting process.\r
204      */\r
205     ldisc->prompts = prompts;\r
206     if (prompts)\r
207         ldisc->prompts->ldisc_ptr_to_us = &ldisc->prompts;\r
210 static void ldisc_input_queue_callback(void *ctx)\r
212     /*\r
213      * Toplevel callback that is triggered whenever the input queue\r
214      * lengthens. If we're currently processing an interactive prompt,\r
215      * we call back the Terminal to tell it to do some more stuff with\r
216      * that prompt based on the new input.\r
217      */\r
218     Ldisc *ldisc = (Ldisc *)ctx;\r
219     if (ldisc->term && ldisc->prompts) {\r
220         /*\r
221          * The integer return value from this call is discarded,\r
222          * because we have no channel to pass it on to the backend\r
223          * that originally wanted it. But that's OK, because if the\r
224          * return value is >= 0 (that is, the prompts are either\r
225          * completely filled in, or aborted by the user), then the\r
226          * terminal will notify the callback in the prompts_t, and\r
227          * when that calls term_get_userpass_input again, it will\r
228          * return the same answer again.\r
229          */\r
230         term_get_userpass_input(ldisc->term, ldisc->prompts);\r
231     }\r
234 static void ldisc_to_backend_raw(\r
235     Ldisc *ldisc, const void *vbuf, size_t len)\r
237     if (backend_sendok(ldisc->backend)) {\r
238         backend_send(ldisc->backend, vbuf, len);\r
239     } else {\r
240         const char *buf = (const char *)vbuf;\r
241         while (len > 0) {\r
242             /*\r
243              * Encode raw data in input_queue, by storing large chunks\r
244              * as long as they don't include 0xFF, and pausing every\r
245              * time they do to escape it.\r
246              */\r
247             const char *ff = memchr(buf, '\xFF', len);\r
248             size_t this_len = ff ? ff - buf : len;\r
249             if (this_len > 0) {\r
250                 bufchain_add(&ldisc->input_queue, buf, len);\r
251             } else {\r
252                 bufchain_add(&ldisc->input_queue, "\xFF\xFF", 2);\r
253                 this_len = 1;\r
254             }\r
255             buf += this_len;\r
256             len -= this_len;\r
257         }\r
258     }\r
261 static void ldisc_to_backend_special(\r
262     Ldisc *ldisc, SessionSpecialCode code, int arg)\r
264     if (backend_sendok(ldisc->backend)) {\r
265         backend_special(ldisc->backend, code, arg);\r
266     } else {\r
267         /*\r
268          * Encode a session special in input_queue.\r
269          */\r
270         unsigned char data[9];\r
271         data[0] = 0xFF;\r
272         PUT_32BIT_MSB_FIRST(data+1, code);\r
273         PUT_32BIT_MSB_FIRST(data+5, arg);\r
274         assert(data[1] != 0xFF &&\r
275                "SessionSpecialCode encoding collides with FF FF escape");\r
276         bufchain_add(&ldisc->input_queue, data, 9);\r
277     }\r
280 bool ldisc_has_input_buffered(Ldisc *ldisc)\r
282     return bufchain_size(&ldisc->input_queue) > 0;\r
285 LdiscInputToken ldisc_get_input_token(Ldisc *ldisc)\r
287     assert(bufchain_size(&ldisc->input_queue) > 0 &&\r
288            "You're not supposed to call this unless there is buffered input!");\r
290     LdiscInputToken tok;\r
292     char c;\r
293     bufchain_fetch_consume(&ldisc->input_queue, &c, 1);\r
294     if (c != '\xFF') {\r
295         /* A literal non-FF byte */\r
296         tok.is_special = false;\r
297         tok.chr = c;\r
298         return tok;\r
299     } else {\r
300         char data[8];\r
302         /* See if the byte after the FF is also FF, indicating a literal FF */\r
303         bufchain_fetch_consume(&ldisc->input_queue, data, 1);\r
304         if (data[0] == '\xFF') {\r
305             tok.is_special = false;\r
306             tok.chr = '\xFF';\r
307             return tok;\r
308         }\r
310         /* If not, get the rest of an 8-byte chunk and decode a special */\r
311         bufchain_fetch_consume(&ldisc->input_queue, data+1, 7);\r
312         tok.is_special = true;\r
313         tok.code = GET_32BIT_MSB_FIRST(data);\r
314         tok.arg = toint(GET_32BIT_MSB_FIRST(data+4));\r
315         return tok;\r
316     }\r
319 static void ldisc_check_sendok_callback(void *ctx)\r
321     Ldisc *ldisc = (Ldisc *)ctx;\r
323     if (!(ldisc->backend && backend_sendok(ldisc->backend)))\r
324         return;\r
326     /*\r
327      * Flush the ldisc input queue into the backend, which is now\r
328      * willing to receive the data.\r
329      */\r
330     while (bufchain_size(&ldisc->input_queue) > 0) {\r
331         /*\r
332          * Process either a chunk of non-special data, or an FF\r
333          * escape, depending on whether the first thing we see is an\r
334          * FF byte.\r
335          */\r
336         ptrlen data = bufchain_prefix(&ldisc->input_queue);\r
337         const char *ff = memchr(data.ptr, '\xFF', data.len);\r
338         if (ff != data.ptr) {\r
339             /* Send a maximal block of data not containing any\r
340              * difficult bytes. */\r
341             if (ff)\r
342                 data.len = ff - (const char *)data.ptr;\r
343             backend_send(ldisc->backend, data.ptr, data.len);\r
344             bufchain_consume(&ldisc->input_queue, data.len);\r
345         } else {\r
346             /* Decode either a special or an escaped FF byte. The\r
347              * easiest way to do this is to reuse the decoding code\r
348              * already in ldisc_get_input_token. */\r
349             LdiscInputToken tok = ldisc_get_input_token(ldisc);\r
350             if (tok.is_special)\r
351                 backend_special(ldisc->backend, tok.code, tok.arg);\r
352             else\r
353                 backend_send(ldisc->backend, &tok.chr, 1);\r
354         }\r
355     }\r
358 void ldisc_check_sendok(Ldisc *ldisc)\r
360     queue_toplevel_callback(ldisc_check_sendok_callback, ldisc);\r
363 void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive)\r
365     const char *buf = (const char *)vbuf;\r
366     int keyflag = 0;\r
368     assert(ldisc->term);\r
370     if (interactive) {\r
371         /*\r
372          * Interrupt a paste from the clipboard, if one was in\r
373          * progress when the user pressed a key. This is easier than\r
374          * buffering the current piece of data and saving it until the\r
375          * terminal has finished pasting, and has the potential side\r
376          * benefit of permitting a user to cancel an accidental huge\r
377          * paste.\r
378          */\r
379         term_nopaste(ldisc->term);\r
380     }\r
382     /*\r
383      * Less than zero means null terminated special string.\r
384      */\r
385     if (len < 0) {\r
386         len = strlen(buf);\r
387         keyflag = KCTRL('@');\r
388     }\r
389     /*\r
390      * Either perform local editing, or just send characters.\r
391      */\r
392     if (EDITING) {\r
393         while (len--) {\r
394             int c;\r
395             c = (unsigned char)(*buf++) + keyflag;\r
396             if (!interactive && c == '\r')\r
397                 c += KCTRL('@');\r
398             switch (ldisc->quotenext ? ' ' : c) {\r
399                 /*\r
400                  * ^h/^?: delete, and output BSBs, to return to\r
401                  * last character boundary (in UTF-8 mode this may\r
402                  * be more than one byte)\r
403                  * ^w: delete, and output BSBs, to return to last\r
404                  * space/nonspace boundary\r
405                  * ^u: delete, and output BSBs, to return to BOL\r
406                  * ^c: Do a ^u then send a telnet IP\r
407                  * ^z: Do a ^u then send a telnet SUSP\r
408                  * ^\: Do a ^u then send a telnet ABORT\r
409                  * ^r: echo "^R\n" and redraw line\r
410                  * ^v: quote next char\r
411                  * ^d: if at BOL, end of file and close connection,\r
412                  * else send line and reset to BOL\r
413                  * ^m: send line-plus-\r\n and reset to BOL\r
414                  */\r
415               case KCTRL('H'):\r
416               case KCTRL('?'):         /* backspace/delete */\r
417                 if (ldisc->buflen > 0) {\r
418                     do {\r
419                         if (ECHOING)\r
420                             bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
421                         ldisc->buflen--;\r
422                     } while (!char_start(ldisc, ldisc->buf[ldisc->buflen]));\r
423                 }\r
424                 break;\r
425               case CTRL('W'):          /* delete word */\r
426                 while (ldisc->buflen > 0) {\r
427                     if (ECHOING)\r
428                         bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
429                     ldisc->buflen--;\r
430                     if (ldisc->buflen > 0 &&\r
431                         isspace((unsigned char)ldisc->buf[ldisc->buflen-1]) &&\r
432                         !isspace((unsigned char)ldisc->buf[ldisc->buflen]))\r
433                         break;\r
434                 }\r
435                 break;\r
436               case CTRL('U'):          /* delete line */\r
437               case CTRL('C'):          /* Send IP */\r
438               case CTRL('\\'):         /* Quit */\r
439               case CTRL('Z'):          /* Suspend */\r
440                 while (ldisc->buflen > 0) {\r
441                     if (ECHOING)\r
442                         bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
443                     ldisc->buflen--;\r
444                 }\r
445                 if (c == CTRL('U'))\r
446                     break;             /* ^U *just* erases a line */\r
447                 ldisc_to_backend_special(ldisc, SS_EL, 0);\r
448                 /*\r
449                  * We don't send IP, SUSP or ABORT if the user has\r
450                  * configured telnet specials off! This breaks\r
451                  * talkers otherwise.\r
452                  */\r
453                 if (!ldisc->telnet_keyboard)\r
454                     goto default_case;\r
455                 if (c == CTRL('C'))\r
456                     ldisc_to_backend_special(ldisc, SS_IP, 0);\r
457                 if (c == CTRL('Z'))\r
458                     ldisc_to_backend_special(ldisc, SS_SUSP, 0);\r
459                 if (c == CTRL('\\'))\r
460                     ldisc_to_backend_special(ldisc, SS_ABORT, 0);\r
461                 break;\r
462               case CTRL('R'):          /* redraw line */\r
463                 if (ECHOING) {\r
464                     int i;\r
465                     c_write(ldisc, "^R\r\n", 4);\r
466                     for (i = 0; i < ldisc->buflen; i++)\r
467                         pwrite(ldisc, ldisc->buf[i]);\r
468                 }\r
469                 break;\r
470               case CTRL('V'):          /* quote next char */\r
471                 ldisc->quotenext = true;\r
472                 break;\r
473               case CTRL('D'):          /* logout or send */\r
474                 if (ldisc->buflen == 0) {\r
475                     ldisc_to_backend_special(ldisc, SS_EOF, 0);\r
476                 } else {\r
477                     ldisc_to_backend_raw(ldisc, ldisc->buf, ldisc->buflen);\r
478                     ldisc->buflen = 0;\r
479                 }\r
480                 break;\r
481                 /*\r
482                  * This particularly hideous bit of code from RDB\r
483                  * allows ordinary ^M^J to do the same thing as\r
484                  * magic-^M when in Raw protocol. The line `case\r
485                  * KCTRL('M'):' is _inside_ the if block. Thus:\r
486                  *\r
487                  *  - receiving regular ^M goes straight to the\r
488                  *    default clause and inserts as a literal ^M.\r
489                  *  - receiving regular ^J _not_ directly after a\r
490                  *    literal ^M (or not in Raw protocol) fails the\r
491                  *    if condition, leaps to the bottom of the if,\r
492                  *    and falls through into the default clause\r
493                  *    again.\r
494                  *  - receiving regular ^J just after a literal ^M\r
495                  *    in Raw protocol passes the if condition,\r
496                  *    deletes the literal ^M, and falls through\r
497                  *    into the magic-^M code\r
498                  *  - receiving a magic-^M empties the line buffer,\r
499                  *    signals end-of-line in one of the various\r
500                  *    entertaining ways, and _doesn't_ fall out of\r
501                  *    the bottom of the if and through to the\r
502                  *    default clause because of the break.\r
503                  */\r
504               case CTRL('J'):\r
505                 if (ldisc->protocol == PROT_RAW &&\r
506                     ldisc->buflen > 0 && ldisc->buf[ldisc->buflen - 1] == '\r') {\r
507                     if (ECHOING)\r
508                         bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
509                     ldisc->buflen--;\r
510                     /* FALLTHROUGH */\r
511               case KCTRL('M'):         /* send with newline */\r
512                     if (ldisc->buflen > 0)\r
513                         ldisc_to_backend_raw(ldisc, ldisc->buf, ldisc->buflen);\r
514                     if (ldisc->protocol == PROT_RAW)\r
515                         ldisc_to_backend_raw(ldisc, "\r\n", 2);\r
516                     else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)\r
517                         ldisc_to_backend_special(ldisc, SS_EOL, 0);\r
518                     else\r
519                         ldisc_to_backend_raw(ldisc, "\r", 1);\r
520                     if (ECHOING)\r
521                         c_write(ldisc, "\r\n", 2);\r
522                     ldisc->buflen = 0;\r
523                     break;\r
524                 }\r
525                 /* FALLTHROUGH */\r
526               default:                 /* get to this label from ^V handler */\r
527               default_case:\r
528                 sgrowarray(ldisc->buf, ldisc->bufsiz, ldisc->buflen);\r
529                 ldisc->buf[ldisc->buflen++] = c;\r
530                 if (ECHOING)\r
531                     pwrite(ldisc, (unsigned char) c);\r
532                 ldisc->quotenext = false;\r
533                 break;\r
534             }\r
535         }\r
536     } else {\r
537         if (ldisc->buflen != 0) {\r
538             ldisc_to_backend_raw(ldisc, ldisc->buf, ldisc->buflen);\r
539             while (ldisc->buflen > 0) {\r
540                 bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));\r
541                 ldisc->buflen--;\r
542             }\r
543         }\r
544         if (len > 0) {\r
545             if (ECHOING)\r
546                 c_write(ldisc, buf, len);\r
547             if (keyflag && ldisc->protocol == PROT_TELNET && len == 1) {\r
548                 switch (buf[0]) {\r
549                   case CTRL('M'):\r
550                     if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline)\r
551                         ldisc_to_backend_special(ldisc, SS_EOL, 0);\r
552                     else\r
553                         ldisc_to_backend_raw(ldisc, "\r", 1);\r
554                     break;\r
555                   case CTRL('?'):\r
556                   case CTRL('H'):\r
557                     if (ldisc->telnet_keyboard) {\r
558                         ldisc_to_backend_special(ldisc, SS_EC, 0);\r
559                         break;\r
560                     }\r
561                   case CTRL('C'):\r
562                     if (ldisc->telnet_keyboard) {\r
563                         ldisc_to_backend_special(ldisc, SS_IP, 0);\r
564                         break;\r
565                     }\r
566                   case CTRL('Z'):\r
567                     if (ldisc->telnet_keyboard) {\r
568                         ldisc_to_backend_special(ldisc, SS_SUSP, 0);\r
569                         break;\r
570                     }\r
572                   default:\r
573                     ldisc_to_backend_raw(ldisc, buf, len);\r
574                     break;\r
575                 }\r
576             } else\r
577                 ldisc_to_backend_raw(ldisc, buf, len);\r
578         }\r
579     }\r