doorman.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. #include "terminal.h"
  2. #include "utils.h"
  3. #include <ctype.h>
  4. #include <fcntl.h>
  5. #include <fstream>
  6. #include <iomanip>
  7. #include <iostream>
  8. #include <map>
  9. #include <pty.h>
  10. #include <sstream>
  11. #include <stdio.h>
  12. #include <stdlib.h> // random()
  13. #include <string.h>
  14. #include <string>
  15. #include <strings.h> // strcasecmp
  16. #include <sys/select.h>
  17. #include <sys/wait.h>
  18. #include <termios.h>
  19. #include <time.h>
  20. #include <unistd.h>
  21. /*
  22. `Tip the DoorMan`
  23. DoorMan is a BBS door manager that handles translating UTF-8 to CP437 for
  24. using ncurses based programs as BBS doors.
  25. We might start with using the quite excellent iconv library for our
  26. conversions, but I believe I'll eventually switch to using a map of some sort
  27. to do the process. (Calling a library over and over again to convert the same
  28. chars to the same thing over and over again just sounds "wrong".)
  29. -NOXLATE (No Translation, leave UTF-8 as-is)
  30. -ICONV (Use ICONV)
  31. (Default will be using our own table.)
  32. We will also need to convert the user input (which will be a BBS Terminal
  33. program) into whatever Linux expects from users. Function keys, arrow keys,
  34. etc.
  35. -NOTERM (Don't translate the terminal input)
  36. Logging will be a huge requirement to figure much of this out!
  37. -NOLOG (Disable logging)
  38. We'll want to optionally capture/eat Ctrl-C, because most programs quit on
  39. that.
  40. -NOC (No Ctrl-C, not the default)
  41. Anything not matching here is considered to be the program + arguments.
  42. */
  43. /*
  44. "FEATURES"
  45. We don't understand color code 96/97. (Bright white/bright cyan). Maybe try
  46. mapping those back to the original non-bright colors?
  47. There's a possibility of missing keys (if they aren't all received in one
  48. "packet"). ?
  49. Unicode double lines are definitely missing! Yikes!
  50. Anything else that exists in unicode that maps over to high ASCII.
  51. https://en.wikipedia.org/wiki/Code_page_437
  52. I'm not sure, but it almost looks like it has the CP437 char and then the
  53. unicode char below it. That might be exactly what I need!
  54. https://jrgraphix.net/r/Unicode/2500-257F
  55. http://xahlee.info/comp/unicode_drawing_shapes.html
  56. Still need to finish up function keys.
  57. still having issues with chars (mc, pageup/pagedown -- garbled chars, that looks
  58. like missing unicode multi-byte issues)
  59. */
  60. enum TRANSLATE { NONE, ICONV, INTERNAL } translate = NONE;
  61. /*
  62. https://softwareengineering.stackexchange.com/questions/141973/how-do-you-achieve-a-numeric-versioning-scheme-with-git
  63. Use tags to mark commits with version numbers:
  64. git tag -a v2.5 -m 'Version 2.5'
  65. Push tags upstream—this is not done by default:
  66. git push --tags
  67. Then use the describe command:
  68. git describe --tags --long
  69. */
  70. std::string version = "0.0.0"; // DMVERSION;
  71. // #include <signal.h> // handle Ctrl-C/SIGINT
  72. /* Log level guideline:
  73. * - ZF_LOG_FATAL - happened something impossible and absolutely unexpected.
  74. * Process can't continue and must be terminated.
  75. * Example: division by zero, unexpected modifications from other thread.
  76. * - ZF_LOG_ERROR - happened something possible, but highly unexpected. The
  77. * process is able to recover and continue execution.
  78. * Example: out of memory (could also be FATAL if not handled properly).
  79. * - ZF_LOG_WARN - happened something that *usually* should not happen and
  80. * significantly changes application behavior for some period of time.
  81. * Example: configuration file not found, auth error.
  82. * - ZF_LOG_INFO - happened significant life cycle event or major state
  83. * transition.
  84. * Example: app started, user logged in.
  85. * - ZF_LOG_DEBUG - minimal set of events that could help to reconstruct the
  86. * execution path. Usually disabled in release builds.
  87. * - ZF_LOG_VERBOSE - all other events. Usually disabled in release builds.
  88. *
  89. * *Ideally*, log file of debugged, well tested, production ready application
  90. * should be empty or very small. Choosing a right log level is as important as
  91. * providing short and self descriptive log message.
  92. */
  93. /*
  94. #define ZF_LOG_VERBOSE 1
  95. #define ZF_LOG_DEBUG 2
  96. #define ZF_LOG_INFO 3
  97. #define ZF_LOG_WARN 4
  98. #define ZF_LOG_ERROR 5
  99. #define ZF_LOG_FATAL 6
  100. */
  101. // When debugging low-level, use this:
  102. // #define ZF_LOG_LEVEL ZF_LOG_VERBOSE
  103. // Except this doesn't work. It needs to be anywere the
  104. // zf_log.h is included.
  105. // LOGGING with file output
  106. #include "zf_log.h"
  107. FILE *g_log_file;
  108. static void file_output_callback(const zf_log_message *msg, void *arg) {
  109. (void)arg;
  110. *msg->p = '\n';
  111. fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_log_file);
  112. fflush(g_log_file);
  113. }
  114. static void file_output_close(void) { fclose(g_log_file); }
  115. static int file_output_open(const char *const log_path) {
  116. g_log_file = fopen(log_path, "a");
  117. if (!g_log_file) {
  118. ZF_LOGW("Failed to open log file %s", log_path);
  119. return 0;
  120. }
  121. atexit(file_output_close);
  122. zf_log_set_output_v(ZF_LOG_PUT_STD, 0, file_output_callback);
  123. return 1;
  124. }
  125. void log_flush(void) { fflush(g_log_file); }
  126. // END LOGGING
  127. /*
  128. This is done. :D My buffering system works with stack'em.
  129. TO FIX: Stop using c strings, must use char * buffer + int length.
  130. MAY CONTAIN NULL VALUES.
  131. Rework some things here.
  132. Here's the "plan":
  133. if buffer is EMPTY:
  134. time_idle = 1;
  135. // setup for "random timeout value mess"
  136. // we're in luck! The last parameter is time interval/timeout. :D
  137. timeout.tv_sec = 10; // randrange(10-25)
  138. timeout.tv_usec = 0;
  139. NOT EMPTY:
  140. // we're in luck! The last parameter is time interval/timeout. :D
  141. timeout.tv_sec = 0;
  142. timeout.tv_usec = 10; // Wild Guess Here? Maybe higher, maybe
  143. lower? time_idle = 0;
  144. ON READ:
  145. read/append to current buffer.
  146. We can't use nulls -- what if they are using ZModem, there's nulls in
  147. the file! Look for trailing / the very last "\r\n".
  148. (I could mangle/chunk it line by line. But I'm not sure I'd need to do
  149. that.)
  150. Optional "mangle" buffer up to that very point -- and send up to that
  151. point.
  152. Option #2: Maybe we send everything if program has been running for
  153. under 20 seconds. This would allow the ANSI detect to not get screwed up by
  154. this new idea.
  155. ON TIMEOUT:
  156. if time_idle:
  157. Activate funny harry timeout events.
  158. else:
  159. Ok, we *STILL* haven't received any more characters into the buffer --
  160. even after waiting. (Maybe we haven't waited long enough?)
  161. send the pending information in the buffer and clear it out.
  162. Maybe this is a prompt, and there won't be a \r\n.
  163. This allows for cleaner process of "lines" of buffer. We shouldn't break
  164. in the midDLE OF A WORD. Downside is that we sit on buffer contents a
  165. little while / some amount of time -- which will add some lag to prompts
  166. showing up.
  167. (LAG? Are you kidding?)
  168. ZModem:
  169. start: "rz^M**"...
  170. 05-12 18:12:15.916 >> rz^M**^XB00000000000000^M<8A>^Q
  171. 05-12 18:12:15.928 << **\x18B0100000023be50\r\n\x11
  172. 05-12 18:12:15.928 >> *^XC^D
  173. 05-12 18:12:15.939 << **\x18B0900000000a87c\r\n\x11
  174. 05-12 18:12:15.940 >> *^XC
  175. # Start of PK zipfile.
  176. 05-12 18:12:15.941 >> PK^C^D^T
  177. end:
  178. 05-12 18:26:38.700 << **\x18B0100000023be50\r\n\x11
  179. 05-12 18:26:38.700 >> **^XB0823a77600344c^M<8A>
  180. 05-12 18:26:38.711 << **\x18B0800000000022d\r\n
  181. 05-12 18:26:38.712 >> OO^MESC[0m
  182. */
  183. // TODO: Get everything above this -- into another file.
  184. /*
  185. void display_line(const char *line) {
  186. char input[1024];
  187. char output[1024];
  188. strcpy(input, line);
  189. converter.convert(input, output, sizeof(output));
  190. printf("%s\n", output);
  191. }
  192. */
  193. void terminal_output(TRANSLATE xlate, std::string &buffer) {
  194. /*
  195. The current problem is this. The unichoad characters are getting spilt by
  196. buffer breaks.
  197. */
  198. static Terminal term;
  199. // static IConv converter("UTF-8", "CP437");
  200. // static IConv converter("CP437", "UTF-8"); // TO, FROM
  201. ZF_LOGV_MEM(buffer.data(), buffer.size(), "Buffer now:");
  202. ZF_LOGD("Buffer: %s", repr(buffer.c_str()));
  203. if (xlate == NONE) {
  204. // no translation
  205. write(STDOUT_FILENO, buffer.data(), buffer.size());
  206. buffer.clear();
  207. } else {
  208. std::string ansi;
  209. int dcs_mode = term.dcs();
  210. // The Hunt for Red Unichoad
  211. // https://en.wikipedia.org/wiki/UTF-8
  212. /*
  213. Number
  214. of bytes First
  215. code point Last
  216. code point Byte 1 Byte 2 Byte 3 Byte 4
  217. 1 U+0000 U+007F 0xxxxxxx
  218. 2 U+0080 U+07FF 110xxxxx 10xxxxxx
  219. 3 U+0800 U+FFFF 1110xxxx 10xxxxxx 10xxxxxx
  220. 4 U+10000 U+10FFFF[18] 11110xxx 10xxxxxx 10xxxxxx
  221. 10xxxxxx
  222. */
  223. int length = buffer.size();
  224. int save = 0; // default to saving zero characters.
  225. char uchoad;
  226. // 1 char searches:
  227. if (length >= 1) {
  228. uchoad = buffer[length - 1];
  229. if (unicode_len(uchoad))
  230. save = 1;
  231. }
  232. if (length >= 2) {
  233. uchoad = buffer[length - 2];
  234. if (unicode_len(uchoad) > 2)
  235. save = 2;
  236. }
  237. // We'll probably never see any of these, but it's here if we need it.
  238. if (length >= 3) {
  239. uchoad = buffer[length - 3];
  240. if (unicode_len(uchoad) > 3)
  241. save = 3;
  242. }
  243. std::string saved;
  244. if (save) {
  245. ZF_LOGE("Saving %d chars", save);
  246. ZF_LOGV_MEM(buffer.data(), buffer.size(), "PRE-SAVE:");
  247. saved = buffer.substr(length - save, save);
  248. buffer = buffer.substr(0, length - save);
  249. ZF_LOGV_MEM(buffer.data(), buffer.size(), "After Save:");
  250. ZF_LOGV_MEM(saved.data(), saved.size(), "SAVED:");
  251. }
  252. // TO FIX: Pull chars out, and look up in the map. Don't brute force your
  253. // way through all of the times.
  254. static std::map<std::string, int> utf8cp437 = {
  255. {"\xe2\x98\xba", 1},
  256. {"\xe2\x98\xbb", 2},
  257. {"\xe2\x99\xa5", 3},
  258. {"\xe2\x99\xa6", 4},
  259. {"\xe2\x99\xa3", 5},
  260. {"\xe2\x99\xa0", 6},
  261. // {"\xe2\x80\xa2", 7}, {"\xe2\x97\x98", 8},
  262. {"\xe2\x97\x8b", 9},
  263. //{"\xe2\x97\x99", 0x0a},
  264. {"\xe2\x99\x82", 0x0b},
  265. {"\xe2\x99\x80", 0x0c},
  266. //{"\xe2\x99\xaa", 0x0d},
  267. {"\xe2\x99\xab", 0x0e},
  268. {"\xe2\x98\xbc", 0x0f},
  269. {"\xe2\x96\xba", 0x10},
  270. {"\xe2\x97\x84", 0x11},
  271. {"\xe2\x86\x95", 0x12},
  272. {"\xe2\x80\xbc", 0x13},
  273. {"\xc2\xb6", 0x14},
  274. {"\xc2\xa7", 0x15},
  275. {"\xe2\x96\xac", 0x16},
  276. {"\xe2\x86\xa8", 0x17},
  277. {"\xe2\x86\x91", 0x18},
  278. {"\xe2\x86\x93", 0x19},
  279. {"\xe2\x86\x92", 0x1a},
  280. {"\xe2\x86\x90", 0x1b},
  281. {"\xe2\x88\x9f", 0x1c},
  282. {"\xe2\x86\x94", 0x1d},
  283. {"\xe2\x96\xb2", 0x1e},
  284. {"\xe2\x96\xbc", 0x1f},
  285. {"\xe2\x8c\x82", 0x7f},
  286. {"\xc3\x87", 0x80},
  287. {"\xc3\xbc", 0x81},
  288. {"\xc3\xa9", 0x82},
  289. {"\xc3\xa2", 0x83},
  290. {"\xc3\xa4", 0x84},
  291. {"\xc3\xa0", 0x85},
  292. {"\xc3\xa5", 0x86},
  293. {"\xc3\xa7", 0x87},
  294. {"\xc3\xaa", 0x88},
  295. {"\xc3\xab", 0x89},
  296. {"\xc3\xa8", 0x8a},
  297. {"\xc3\xaf", 0x8b},
  298. {"\xc3\xae", 0x8c},
  299. {"\xc3\xac", 0x8d},
  300. {"\xc3\x84", 0x8e},
  301. {"\xc3\x85", 0x8f},
  302. {"\xc3\x89", 0x90},
  303. {"\xc3\xa6", 0x91},
  304. {"\xc3\x86", 0x92},
  305. {"\xc3\xb4", 0x93},
  306. {"\xc3\xb6", 0x94},
  307. {"\xc3\xb2", 0x95},
  308. {"\xc3\xbb", 0x96},
  309. {"\xc3\xb9", 0x97},
  310. {"\xc3\xbf", 0x98},
  311. {"\xc3\x96", 0x99},
  312. {"\xc3\x9c", 0x9a},
  313. {"\xc2\xa2", 0x9b},
  314. {"\xc2\xa3", 0x9c},
  315. {"\xc2\xa5", 0x9d},
  316. {"\xe2\x82\xa7", 0x9e},
  317. {"\xc6\x92", 0x9f},
  318. {"\xc3\xa1", 0xa0},
  319. {"\xc3\xad", 0xa1},
  320. {"\xc3\xb3", 0xa2},
  321. {"\xc3\xba", 0xa3},
  322. {"\xc3\xb1", 0xa4},
  323. {"\xc3\x91", 0xa5},
  324. {"\xc2\xaa", 0xa6},
  325. {"\xc2\xba", 0xa7},
  326. {"\xc2\xbf", 0xa8},
  327. {"\xe2\x8c\x90", 0xa9},
  328. {"\xc2\xac", 0xaa},
  329. {"\xc2\xbd", 0xab},
  330. {"\xc2\xbc", 0xac},
  331. {"\xc2\xa1", 0xad},
  332. {"\xc2\xab", 0xae},
  333. {"\xc2\xbb", 0xaf},
  334. {"\xe2\x96\x91", 0xb0},
  335. {"\xe2\x96\x92", 0xb1},
  336. {"\xe2\x96\x93", 0xb2},
  337. {"\xe2\x94\x82", 0xb3},
  338. {"\xe2\x94\xa4", 0xb4},
  339. {"\xe2\x95\xa1", 0xb5},
  340. {"\xe2\x95\xa2", 0xb6},
  341. {"\xe2\x95\x96", 0xb7},
  342. {"\xe2\x95\x95", 0xb8},
  343. {"\xe2\x95\xa3", 0xb9},
  344. {"\xe2\x95\x91", 0xba},
  345. {"\xe2\x95\x97", 0xbb},
  346. {"\xe2\x95\x9d", 0xbc},
  347. {"\xe2\x95\x9c", 0xbd},
  348. {"\xe2\x95\x9b", 0xbe},
  349. {"\xe2\x94\x90", 0xbf},
  350. {"\xe2\x94\x94", 0xc0},
  351. {"\xe2\x94\xb4", 0xc1},
  352. {"\xe2\x94\xac", 0xc2},
  353. {"\xe2\x94\x9c", 0xc3},
  354. {"\xe2\x94\x80", 0xc4},
  355. {"\xe2\x94\xbc", 0xc5},
  356. {"\xe2\x95\x9e", 0xc6},
  357. {"\xe2\x95\x9f", 0xc7},
  358. {"\xe2\x95\x9a", 0xc8},
  359. {"\xe2\x95\x94", 0xc9},
  360. {"\xe2\x95\xa9", 0xca},
  361. {"\xe2\x95\xa6", 0xcb},
  362. {"\xe2\x95\xa0", 0xcc},
  363. {"\xe2\x95\x90", 0xcd},
  364. {"\xe2\x95\xac", 0xce},
  365. {"\xe2\x95\xa7", 0xcf},
  366. {"\xe2\x95\xa8", 0xd0},
  367. {"\xe2\x95\xa4", 0xd1},
  368. {"\xe2\x95\xa5", 0xd2},
  369. {"\xe2\x95\x99", 0xd3},
  370. {"\xe2\x95\x98", 0xd4},
  371. {"\xe2\x95\x92", 0xd5},
  372. {"\xe2\x95\x93", 0xd6},
  373. {"\xe2\x95\xab", 0xd7},
  374. {"\xe2\x95\xaa", 0xd8},
  375. {"\xe2\x94\x98", 0xd9},
  376. {"\xe2\x94\x8c", 0xda},
  377. {"\xe2\x96\x88", 0xdb},
  378. {"\xe2\x96\x84", 0xdc},
  379. {"\xe2\x96\x8c", 0xdd},
  380. {"\xe2\x96\x90", 0xde},
  381. {"\xe2\x96\x80", 0xdf},
  382. {"\xce\xb1", 0xe0},
  383. {"\xc3\x9f", 0xe1},
  384. {"\xce\x93", 0xe2},
  385. {"\xcf\x80", 0xe3},
  386. {"\xce\xa3", 0xe4},
  387. {"\xcf\x83", 0xe5},
  388. {"\xc2\xb5", 0xe6},
  389. {"\xcf\x84", 0xe7},
  390. {"\xce\xa6", 0xe8},
  391. {"\xce\x98", 0xe9},
  392. {"\xce\xa9", 0xea},
  393. {"\xce\xb4", 0xeb},
  394. {"\xe2\x88\x9e", 0xec},
  395. {"\xcf\x86", 0xed},
  396. {"\xce\xb5", 0xee},
  397. {"\xe2\x88\xa9", 0xef},
  398. {"\xe2\x89\xa1", 0xf0},
  399. {"\xc2\xb1", 0xf1},
  400. {"\xe2\x89\xa5", 0xf2},
  401. {"\xe2\x89\xa4", 0xf3},
  402. {"\xe2\x8c\xa0", 0xf4},
  403. {"\xe2\x8c\xa1", 0xf5},
  404. {"\xc3\xb7", 0xf6},
  405. {"\xe2\x89\x88", 0xf7},
  406. {"\xc2\xb0", 0xf8},
  407. {"\xe2\x88\x99", 0xf9},
  408. {"\xc2\xb7", 0xfa},
  409. {"\xe2\x88\x9a", 0xfb},
  410. {"\xe2\x81\xbf", 0xfc},
  411. {"\xc2\xb2", 0xfd},
  412. {"\xe2\x96\xa0", 0xfe},
  413. {"\xc2\xa0", 0xff}};
  414. int c = 0;
  415. size_t pos;
  416. std::vector<char> keys = {'\xc2', '\xc3', '\xce', '\xcf', '\xe2'};
  417. for (auto ikey = keys.begin(); ikey != keys.end(); ++ikey) {
  418. size_t spos = 0;
  419. while ((pos = buffer.find(*ikey, spos)) != std::string::npos) {
  420. char uchoad = buffer[pos];
  421. int ulen = unicode_len(uchoad);
  422. std::string ustr = buffer.substr(pos, ulen);
  423. ZF_LOGE_MEM(ustr.data(), ustr.length(), "unicode %d : (@%ld)", ulen,
  424. pos);
  425. auto upos = utf8cp437.find(ustr.c_str());
  426. if (upos != utf8cp437.end()) {
  427. std::string to(1, (char)upos->second);
  428. while (replace(buffer, ustr, to)) {
  429. c++;
  430. }
  431. spos = 0;
  432. } else {
  433. ZF_LOGE("failed to locate.");
  434. spos = pos + 1;
  435. }
  436. }
  437. }
  438. if (c) {
  439. ZF_LOGE("vector/map Replaced %d", c);
  440. ZF_LOGV_MEM(buffer.data(), buffer.size(), "After Replace:");
  441. }
  442. /*
  443. if ((buffer.find('\xe2') != std::string::npos) ||
  444. (buffer.find('\xc2') != std::string::npos) ||
  445. (buffer.find('\xc3') != std::string::npos) ||
  446. (buffer.find('\xce') != std::string::npos) ||
  447. (buffer.find('\xcf') != std::string::npos)) {
  448. c = 0;
  449. for (auto it = utf8cp437.begin(); it != utf8cp437.end(); ++it) {
  450. while (replace(buffer, it->first, std::string(1, char(it->second))))
  451. { c++;
  452. }
  453. }
  454. if (c) {
  455. ZF_LOGE("Replaced %d", c);
  456. ZF_LOGV_MEM(buffer.data(), buffer.size(), "After Replace:");
  457. }
  458. }
  459. */
  460. // Convert bright to bold.
  461. while (replace(buffer, "\x1b[90m", "\x1b[1;30m")) {
  462. };
  463. while (replace(buffer, "\x1b[91m", "\x1b[1;31m")) {
  464. };
  465. while (replace(buffer, "\x1b[92m", "\x1b[1;32m")) {
  466. };
  467. while (replace(buffer, "\x1b[93m", "\x1b[1;33m")) {
  468. };
  469. while (replace(buffer, "\x1b[94m", "\x1b[1;34m")) {
  470. };
  471. while (replace(buffer, "\x1b[95m", "\x1b[1;35m")) {
  472. };
  473. while (replace(buffer, "\x1b[96m", "\x1b[1;36m")) {
  474. };
  475. while (replace(buffer, "\x1b[97m", "\x1b[1;37m")) {
  476. };
  477. // Search for DCS mode, and translate the line characters.
  478. for (auto iter = buffer.begin(); iter != buffer.end(); ++iter) {
  479. char c = *iter;
  480. Terminal::termchar tc = term.putchar(c);
  481. if (tc.in_ansi) {
  482. ansi.append(1, c);
  483. if (tc.ansi == Terminal::ANSI_TYPE::START)
  484. continue;
  485. // Ok, the ansi command is completed.
  486. if (tc.ansi ==
  487. Terminal::ANSI_TYPE::DCS) { // Terminal::ANSI_TYPE::DCS) {
  488. // Ok, EAT this command! it's worthless to the terminal we're talking
  489. // to.
  490. dcs_mode = term.dcs();
  491. }
  492. } else {
  493. // Ok, we're not in any ANSI mode.
  494. /*
  495. ┌ ─ ┬ ┐
  496. │ │ │ │
  497. ├ ─ ┼ ┤
  498. └ ─ ┴ ┘
  499. We cheat here. Instead of converting to unicode, we convert to CP437
  500. chars.
  501. */
  502. if (dcs_mode == 0) {
  503. switch (c) {
  504. case 'j':
  505. c = '\xd9'; // '┘';
  506. break;
  507. case 'k':
  508. c = '\xbf'; // '┐';
  509. break;
  510. case 'l':
  511. c = '\xda'; // '┌';
  512. break;
  513. case 'm':
  514. c = '\xc0'; // '└';
  515. break;
  516. case 'n':
  517. c = '\xc5'; // '┼';
  518. break;
  519. case 'q':
  520. c = '\xc4'; // '─';
  521. break;
  522. case 't':
  523. c = '\xc3'; // '├';
  524. break;
  525. case 'u':
  526. c = '\xb4'; // '┤';
  527. break;
  528. case 'v':
  529. c = '\xc1'; // '┴';
  530. break;
  531. case 'w':
  532. c = '\xc2'; // '┬';
  533. break;
  534. case 'x':
  535. c = '\xb3'; // '│';
  536. break;
  537. default:
  538. ZF_LOGE("DCS(0): I don't have translation for [%c]", c);
  539. }
  540. ZF_LOGE("DCS(0) Converted [%c] to [%c]", *iter, c);
  541. *iter = c;
  542. }
  543. }
  544. }
  545. write(STDOUT_FILENO, buffer.data(), buffer.size());
  546. buffer.clear();
  547. buffer.insert(0, saved);
  548. saved.clear();
  549. }
  550. }
  551. void help(void) {
  552. printf("Usage:\n");
  553. printf("Translate (default to internal)\n");
  554. printf("\t-NOXLATE\tNo translation\n");
  555. printf("\t-ICONV\tUse ICONV library\n");
  556. printf("\t-NOLOG\tNo logging\n");
  557. printf("\t-NOC\tDon't allow Ctrl-C\n");
  558. printf("\t-NOTERM\tDon't Translate Keys\n");
  559. printf("\t-LOGTIME\tAdd Hours/Minutes to logfile name\n");
  560. }
  561. int main(int argc, char *argv[]) {
  562. int master;
  563. pid_t pid;
  564. // init_harry();
  565. srandom(time(NULL));
  566. /*
  567. -NOXLATE (No Translation, leave UTF-8 as-is)
  568. -ICONV (Use ICONV)
  569. -NOTERM (Don't translate the terminal input)
  570. -NOLOG (Disable logging)
  571. -NOC (No Ctrl-C, not the default)
  572. */
  573. translate =
  574. ICONV; // Default to internal translation map (well, not initially.)
  575. int CATCH_CTRLC = 0;
  576. int NO_LOGGING = 0;
  577. int TERM_XLATE = 1;
  578. int LOG_TIME = 0;
  579. int x;
  580. for (x = 1; x < argc; x++) {
  581. if (strcasecmp("-NOXLATE", argv[x]) == 0) {
  582. translate = NONE;
  583. continue;
  584. }
  585. if (strcasecmp("-ICONV", argv[x]) == 0) {
  586. translate = ICONV;
  587. continue;
  588. }
  589. if (strcasecmp("-NOC", argv[x]) == 0) {
  590. CATCH_CTRLC = 1;
  591. continue;
  592. }
  593. if (strcasecmp("-NOLOG", argv[x]) == 0) {
  594. NO_LOGGING = 1;
  595. continue;
  596. }
  597. if (strcasecmp("-NOTERM", argv[x]) == 0) {
  598. TERM_XLATE = 0;
  599. continue;
  600. }
  601. if (strcasecmp("-LOGTIME", argv[x]) == 0) {
  602. LOG_TIME = 1;
  603. continue;
  604. }
  605. if ((strcasecmp("-H", argv[x]) == 0) || (strcmp("-?", argv[x]) == 0)) {
  606. // display help information.
  607. help();
  608. return 0;
  609. }
  610. if (argv[x][0] == '-') {
  611. printf("I don't understand arg %s\n", argv[x]);
  612. help();
  613. return 0;
  614. }
  615. break;
  616. }
  617. if (x == argc) {
  618. printf("No command to run found.\n");
  619. help();
  620. return 0;
  621. }
  622. std::string logfile;
  623. {
  624. std::ostringstream buffer;
  625. time_t now = time(NULL);
  626. struct tm *tmp;
  627. tmp = gmtime(&now);
  628. // tmp->tm_mon
  629. buffer << "doorman-" << tmp->tm_year + 1900 << "-" << std::setfill('0')
  630. << std::setw(2) << tmp->tm_mon + 1 << "-" << std::setfill('0')
  631. << std::setw(2) << tmp->tm_mday;
  632. if (LOG_TIME) {
  633. buffer << "-" << std::setw(2) << tmp->tm_hour << std::setw(2)
  634. << tmp->tm_min;
  635. }
  636. buffer << ".log";
  637. logfile = buffer.str();
  638. }
  639. if (NO_LOGGING) {
  640. zf_log_set_output_level(ZF_LOG_NONE);
  641. } else {
  642. if (!file_output_open((const char *)logfile.c_str()))
  643. return 2;
  644. };
  645. ZF_LOGE("DoorMan %s", version.c_str());
  646. // Build the new command to run here
  647. char *args[20]; // max 20 args
  648. char *target_exec = argv[x];
  649. ZF_LOGE("Target: (%d) [%s]", x, target_exec);
  650. // build new args list
  651. args[0] = target_exec;
  652. // We have the target, skip to the next arg
  653. if (x < argc)
  654. ++x;
  655. int ax = 1;
  656. for (; x < argc; x++) {
  657. args[ax] = argv[x];
  658. ZF_LOGD("ARG %d : %s", ax, args[ax]);
  659. ax++;
  660. };
  661. // null term the list
  662. args[ax] = NULL;
  663. pid = forkpty(&master, NULL, NULL, NULL);
  664. // impossible to fork
  665. if (pid < 0) {
  666. return 1;
  667. }
  668. // child
  669. else if (pid == 0) {
  670. // This has been done up above.
  671. /*
  672. char *args[20]; // max 20 args
  673. int x;
  674. char new_exec[] = TARGET;
  675. // build new args list
  676. args[0] = new_exec;
  677. for (x = 1; x < argc; x++) {
  678. args[x] = argv[x];
  679. };
  680. // null term the list
  681. args[x] = NULL;
  682. */
  683. // run TARGET, run!
  684. /*
  685. for (x = 0; args[x] != nullptr; x++) {
  686. ZF_LOGD("%d : %s", x, args[x]);
  687. }
  688. */
  689. execvp(target_exec, args);
  690. }
  691. // parent
  692. else {
  693. struct termios tios, orig1;
  694. struct timeval timeout;
  695. ZF_LOGD("starting");
  696. tcgetattr(master, &tios);
  697. tios.c_lflag &= ~(ECHO | ECHONL | ICANON);
  698. /*
  699. tios.c_iflag &= ~(ICRNL | IXON | BRKINT);
  700. tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  701. tios.c_oflag &= ~(OPOST);
  702. */
  703. tcsetattr(master, TCSAFLUSH, &tios);
  704. tcgetattr(1, &orig1);
  705. tios = orig1;
  706. /*
  707. tios.c_iflag &= ~(ICRNL | IXON | BRKINT);
  708. tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  709. tios.c_oflag &= ~(OPOST);
  710. */
  711. tios.c_iflag &= ~(ICRNL | IXON | ISTRIP | BRKINT);
  712. tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
  713. tios.c_oflag &= ~(OPOST);
  714. tios.c_cflag |= (CS8);
  715. // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
  716. tcsetattr(1, TCSAFLUSH, &tios);
  717. /*
  718. This doesn't need to be static -- because it is part of
  719. main. Once main ends, we're done.
  720. */
  721. #define BSIZE 128
  722. std::string buffer;
  723. buffer.reserve(BSIZE);
  724. for (;;) {
  725. // define estruturas para o select, que serve para verificar qual
  726. // se tornou "pronto pra uso"
  727. fd_set read_fd;
  728. fd_set write_fd;
  729. fd_set except_fd;
  730. // inicializa as estruturas
  731. FD_ZERO(&read_fd);
  732. FD_ZERO(&write_fd);
  733. FD_ZERO(&except_fd);
  734. // atribui o descritor master, obtido pelo forkpty, ao read_fd
  735. FD_SET(master, &read_fd);
  736. // atribui o stdin ao read_fd
  737. FD_SET(STDIN_FILENO, &read_fd);
  738. // o descritor tem que ser unico para o programa, a documentacao
  739. // recomenda um calculo entre os descritores sendo usados + 1
  740. timeout.tv_sec = 60;
  741. timeout.tv_usec = 0;
  742. // if (select(master + 1, &read_fd, &write_fd, &except_fd, &timeout) == 0)
  743. // {
  744. if (select(master + 1, &read_fd, &write_fd, &except_fd, &timeout) == 0) {
  745. ZF_LOGI("TIMEOUT");
  746. // This means timeout!
  747. }
  748. // read_fd esta atribuido com read_fd?
  749. if (FD_ISSET(master, &read_fd)) {
  750. // leia o que bc esta mandando
  751. // ZF_LOGD("read (%d) %d bytes", size, BSIZE - size);
  752. char read_buffer[BSIZE + 1];
  753. int total;
  754. // We may adjust this later on (adjusting read length).
  755. if ((total = read(master, read_buffer, BSIZE)) != -1) {
  756. // Ok, we've read more into the buffer.
  757. ZF_LOGV("Read %d bytes", total);
  758. buffer.append(read_buffer, total);
  759. terminal_output(translate, buffer);
  760. } else {
  761. ZF_LOGE("read = -1, break/exit");
  762. break;
  763. }
  764. }
  765. // read_fd esta atribuido com a entrada padrao?
  766. if (FD_ISSET(STDIN_FILENO, &read_fd)) {
  767. // leia a entrada padrao
  768. char input[BSIZE];
  769. int r = read(STDIN_FILENO, &input, BSIZE);
  770. std::string input_str(input, r);
  771. // input[r] = 0;
  772. // F11 didn't work, it's a "fullscreen" key.
  773. static const char *func_keys[12] = {"\x1bOP",
  774. "\x1bOQ",
  775. "\x1bOR",
  776. "\x1bOS",
  777. "\x1b[15\x7e",
  778. "\x1b[17\x7e",
  779. "\x1b[18\x7e",
  780. "\x1b[19\x7e",
  781. "\x1b[20\x7e",
  782. "\x1b[21\x7e",
  783. "",
  784. "\x1b[24]x7e"};
  785. // left right up down ins del home end PgUp PgDown
  786. static const char *dir_keys[] = {
  787. "\x1bOD", "\x1bOC", "\x1bOA", "\x1bOB", "\x1b[2\x7e",
  788. "\x1b[3\x7e", "\x1bOH", "\x1b[F", "\x1b[5\x7e", "\x1b[6\x7e"};
  789. // e escreva no bc
  790. ZF_LOGI_MEM(input_str.data(), input_str.size(), "INPUT <<");
  791. if (CATCH_CTRLC) {
  792. remove_all(input_str, '\x03');
  793. }
  794. if (TERM_XLATE) {
  795. // We're going to try it without any buffer or understanding of ANSI
  796. // codes.
  797. int c = 0;
  798. while (replace(input_str, "\x1b[A", dir_keys[2])) { // OA
  799. c++;
  800. }
  801. while (replace(input_str, "\x1b[B", dir_keys[3])) { // OB
  802. c++;
  803. }
  804. while (replace(input_str, "\x1b[C", dir_keys[1])) { // OC
  805. c++;
  806. }
  807. while (replace(input_str, "\x1b[D", dir_keys[0])) { // OD
  808. c++;
  809. }
  810. while (replace(input_str, "\x1b[U", dir_keys[9])) { // [6~
  811. c++;
  812. }
  813. while (replace(input_str, "\x1b[V", dir_keys[8])) { // [5~
  814. c++;
  815. }
  816. while (replace(input_str, "\x1b[H", dir_keys[6])) { // OH
  817. c++;
  818. }
  819. while (replace(input_str, "\x1b[K", dir_keys[7])) { // OF
  820. c++;
  821. }
  822. while (replace(input_str, "\x1b[@", dir_keys[4])) { // [2~
  823. c++;
  824. }
  825. /* F1-F5 are "broken". They all map to this.
  826. while (replace(input_str, "\x1b[1", "\x1bOR")) { // F?
  827. c++;
  828. }
  829. */
  830. while (replace(input_str, "\r\n", "\r")) {
  831. c++;
  832. }
  833. // NON-Doorways, DEL = 0x7f
  834. // Terminal DEL = "\x1b[3\7e"
  835. while (replace(input_str, std::string("\x7f"), dir_keys[5])) {
  836. c++;
  837. }
  838. // DOORWAYS mode:
  839. // Future Fix: Find char(0) pos, get next char, look those 2 chars up
  840. // in a map.
  841. if (input_str.find(char(0)) != std::string::npos) {
  842. // We found a null character, TIAS
  843. ZF_LOGE("null found. Start doorways replace mode.");
  844. using namespace std::string_literals;
  845. // F1-F12
  846. while (replace(input_str, std::string("\x00\x3b"s),
  847. std::string(func_keys[0]))) {
  848. c++;
  849. }
  850. while (replace(input_str, std::string("\x00\x3c"s), func_keys[1])) {
  851. c++;
  852. }
  853. while (replace(input_str, std::string("\x00\x3d"s), func_keys[2])) {
  854. c++;
  855. }
  856. while (replace(input_str, std::string("\x00\x3e"s), func_keys[3])) {
  857. c++;
  858. }
  859. while (replace(input_str, std::string("\x00\x3f"s), func_keys[4])) {
  860. c++;
  861. }
  862. while (replace(input_str, std::string("\x00\x40"s), func_keys[5])) {
  863. c++;
  864. }
  865. while (replace(input_str, std::string("\x00\x41"s), func_keys[6])) {
  866. c++;
  867. }
  868. while (replace(input_str, std::string("\x00\x42"s), func_keys[7])) {
  869. c++;
  870. }
  871. while (replace(input_str, std::string("\x00\x43"s), func_keys[8])) {
  872. c++;
  873. }
  874. while (replace(input_str, std::string("\x00\x44"s), func_keys[9])) {
  875. c++;
  876. }
  877. /*
  878. while (replace(input_str, std::string("\x00\x85"s), func_keys[10]))
  879. { // F11 c++;
  880. } */
  881. while (
  882. replace(input_str, std::string("\x00\x86"s), func_keys[11])) {
  883. c++;
  884. }
  885. // Delete = 0x7f (not here)
  886. // Left, Right, Up, Down, Insert, Home, End, PgUp, PgDwn
  887. while (replace(input_str, std::string("\x00\x4b"s),
  888. dir_keys[0])) { // Left
  889. c++;
  890. }
  891. while (replace(input_str, std::string("\x00\x4d"s),
  892. dir_keys[1])) { // Right
  893. c++;
  894. }
  895. while (replace(input_str, std::string("\x00\x48"s),
  896. dir_keys[2])) { // Up
  897. c++;
  898. }
  899. while (replace(input_str, std::string("\x00\x50"s),
  900. dir_keys[3])) { // Down
  901. c++;
  902. }
  903. while (replace(input_str, std::string("\x00\x52"s),
  904. dir_keys[4])) { // Ins
  905. c++;
  906. }
  907. // Delete is 0x7f (no null)
  908. while (replace(input_str, std::string("\x00\x47"s),
  909. dir_keys[6])) { // Home
  910. c++;
  911. }
  912. while (replace(input_str, std::string("\x00\x4f"s),
  913. dir_keys[7])) { // End
  914. c++;
  915. }
  916. while (replace(input_str, std::string("\x00\x49"s),
  917. dir_keys[8])) { // Page Up
  918. c++;
  919. }
  920. while (replace(input_str, std::string("\x00\x51"s),
  921. dir_keys[9])) { // Page Down
  922. c++;
  923. }
  924. if (c) {
  925. ZF_LOGE_MEM(input_str.data(), input_str.size(),
  926. "Input (%d changed):", c);
  927. }
  928. }
  929. ZF_LOGD_MEM(input_str.data(), input_str.size(),
  930. "Write Input String:");
  931. // write(master, &input, r);
  932. write(master, input_str.data(), input_str.size());
  933. // This is INPUT from the USER
  934. // ZF_LOGI_MEM( input, strlen(input), "<< ");
  935. }
  936. }
  937. }
  938. // Ok, we get to EXIT, but the ... program is still open/running. Why?
  939. // I think this is a netcat problem. It can't tell that the program has
  940. // been closed, so it just flips out.
  941. // FIXED with pipexec
  942. /*
  943. close(master);
  944. close(STDIN_FILENO);
  945. close(STDOUT_FILENO);
  946. int wstate;
  947. int rc = waitpid(pid, &wstate, 0);
  948. ZF_LOGE("waitpid %d (%d) = %d", rc, pid, wstate);
  949. */
  950. // Restore terminal
  951. tcsetattr(1, TCSAFLUSH, &orig1);
  952. ZF_LOGD("exit");
  953. }
  954. return 0;
  955. }